test_graph.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. import gc
  2. import pickle
  3. import platform
  4. import weakref
  5. import pytest
  6. import networkx as nx
  7. from networkx.utils import edges_equal, graphs_equal, nodes_equal
  8. class BaseGraphTester:
  9. """Tests for data-structure independent graph class features."""
  10. def test_contains(self):
  11. G = self.K3
  12. assert 1 in G
  13. assert 4 not in G
  14. assert "b" not in G
  15. assert [] not in G # no exception for nonhashable
  16. assert {1: 1} not in G # no exception for nonhashable
  17. def test_order(self):
  18. G = self.K3
  19. assert len(G) == 3
  20. assert G.order() == 3
  21. assert G.number_of_nodes() == 3
  22. def test_nodes(self):
  23. G = self.K3
  24. assert isinstance(G._node, G.node_dict_factory)
  25. assert isinstance(G._adj, G.adjlist_outer_dict_factory)
  26. assert all(
  27. isinstance(adj, G.adjlist_inner_dict_factory) for adj in G._adj.values()
  28. )
  29. assert sorted(G.nodes()) == self.k3nodes
  30. assert sorted(G.nodes(data=True)) == [(0, {}), (1, {}), (2, {})]
  31. def test_none_node(self):
  32. G = self.Graph()
  33. with pytest.raises(ValueError):
  34. G.add_node(None)
  35. with pytest.raises(ValueError):
  36. G.add_nodes_from([None])
  37. with pytest.raises(ValueError):
  38. G.add_edge(0, None)
  39. with pytest.raises(ValueError):
  40. G.add_edges_from([(0, None)])
  41. def test_has_node(self):
  42. G = self.K3
  43. assert G.has_node(1)
  44. assert not G.has_node(4)
  45. assert not G.has_node([]) # no exception for nonhashable
  46. assert not G.has_node({1: 1}) # no exception for nonhashable
  47. def test_has_edge(self):
  48. G = self.K3
  49. assert G.has_edge(0, 1)
  50. assert not G.has_edge(0, -1)
  51. def test_neighbors(self):
  52. G = self.K3
  53. assert sorted(G.neighbors(0)) == [1, 2]
  54. with pytest.raises(nx.NetworkXError):
  55. G.neighbors(-1)
  56. @pytest.mark.skipif(
  57. platform.python_implementation() == "PyPy", reason="PyPy gc is different"
  58. )
  59. def test_memory_leak(self):
  60. G = self.Graph()
  61. def count_objects_of_type(_type):
  62. # Iterating over all objects tracked by gc can include weak references
  63. # whose weakly-referenced objects may no longer exist. Calling `isinstance`
  64. # on such a weak reference will raise ReferenceError. There are at least
  65. # three workarounds for this: one is to compare type names instead of using
  66. # `isinstance` such as `type(obj).__name__ == typename`, another is to use
  67. # `type(obj) == _type`, and the last is to ignore ProxyTypes as we do below.
  68. # NOTE: even if this safeguard is deemed unnecessary to pass NetworkX tests,
  69. # we should still keep it for maximum safety for other NetworkX backends.
  70. return sum(
  71. 1
  72. for obj in gc.get_objects()
  73. if not isinstance(obj, weakref.ProxyTypes) and isinstance(obj, _type)
  74. )
  75. gc.collect()
  76. before = count_objects_of_type(self.Graph)
  77. G.copy()
  78. gc.collect()
  79. after = count_objects_of_type(self.Graph)
  80. assert before == after
  81. # test a subgraph of the base class
  82. class MyGraph(self.Graph):
  83. pass
  84. gc.collect()
  85. G = MyGraph()
  86. before = count_objects_of_type(MyGraph)
  87. G.copy()
  88. gc.collect()
  89. after = count_objects_of_type(MyGraph)
  90. assert before == after
  91. def test_edges(self):
  92. G = self.K3
  93. assert isinstance(G._adj, G.adjlist_outer_dict_factory)
  94. assert edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
  95. assert edges_equal(G.edges(0), [(0, 1), (0, 2)])
  96. assert edges_equal(G.edges([0, 1]), [(0, 1), (0, 2), (1, 2)])
  97. with pytest.raises(nx.NetworkXError):
  98. G.edges(-1)
  99. def test_degree(self):
  100. G = self.K3
  101. assert sorted(G.degree()) == [(0, 2), (1, 2), (2, 2)]
  102. assert dict(G.degree()) == {0: 2, 1: 2, 2: 2}
  103. assert G.degree(0) == 2
  104. with pytest.raises(nx.NetworkXError):
  105. G.degree(-1) # node not in graph
  106. def test_size(self):
  107. G = self.K3
  108. assert G.size() == 3
  109. assert G.number_of_edges() == 3
  110. def test_nbunch_iter(self):
  111. G = self.K3
  112. assert nodes_equal(G.nbunch_iter(), self.k3nodes) # all nodes
  113. assert nodes_equal(G.nbunch_iter(0), [0]) # single node
  114. assert nodes_equal(G.nbunch_iter([0, 1]), [0, 1]) # sequence
  115. # sequence with none in graph
  116. assert nodes_equal(G.nbunch_iter([-1]), [])
  117. # string sequence with none in graph
  118. assert nodes_equal(G.nbunch_iter("foo"), [])
  119. # node not in graph doesn't get caught upon creation of iterator
  120. bunch = G.nbunch_iter(-1)
  121. # but gets caught when iterator used
  122. with pytest.raises(nx.NetworkXError, match="is not a node or a sequence"):
  123. list(bunch)
  124. # unhashable doesn't get caught upon creation of iterator
  125. bunch = G.nbunch_iter([0, 1, 2, {}])
  126. # but gets caught when iterator hits the unhashable
  127. with pytest.raises(
  128. nx.NetworkXError, match="in sequence nbunch is not a valid node"
  129. ):
  130. list(bunch)
  131. def test_nbunch_iter_node_format_raise(self):
  132. # Tests that a node that would have failed string formatting
  133. # doesn't cause an error when attempting to raise a
  134. # :exc:`nx.NetworkXError`.
  135. # For more information, see pull request #1813.
  136. G = self.Graph()
  137. nbunch = [("x", set())]
  138. with pytest.raises(nx.NetworkXError):
  139. list(G.nbunch_iter(nbunch))
  140. def test_selfloop_degree(self):
  141. G = self.Graph()
  142. G.add_edge(1, 1)
  143. assert sorted(G.degree()) == [(1, 2)]
  144. assert dict(G.degree()) == {1: 2}
  145. assert G.degree(1) == 2
  146. assert sorted(G.degree([1])) == [(1, 2)]
  147. assert G.degree(1, weight="weight") == 2
  148. def test_selfloops(self):
  149. G = self.K3.copy()
  150. G.add_edge(0, 0)
  151. assert nodes_equal(nx.nodes_with_selfloops(G), [0])
  152. assert edges_equal(nx.selfloop_edges(G), [(0, 0)])
  153. assert nx.number_of_selfloops(G) == 1
  154. G.remove_edge(0, 0)
  155. G.add_edge(0, 0)
  156. G.remove_edges_from([(0, 0)])
  157. G.add_edge(1, 1)
  158. G.remove_node(1)
  159. G.add_edge(0, 0)
  160. G.add_edge(1, 1)
  161. G.remove_nodes_from([0, 1])
  162. def test_cache_reset(self):
  163. G = self.K3.copy()
  164. old_adj = G.adj
  165. assert id(G.adj) == id(old_adj)
  166. G._adj = {}
  167. assert id(G.adj) != id(old_adj)
  168. old_nodes = G.nodes
  169. assert id(G.nodes) == id(old_nodes)
  170. G._node = {}
  171. assert id(G.nodes) != id(old_nodes)
  172. def test_attributes_cached(self):
  173. G = self.K3.copy()
  174. assert id(G.nodes) == id(G.nodes)
  175. assert id(G.edges) == id(G.edges)
  176. assert id(G.degree) == id(G.degree)
  177. assert id(G.adj) == id(G.adj)
  178. class BaseAttrGraphTester(BaseGraphTester):
  179. """Tests of graph class attribute features."""
  180. def test_weighted_degree(self):
  181. G = self.Graph()
  182. G.add_edge(1, 2, weight=2, other=3)
  183. G.add_edge(2, 3, weight=3, other=4)
  184. assert sorted(d for n, d in G.degree(weight="weight")) == [2, 3, 5]
  185. assert dict(G.degree(weight="weight")) == {1: 2, 2: 5, 3: 3}
  186. assert G.degree(1, weight="weight") == 2
  187. assert nodes_equal((G.degree([1], weight="weight")), [(1, 2)])
  188. assert nodes_equal((d for n, d in G.degree(weight="other")), [3, 7, 4])
  189. assert dict(G.degree(weight="other")) == {1: 3, 2: 7, 3: 4}
  190. assert G.degree(1, weight="other") == 3
  191. assert edges_equal((G.degree([1], weight="other")), [(1, 3)])
  192. def add_attributes(self, G):
  193. G.graph["foo"] = []
  194. G.nodes[0]["foo"] = []
  195. G.remove_edge(1, 2)
  196. ll = []
  197. G.add_edge(1, 2, foo=ll)
  198. G.add_edge(2, 1, foo=ll)
  199. def test_name(self):
  200. G = self.Graph(name="")
  201. assert G.name == ""
  202. G = self.Graph(name="test")
  203. assert G.name == "test"
  204. def test_str_unnamed(self):
  205. G = self.Graph()
  206. G.add_edges_from([(1, 2), (2, 3)])
  207. assert str(G) == f"{type(G).__name__} with 3 nodes and 2 edges"
  208. def test_str_named(self):
  209. G = self.Graph(name="foo")
  210. G.add_edges_from([(1, 2), (2, 3)])
  211. assert str(G) == f"{type(G).__name__} named 'foo' with 3 nodes and 2 edges"
  212. def test_graph_chain(self):
  213. G = self.Graph([(0, 1), (1, 2)])
  214. DG = G.to_directed(as_view=True)
  215. SDG = DG.subgraph([0, 1])
  216. RSDG = SDG.reverse(copy=False)
  217. assert G is DG._graph
  218. assert DG is SDG._graph
  219. assert SDG is RSDG._graph
  220. def test_copy(self):
  221. G = self.Graph()
  222. G.add_node(0)
  223. G.add_edge(1, 2)
  224. self.add_attributes(G)
  225. # copy edge datadict but any container attr are same
  226. H = G.copy()
  227. self.graphs_equal(H, G)
  228. self.different_attrdict(H, G)
  229. self.shallow_copy_attrdict(H, G)
  230. def test_class_copy(self):
  231. G = self.Graph()
  232. G.add_node(0)
  233. G.add_edge(1, 2)
  234. self.add_attributes(G)
  235. # copy edge datadict but any container attr are same
  236. H = G.__class__(G)
  237. self.graphs_equal(H, G)
  238. self.different_attrdict(H, G)
  239. self.shallow_copy_attrdict(H, G)
  240. def test_fresh_copy(self):
  241. G = self.Graph()
  242. G.add_node(0)
  243. G.add_edge(1, 2)
  244. self.add_attributes(G)
  245. # copy graph structure but use fresh datadict
  246. H = G.__class__()
  247. H.add_nodes_from(G)
  248. H.add_edges_from(G.edges())
  249. assert len(G.nodes[0]) == 1
  250. ddict = G.adj[1][2][0] if G.is_multigraph() else G.adj[1][2]
  251. assert len(ddict) == 1
  252. assert len(H.nodes[0]) == 0
  253. ddict = H.adj[1][2][0] if H.is_multigraph() else H.adj[1][2]
  254. assert len(ddict) == 0
  255. def is_deepcopy(self, H, G):
  256. self.graphs_equal(H, G)
  257. self.different_attrdict(H, G)
  258. self.deep_copy_attrdict(H, G)
  259. def deep_copy_attrdict(self, H, G):
  260. self.deepcopy_graph_attr(H, G)
  261. self.deepcopy_node_attr(H, G)
  262. self.deepcopy_edge_attr(H, G)
  263. def deepcopy_graph_attr(self, H, G):
  264. assert G.graph["foo"] == H.graph["foo"]
  265. G.graph["foo"].append(1)
  266. assert G.graph["foo"] != H.graph["foo"]
  267. def deepcopy_node_attr(self, H, G):
  268. assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
  269. G.nodes[0]["foo"].append(1)
  270. assert G.nodes[0]["foo"] != H.nodes[0]["foo"]
  271. def deepcopy_edge_attr(self, H, G):
  272. assert G[1][2]["foo"] == H[1][2]["foo"]
  273. G[1][2]["foo"].append(1)
  274. assert G[1][2]["foo"] != H[1][2]["foo"]
  275. def is_shallow_copy(self, H, G):
  276. self.graphs_equal(H, G)
  277. self.shallow_copy_attrdict(H, G)
  278. def shallow_copy_attrdict(self, H, G):
  279. self.shallow_copy_graph_attr(H, G)
  280. self.shallow_copy_node_attr(H, G)
  281. self.shallow_copy_edge_attr(H, G)
  282. def shallow_copy_graph_attr(self, H, G):
  283. assert G.graph["foo"] == H.graph["foo"]
  284. G.graph["foo"].append(1)
  285. assert G.graph["foo"] == H.graph["foo"]
  286. def shallow_copy_node_attr(self, H, G):
  287. assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
  288. G.nodes[0]["foo"].append(1)
  289. assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
  290. def shallow_copy_edge_attr(self, H, G):
  291. assert G[1][2]["foo"] == H[1][2]["foo"]
  292. G[1][2]["foo"].append(1)
  293. assert G[1][2]["foo"] == H[1][2]["foo"]
  294. def same_attrdict(self, H, G):
  295. old_foo = H[1][2]["foo"]
  296. H.adj[1][2]["foo"] = "baz"
  297. assert G.edges == H.edges
  298. H.adj[1][2]["foo"] = old_foo
  299. assert G.edges == H.edges
  300. old_foo = H.nodes[0]["foo"]
  301. H.nodes[0]["foo"] = "baz"
  302. assert G.nodes == H.nodes
  303. H.nodes[0]["foo"] = old_foo
  304. assert G.nodes == H.nodes
  305. def different_attrdict(self, H, G):
  306. old_foo = H[1][2]["foo"]
  307. H.adj[1][2]["foo"] = "baz"
  308. assert G._adj != H._adj
  309. H.adj[1][2]["foo"] = old_foo
  310. assert G._adj == H._adj
  311. old_foo = H.nodes[0]["foo"]
  312. H.nodes[0]["foo"] = "baz"
  313. assert G._node != H._node
  314. H.nodes[0]["foo"] = old_foo
  315. assert G._node == H._node
  316. def graphs_equal(self, H, G):
  317. assert G._adj == H._adj
  318. assert G._node == H._node
  319. assert G.graph == H.graph
  320. assert G.name == H.name
  321. if not G.is_directed() and not H.is_directed():
  322. assert H._adj[1][2] is H._adj[2][1]
  323. assert G._adj[1][2] is G._adj[2][1]
  324. else: # at least one is directed
  325. if not G.is_directed():
  326. G._pred = G._adj
  327. G._succ = G._adj
  328. if not H.is_directed():
  329. H._pred = H._adj
  330. H._succ = H._adj
  331. assert G._pred == H._pred
  332. assert G._succ == H._succ
  333. assert H._succ[1][2] is H._pred[2][1]
  334. assert G._succ[1][2] is G._pred[2][1]
  335. def test_graph_attr(self):
  336. G = self.K3.copy()
  337. G.graph["foo"] = "bar"
  338. assert isinstance(G.graph, G.graph_attr_dict_factory)
  339. assert G.graph["foo"] == "bar"
  340. del G.graph["foo"]
  341. assert G.graph == {}
  342. H = self.Graph(foo="bar")
  343. assert H.graph["foo"] == "bar"
  344. def test_node_attr(self):
  345. G = self.K3.copy()
  346. G.add_node(1, foo="bar")
  347. assert all(
  348. isinstance(d, G.node_attr_dict_factory) for u, d in G.nodes(data=True)
  349. )
  350. assert nodes_equal(G.nodes(), [0, 1, 2])
  351. assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "bar"}), (2, {})])
  352. G.nodes[1]["foo"] = "baz"
  353. assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "baz"}), (2, {})])
  354. assert nodes_equal(G.nodes(data="foo"), [(0, None), (1, "baz"), (2, None)])
  355. assert nodes_equal(
  356. G.nodes(data="foo", default="bar"), [(0, "bar"), (1, "baz"), (2, "bar")]
  357. )
  358. def test_node_attr2(self):
  359. G = self.K3.copy()
  360. a = {"foo": "bar"}
  361. G.add_node(3, **a)
  362. assert nodes_equal(G.nodes(), [0, 1, 2, 3])
  363. assert nodes_equal(
  364. G.nodes(data=True), [(0, {}), (1, {}), (2, {}), (3, {"foo": "bar"})]
  365. )
  366. def test_edge_lookup(self):
  367. G = self.Graph()
  368. G.add_edge(1, 2, foo="bar")
  369. assert edges_equal(G.edges[1, 2], {"foo": "bar"})
  370. def test_edge_attr(self):
  371. G = self.Graph()
  372. G.add_edge(1, 2, foo="bar")
  373. assert all(
  374. isinstance(d, G.edge_attr_dict_factory) for u, v, d in G.edges(data=True)
  375. )
  376. assert edges_equal(G.edges(data=True), [(1, 2, {"foo": "bar"})])
  377. assert edges_equal(G.edges(data="foo"), [(1, 2, "bar")])
  378. def test_edge_attr2(self):
  379. G = self.Graph()
  380. G.add_edges_from([(1, 2), (3, 4)], foo="foo")
  381. assert edges_equal(
  382. G.edges(data=True), [(1, 2, {"foo": "foo"}), (3, 4, {"foo": "foo"})]
  383. )
  384. assert edges_equal(G.edges(data="foo"), [(1, 2, "foo"), (3, 4, "foo")])
  385. def test_edge_attr3(self):
  386. G = self.Graph()
  387. G.add_edges_from([(1, 2, {"weight": 32}), (3, 4, {"weight": 64})], foo="foo")
  388. assert edges_equal(
  389. G.edges(data=True),
  390. [
  391. (1, 2, {"foo": "foo", "weight": 32}),
  392. (3, 4, {"foo": "foo", "weight": 64}),
  393. ],
  394. )
  395. G.remove_edges_from([(1, 2), (3, 4)])
  396. G.add_edge(1, 2, data=7, spam="bar", bar="foo")
  397. assert edges_equal(
  398. G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
  399. )
  400. def test_edge_attr4(self):
  401. G = self.Graph()
  402. G.add_edge(1, 2, data=7, spam="bar", bar="foo")
  403. assert edges_equal(
  404. G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
  405. )
  406. G[1][2]["data"] = 10 # OK to set data like this
  407. assert edges_equal(
  408. G.edges(data=True), [(1, 2, {"data": 10, "spam": "bar", "bar": "foo"})]
  409. )
  410. G.adj[1][2]["data"] = 20
  411. assert edges_equal(
  412. G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})]
  413. )
  414. G.edges[1, 2]["data"] = 21 # another spelling, "edge"
  415. assert edges_equal(
  416. G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})]
  417. )
  418. G.adj[1][2]["listdata"] = [20, 200]
  419. G.adj[1][2]["weight"] = 20
  420. dd = {
  421. "data": 21,
  422. "spam": "bar",
  423. "bar": "foo",
  424. "listdata": [20, 200],
  425. "weight": 20,
  426. }
  427. assert edges_equal(G.edges(data=True), [(1, 2, dd)])
  428. def test_to_undirected(self):
  429. G = self.K3
  430. self.add_attributes(G)
  431. H = nx.Graph(G)
  432. self.is_shallow_copy(H, G)
  433. self.different_attrdict(H, G)
  434. H = G.to_undirected()
  435. self.is_deepcopy(H, G)
  436. def test_to_directed_as_view(self):
  437. H = nx.path_graph(2, create_using=self.Graph)
  438. H2 = H.to_directed(as_view=True)
  439. assert H is H2._graph
  440. assert H2.has_edge(0, 1)
  441. assert H2.has_edge(1, 0) or H.is_directed()
  442. pytest.raises(nx.NetworkXError, H2.add_node, -1)
  443. pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
  444. H.add_edge(1, 2)
  445. assert H2.has_edge(1, 2)
  446. assert H2.has_edge(2, 1) or H.is_directed()
  447. def test_to_undirected_as_view(self):
  448. H = nx.path_graph(2, create_using=self.Graph)
  449. H2 = H.to_undirected(as_view=True)
  450. assert H is H2._graph
  451. assert H2.has_edge(0, 1)
  452. assert H2.has_edge(1, 0)
  453. pytest.raises(nx.NetworkXError, H2.add_node, -1)
  454. pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
  455. H.add_edge(1, 2)
  456. assert H2.has_edge(1, 2)
  457. assert H2.has_edge(2, 1)
  458. def test_directed_class(self):
  459. G = self.Graph()
  460. class newGraph(G.to_undirected_class()):
  461. def to_directed_class(self):
  462. return newDiGraph
  463. def to_undirected_class(self):
  464. return newGraph
  465. class newDiGraph(G.to_directed_class()):
  466. def to_directed_class(self):
  467. return newDiGraph
  468. def to_undirected_class(self):
  469. return newGraph
  470. G = newDiGraph() if G.is_directed() else newGraph()
  471. H = G.to_directed()
  472. assert isinstance(H, newDiGraph)
  473. H = G.to_undirected()
  474. assert isinstance(H, newGraph)
  475. def test_to_directed(self):
  476. G = self.K3
  477. self.add_attributes(G)
  478. H = nx.DiGraph(G)
  479. self.is_shallow_copy(H, G)
  480. self.different_attrdict(H, G)
  481. H = G.to_directed()
  482. self.is_deepcopy(H, G)
  483. def test_subgraph(self):
  484. G = self.K3
  485. self.add_attributes(G)
  486. H = G.subgraph([0, 1, 2, 5])
  487. self.graphs_equal(H, G)
  488. self.same_attrdict(H, G)
  489. self.shallow_copy_attrdict(H, G)
  490. H = G.subgraph(0)
  491. assert H.adj == {0: {}}
  492. H = G.subgraph([])
  493. assert H.adj == {}
  494. assert G.adj != {}
  495. def test_selfloops_attr(self):
  496. G = self.K3.copy()
  497. G.add_edge(0, 0)
  498. G.add_edge(1, 1, weight=2)
  499. assert edges_equal(
  500. nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
  501. )
  502. assert edges_equal(
  503. nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)]
  504. )
  505. class TestGraph(BaseAttrGraphTester):
  506. """Tests specific to dict-of-dict-of-dict graph data structure"""
  507. def setup_method(self):
  508. self.Graph = nx.Graph
  509. # build dict-of-dict-of-dict K3
  510. ed1, ed2, ed3 = ({}, {}, {})
  511. self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
  512. self.k3edges = [(0, 1), (0, 2), (1, 2)]
  513. self.k3nodes = [0, 1, 2]
  514. self.K3 = self.Graph()
  515. self.K3._adj = self.k3adj
  516. self.K3._node = {}
  517. self.K3._node[0] = {}
  518. self.K3._node[1] = {}
  519. self.K3._node[2] = {}
  520. def test_pickle(self):
  521. G = self.K3
  522. pg = pickle.loads(pickle.dumps(G, -1))
  523. self.graphs_equal(pg, G)
  524. pg = pickle.loads(pickle.dumps(G))
  525. self.graphs_equal(pg, G)
  526. def test_data_input(self):
  527. G = self.Graph({1: [2], 2: [1]}, name="test")
  528. assert G.name == "test"
  529. assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
  530. def test_adjacency(self):
  531. G = self.K3
  532. assert dict(G.adjacency()) == {
  533. 0: {1: {}, 2: {}},
  534. 1: {0: {}, 2: {}},
  535. 2: {0: {}, 1: {}},
  536. }
  537. def test_getitem(self):
  538. G = self.K3
  539. assert G.adj[0] == {1: {}, 2: {}}
  540. assert G[0] == {1: {}, 2: {}}
  541. with pytest.raises(KeyError):
  542. G.__getitem__("j")
  543. with pytest.raises(TypeError):
  544. G.__getitem__(["A"])
  545. def test_add_node(self):
  546. G = self.Graph()
  547. G.add_node(0)
  548. assert G.adj == {0: {}}
  549. # test add attributes
  550. G.add_node(1, c="red")
  551. G.add_node(2, c="blue")
  552. G.add_node(3, c="red")
  553. assert G.nodes[1]["c"] == "red"
  554. assert G.nodes[2]["c"] == "blue"
  555. assert G.nodes[3]["c"] == "red"
  556. # test updating attributes
  557. G.add_node(1, c="blue")
  558. G.add_node(2, c="red")
  559. G.add_node(3, c="blue")
  560. assert G.nodes[1]["c"] == "blue"
  561. assert G.nodes[2]["c"] == "red"
  562. assert G.nodes[3]["c"] == "blue"
  563. def test_add_nodes_from(self):
  564. G = self.Graph()
  565. G.add_nodes_from([0, 1, 2])
  566. assert G.adj == {0: {}, 1: {}, 2: {}}
  567. # test add attributes
  568. G.add_nodes_from([0, 1, 2], c="red")
  569. assert G.nodes[0]["c"] == "red"
  570. assert G.nodes[2]["c"] == "red"
  571. # test that attribute dicts are not the same
  572. assert G.nodes[0] is not G.nodes[1]
  573. # test updating attributes
  574. G.add_nodes_from([0, 1, 2], c="blue")
  575. assert G.nodes[0]["c"] == "blue"
  576. assert G.nodes[2]["c"] == "blue"
  577. assert G.nodes[0] is not G.nodes[1]
  578. # test tuple input
  579. H = self.Graph()
  580. H.add_nodes_from(G.nodes(data=True))
  581. assert H.nodes[0]["c"] == "blue"
  582. assert H.nodes[2]["c"] == "blue"
  583. assert H.nodes[0] is not H.nodes[1]
  584. # specific overrides general
  585. H.add_nodes_from([0, (1, {"c": "green"}), (3, {"c": "cyan"})], c="red")
  586. assert H.nodes[0]["c"] == "red"
  587. assert H.nodes[1]["c"] == "green"
  588. assert H.nodes[2]["c"] == "blue"
  589. assert H.nodes[3]["c"] == "cyan"
  590. def test_remove_node(self):
  591. G = self.K3.copy()
  592. G.remove_node(0)
  593. assert G.adj == {1: {2: {}}, 2: {1: {}}}
  594. with pytest.raises(nx.NetworkXError):
  595. G.remove_node(-1)
  596. # generator here to implement list,set,string...
  597. def test_remove_nodes_from(self):
  598. G = self.K3.copy()
  599. G.remove_nodes_from([0, 1])
  600. assert G.adj == {2: {}}
  601. G.remove_nodes_from([-1]) # silent fail
  602. def test_add_edge(self):
  603. G = self.Graph()
  604. G.add_edge(0, 1)
  605. assert G.adj == {0: {1: {}}, 1: {0: {}}}
  606. G = self.Graph()
  607. G.add_edge(*(0, 1))
  608. assert G.adj == {0: {1: {}}, 1: {0: {}}}
  609. G = self.Graph()
  610. with pytest.raises(ValueError):
  611. G.add_edge(None, "anything")
  612. def test_add_edges_from(self):
  613. G = self.Graph()
  614. G.add_edges_from([(0, 1), (0, 2, {"weight": 3})])
  615. assert G.adj == {
  616. 0: {1: {}, 2: {"weight": 3}},
  617. 1: {0: {}},
  618. 2: {0: {"weight": 3}},
  619. }
  620. G = self.Graph()
  621. G.add_edges_from([(0, 1), (0, 2, {"weight": 3}), (1, 2, {"data": 4})], data=2)
  622. assert G.adj == {
  623. 0: {1: {"data": 2}, 2: {"weight": 3, "data": 2}},
  624. 1: {0: {"data": 2}, 2: {"data": 4}},
  625. 2: {0: {"weight": 3, "data": 2}, 1: {"data": 4}},
  626. }
  627. with pytest.raises(nx.NetworkXError):
  628. G.add_edges_from([(0,)]) # too few in tuple
  629. with pytest.raises(nx.NetworkXError):
  630. G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
  631. with pytest.raises(TypeError):
  632. G.add_edges_from([0]) # not a tuple
  633. with pytest.raises(ValueError):
  634. G.add_edges_from([(None, 3), (3, 2)]) # None cannot be a node
  635. def test_remove_edge(self):
  636. G = self.K3.copy()
  637. G.remove_edge(0, 1)
  638. assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
  639. with pytest.raises(nx.NetworkXError):
  640. G.remove_edge(-1, 0)
  641. def test_remove_edges_from(self):
  642. G = self.K3.copy()
  643. G.remove_edges_from([(0, 1)])
  644. assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
  645. G.remove_edges_from([(0, 0)]) # silent fail
  646. def test_clear(self):
  647. G = self.K3.copy()
  648. G.graph["name"] = "K3"
  649. G.clear()
  650. assert list(G.nodes) == []
  651. assert G.adj == {}
  652. assert G.graph == {}
  653. def test_clear_edges(self):
  654. G = self.K3.copy()
  655. G.graph["name"] = "K3"
  656. nodes = list(G.nodes)
  657. G.clear_edges()
  658. assert list(G.nodes) == nodes
  659. assert G.adj == {0: {}, 1: {}, 2: {}}
  660. assert list(G.edges) == []
  661. assert G.graph["name"] == "K3"
  662. def test_edges_data(self):
  663. G = self.K3
  664. all_edges = [(0, 1, {}), (0, 2, {}), (1, 2, {})]
  665. assert edges_equal(G.edges(data=True), all_edges)
  666. assert edges_equal(G.edges(0, data=True), [(0, 1, {}), (0, 2, {})])
  667. assert edges_equal(G.edges([0, 1], data=True), all_edges)
  668. with pytest.raises(nx.NetworkXError):
  669. G.edges(-1, True)
  670. def test_get_edge_data(self):
  671. G = self.K3.copy()
  672. assert G.get_edge_data(0, 1) == {}
  673. assert G[0][1] == {}
  674. assert G.get_edge_data(10, 20) is None
  675. assert G.get_edge_data(-1, 0) is None
  676. assert G.get_edge_data(-1, 0, default=1) == 1
  677. def test_update(self):
  678. # specify both edgees and nodes
  679. G = self.K3.copy()
  680. G.update(nodes=[3, (4, {"size": 2})], edges=[(4, 5), (6, 7, {"weight": 2})])
  681. nlist = [
  682. (0, {}),
  683. (1, {}),
  684. (2, {}),
  685. (3, {}),
  686. (4, {"size": 2}),
  687. (5, {}),
  688. (6, {}),
  689. (7, {}),
  690. ]
  691. assert sorted(G.nodes.data()) == nlist
  692. if G.is_directed():
  693. elist = [
  694. (0, 1, {}),
  695. (0, 2, {}),
  696. (1, 0, {}),
  697. (1, 2, {}),
  698. (2, 0, {}),
  699. (2, 1, {}),
  700. (4, 5, {}),
  701. (6, 7, {"weight": 2}),
  702. ]
  703. else:
  704. elist = [
  705. (0, 1, {}),
  706. (0, 2, {}),
  707. (1, 2, {}),
  708. (4, 5, {}),
  709. (6, 7, {"weight": 2}),
  710. ]
  711. assert sorted(G.edges.data()) == elist
  712. assert G.graph == {}
  713. # no keywords -- order is edges, nodes
  714. G = self.K3.copy()
  715. G.update([(4, 5), (6, 7, {"weight": 2})], [3, (4, {"size": 2})])
  716. assert sorted(G.nodes.data()) == nlist
  717. assert sorted(G.edges.data()) == elist
  718. assert G.graph == {}
  719. # update using only a graph
  720. G = self.Graph()
  721. G.graph["foo"] = "bar"
  722. G.add_node(2, data=4)
  723. G.add_edge(0, 1, weight=0.5)
  724. GG = G.copy()
  725. H = self.Graph()
  726. GG.update(H)
  727. assert graphs_equal(G, GG)
  728. H.update(G)
  729. assert graphs_equal(H, G)
  730. # update nodes only
  731. H = self.Graph()
  732. H.update(nodes=[3, 4])
  733. assert H.nodes ^ {3, 4} == set()
  734. assert H.size() == 0
  735. # update edges only
  736. H = self.Graph()
  737. H.update(edges=[(3, 4)])
  738. assert sorted(H.edges.data()) == [(3, 4, {})]
  739. assert H.size() == 1
  740. # No inputs -> exception
  741. with pytest.raises(nx.NetworkXError):
  742. nx.Graph().update()
  743. class TestEdgeSubgraph:
  744. """Unit tests for the :meth:`Graph.edge_subgraph` method."""
  745. def setup_method(self):
  746. # Create a path graph on five nodes.
  747. G = nx.path_graph(5)
  748. # Add some node, edge, and graph attributes.
  749. for i in range(5):
  750. G.nodes[i]["name"] = f"node{i}"
  751. G.edges[0, 1]["name"] = "edge01"
  752. G.edges[3, 4]["name"] = "edge34"
  753. G.graph["name"] = "graph"
  754. # Get the subgraph induced by the first and last edges.
  755. self.G = G
  756. self.H = G.edge_subgraph([(0, 1), (3, 4)])
  757. def test_correct_nodes(self):
  758. """Tests that the subgraph has the correct nodes."""
  759. assert [0, 1, 3, 4] == sorted(self.H.nodes())
  760. def test_correct_edges(self):
  761. """Tests that the subgraph has the correct edges."""
  762. assert [(0, 1, "edge01"), (3, 4, "edge34")] == sorted(self.H.edges(data="name"))
  763. def test_add_node(self):
  764. """Tests that adding a node to the original graph does not
  765. affect the nodes of the subgraph.
  766. """
  767. self.G.add_node(5)
  768. assert [0, 1, 3, 4] == sorted(self.H.nodes())
  769. def test_remove_node(self):
  770. """Tests that removing a node in the original graph does
  771. affect the nodes of the subgraph.
  772. """
  773. self.G.remove_node(0)
  774. assert [1, 3, 4] == sorted(self.H.nodes())
  775. def test_node_attr_dict(self):
  776. """Tests that the node attribute dictionary of the two graphs is
  777. the same object.
  778. """
  779. for v in self.H:
  780. assert self.G.nodes[v] == self.H.nodes[v]
  781. # Making a change to G should make a change in H and vice versa.
  782. self.G.nodes[0]["name"] = "foo"
  783. assert self.G.nodes[0] == self.H.nodes[0]
  784. self.H.nodes[1]["name"] = "bar"
  785. assert self.G.nodes[1] == self.H.nodes[1]
  786. def test_edge_attr_dict(self):
  787. """Tests that the edge attribute dictionary of the two graphs is
  788. the same object.
  789. """
  790. for u, v in self.H.edges():
  791. assert self.G.edges[u, v] == self.H.edges[u, v]
  792. # Making a change to G should make a change in H and vice versa.
  793. self.G.edges[0, 1]["name"] = "foo"
  794. assert self.G.edges[0, 1]["name"] == self.H.edges[0, 1]["name"]
  795. self.H.edges[3, 4]["name"] = "bar"
  796. assert self.G.edges[3, 4]["name"] == self.H.edges[3, 4]["name"]
  797. def test_graph_attr_dict(self):
  798. """Tests that the graph attribute dictionary of the two graphs
  799. is the same object.
  800. """
  801. assert self.G.graph is self.H.graph