123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920 |
- import gc
- import pickle
- import platform
- import weakref
- import pytest
- import networkx as nx
- from networkx.utils import edges_equal, graphs_equal, nodes_equal
- class BaseGraphTester:
- """Tests for data-structure independent graph class features."""
- def test_contains(self):
- G = self.K3
- assert 1 in G
- assert 4 not in G
- assert "b" not in G
- assert [] not in G # no exception for nonhashable
- assert {1: 1} not in G # no exception for nonhashable
- def test_order(self):
- G = self.K3
- assert len(G) == 3
- assert G.order() == 3
- assert G.number_of_nodes() == 3
- def test_nodes(self):
- G = self.K3
- assert isinstance(G._node, G.node_dict_factory)
- assert isinstance(G._adj, G.adjlist_outer_dict_factory)
- assert all(
- isinstance(adj, G.adjlist_inner_dict_factory) for adj in G._adj.values()
- )
- assert sorted(G.nodes()) == self.k3nodes
- assert sorted(G.nodes(data=True)) == [(0, {}), (1, {}), (2, {})]
- def test_none_node(self):
- G = self.Graph()
- with pytest.raises(ValueError):
- G.add_node(None)
- with pytest.raises(ValueError):
- G.add_nodes_from([None])
- with pytest.raises(ValueError):
- G.add_edge(0, None)
- with pytest.raises(ValueError):
- G.add_edges_from([(0, None)])
- def test_has_node(self):
- G = self.K3
- assert G.has_node(1)
- assert not G.has_node(4)
- assert not G.has_node([]) # no exception for nonhashable
- assert not G.has_node({1: 1}) # no exception for nonhashable
- def test_has_edge(self):
- G = self.K3
- assert G.has_edge(0, 1)
- assert not G.has_edge(0, -1)
- def test_neighbors(self):
- G = self.K3
- assert sorted(G.neighbors(0)) == [1, 2]
- with pytest.raises(nx.NetworkXError):
- G.neighbors(-1)
- @pytest.mark.skipif(
- platform.python_implementation() == "PyPy", reason="PyPy gc is different"
- )
- def test_memory_leak(self):
- G = self.Graph()
- def count_objects_of_type(_type):
- # Iterating over all objects tracked by gc can include weak references
- # whose weakly-referenced objects may no longer exist. Calling `isinstance`
- # on such a weak reference will raise ReferenceError. There are at least
- # three workarounds for this: one is to compare type names instead of using
- # `isinstance` such as `type(obj).__name__ == typename`, another is to use
- # `type(obj) == _type`, and the last is to ignore ProxyTypes as we do below.
- # NOTE: even if this safeguard is deemed unnecessary to pass NetworkX tests,
- # we should still keep it for maximum safety for other NetworkX backends.
- return sum(
- 1
- for obj in gc.get_objects()
- if not isinstance(obj, weakref.ProxyTypes) and isinstance(obj, _type)
- )
- gc.collect()
- before = count_objects_of_type(self.Graph)
- G.copy()
- gc.collect()
- after = count_objects_of_type(self.Graph)
- assert before == after
- # test a subgraph of the base class
- class MyGraph(self.Graph):
- pass
- gc.collect()
- G = MyGraph()
- before = count_objects_of_type(MyGraph)
- G.copy()
- gc.collect()
- after = count_objects_of_type(MyGraph)
- assert before == after
- def test_edges(self):
- G = self.K3
- assert isinstance(G._adj, G.adjlist_outer_dict_factory)
- assert edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
- assert edges_equal(G.edges(0), [(0, 1), (0, 2)])
- assert edges_equal(G.edges([0, 1]), [(0, 1), (0, 2), (1, 2)])
- with pytest.raises(nx.NetworkXError):
- G.edges(-1)
- def test_degree(self):
- G = self.K3
- assert sorted(G.degree()) == [(0, 2), (1, 2), (2, 2)]
- assert dict(G.degree()) == {0: 2, 1: 2, 2: 2}
- assert G.degree(0) == 2
- with pytest.raises(nx.NetworkXError):
- G.degree(-1) # node not in graph
- def test_size(self):
- G = self.K3
- assert G.size() == 3
- assert G.number_of_edges() == 3
- def test_nbunch_iter(self):
- G = self.K3
- assert nodes_equal(G.nbunch_iter(), self.k3nodes) # all nodes
- assert nodes_equal(G.nbunch_iter(0), [0]) # single node
- assert nodes_equal(G.nbunch_iter([0, 1]), [0, 1]) # sequence
- # sequence with none in graph
- assert nodes_equal(G.nbunch_iter([-1]), [])
- # string sequence with none in graph
- assert nodes_equal(G.nbunch_iter("foo"), [])
- # node not in graph doesn't get caught upon creation of iterator
- bunch = G.nbunch_iter(-1)
- # but gets caught when iterator used
- with pytest.raises(nx.NetworkXError, match="is not a node or a sequence"):
- list(bunch)
- # unhashable doesn't get caught upon creation of iterator
- bunch = G.nbunch_iter([0, 1, 2, {}])
- # but gets caught when iterator hits the unhashable
- with pytest.raises(
- nx.NetworkXError, match="in sequence nbunch is not a valid node"
- ):
- list(bunch)
- def test_nbunch_iter_node_format_raise(self):
- # Tests that a node that would have failed string formatting
- # doesn't cause an error when attempting to raise a
- # :exc:`nx.NetworkXError`.
- # For more information, see pull request #1813.
- G = self.Graph()
- nbunch = [("x", set())]
- with pytest.raises(nx.NetworkXError):
- list(G.nbunch_iter(nbunch))
- def test_selfloop_degree(self):
- G = self.Graph()
- G.add_edge(1, 1)
- assert sorted(G.degree()) == [(1, 2)]
- assert dict(G.degree()) == {1: 2}
- assert G.degree(1) == 2
- assert sorted(G.degree([1])) == [(1, 2)]
- assert G.degree(1, weight="weight") == 2
- def test_selfloops(self):
- G = self.K3.copy()
- G.add_edge(0, 0)
- assert nodes_equal(nx.nodes_with_selfloops(G), [0])
- assert edges_equal(nx.selfloop_edges(G), [(0, 0)])
- assert nx.number_of_selfloops(G) == 1
- G.remove_edge(0, 0)
- G.add_edge(0, 0)
- G.remove_edges_from([(0, 0)])
- G.add_edge(1, 1)
- G.remove_node(1)
- G.add_edge(0, 0)
- G.add_edge(1, 1)
- G.remove_nodes_from([0, 1])
- def test_cache_reset(self):
- G = self.K3.copy()
- old_adj = G.adj
- assert id(G.adj) == id(old_adj)
- G._adj = {}
- assert id(G.adj) != id(old_adj)
- old_nodes = G.nodes
- assert id(G.nodes) == id(old_nodes)
- G._node = {}
- assert id(G.nodes) != id(old_nodes)
- def test_attributes_cached(self):
- G = self.K3.copy()
- assert id(G.nodes) == id(G.nodes)
- assert id(G.edges) == id(G.edges)
- assert id(G.degree) == id(G.degree)
- assert id(G.adj) == id(G.adj)
- class BaseAttrGraphTester(BaseGraphTester):
- """Tests of graph class attribute features."""
- def test_weighted_degree(self):
- G = self.Graph()
- G.add_edge(1, 2, weight=2, other=3)
- G.add_edge(2, 3, weight=3, other=4)
- assert sorted(d for n, d in G.degree(weight="weight")) == [2, 3, 5]
- assert dict(G.degree(weight="weight")) == {1: 2, 2: 5, 3: 3}
- assert G.degree(1, weight="weight") == 2
- assert nodes_equal((G.degree([1], weight="weight")), [(1, 2)])
- assert nodes_equal((d for n, d in G.degree(weight="other")), [3, 7, 4])
- assert dict(G.degree(weight="other")) == {1: 3, 2: 7, 3: 4}
- assert G.degree(1, weight="other") == 3
- assert edges_equal((G.degree([1], weight="other")), [(1, 3)])
- def add_attributes(self, G):
- G.graph["foo"] = []
- G.nodes[0]["foo"] = []
- G.remove_edge(1, 2)
- ll = []
- G.add_edge(1, 2, foo=ll)
- G.add_edge(2, 1, foo=ll)
- def test_name(self):
- G = self.Graph(name="")
- assert G.name == ""
- G = self.Graph(name="test")
- assert G.name == "test"
- def test_str_unnamed(self):
- G = self.Graph()
- G.add_edges_from([(1, 2), (2, 3)])
- assert str(G) == f"{type(G).__name__} with 3 nodes and 2 edges"
- def test_str_named(self):
- G = self.Graph(name="foo")
- G.add_edges_from([(1, 2), (2, 3)])
- assert str(G) == f"{type(G).__name__} named 'foo' with 3 nodes and 2 edges"
- def test_graph_chain(self):
- G = self.Graph([(0, 1), (1, 2)])
- DG = G.to_directed(as_view=True)
- SDG = DG.subgraph([0, 1])
- RSDG = SDG.reverse(copy=False)
- assert G is DG._graph
- assert DG is SDG._graph
- assert SDG is RSDG._graph
- def test_copy(self):
- G = self.Graph()
- G.add_node(0)
- G.add_edge(1, 2)
- self.add_attributes(G)
- # copy edge datadict but any container attr are same
- H = G.copy()
- self.graphs_equal(H, G)
- self.different_attrdict(H, G)
- self.shallow_copy_attrdict(H, G)
- def test_class_copy(self):
- G = self.Graph()
- G.add_node(0)
- G.add_edge(1, 2)
- self.add_attributes(G)
- # copy edge datadict but any container attr are same
- H = G.__class__(G)
- self.graphs_equal(H, G)
- self.different_attrdict(H, G)
- self.shallow_copy_attrdict(H, G)
- def test_fresh_copy(self):
- G = self.Graph()
- G.add_node(0)
- G.add_edge(1, 2)
- self.add_attributes(G)
- # copy graph structure but use fresh datadict
- H = G.__class__()
- H.add_nodes_from(G)
- H.add_edges_from(G.edges())
- assert len(G.nodes[0]) == 1
- ddict = G.adj[1][2][0] if G.is_multigraph() else G.adj[1][2]
- assert len(ddict) == 1
- assert len(H.nodes[0]) == 0
- ddict = H.adj[1][2][0] if H.is_multigraph() else H.adj[1][2]
- assert len(ddict) == 0
- def is_deepcopy(self, H, G):
- self.graphs_equal(H, G)
- self.different_attrdict(H, G)
- self.deep_copy_attrdict(H, G)
- def deep_copy_attrdict(self, H, G):
- self.deepcopy_graph_attr(H, G)
- self.deepcopy_node_attr(H, G)
- self.deepcopy_edge_attr(H, G)
- def deepcopy_graph_attr(self, H, G):
- assert G.graph["foo"] == H.graph["foo"]
- G.graph["foo"].append(1)
- assert G.graph["foo"] != H.graph["foo"]
- def deepcopy_node_attr(self, H, G):
- assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
- G.nodes[0]["foo"].append(1)
- assert G.nodes[0]["foo"] != H.nodes[0]["foo"]
- def deepcopy_edge_attr(self, H, G):
- assert G[1][2]["foo"] == H[1][2]["foo"]
- G[1][2]["foo"].append(1)
- assert G[1][2]["foo"] != H[1][2]["foo"]
- def is_shallow_copy(self, H, G):
- self.graphs_equal(H, G)
- self.shallow_copy_attrdict(H, G)
- def shallow_copy_attrdict(self, H, G):
- self.shallow_copy_graph_attr(H, G)
- self.shallow_copy_node_attr(H, G)
- self.shallow_copy_edge_attr(H, G)
- def shallow_copy_graph_attr(self, H, G):
- assert G.graph["foo"] == H.graph["foo"]
- G.graph["foo"].append(1)
- assert G.graph["foo"] == H.graph["foo"]
- def shallow_copy_node_attr(self, H, G):
- assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
- G.nodes[0]["foo"].append(1)
- assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
- def shallow_copy_edge_attr(self, H, G):
- assert G[1][2]["foo"] == H[1][2]["foo"]
- G[1][2]["foo"].append(1)
- assert G[1][2]["foo"] == H[1][2]["foo"]
- def same_attrdict(self, H, G):
- old_foo = H[1][2]["foo"]
- H.adj[1][2]["foo"] = "baz"
- assert G.edges == H.edges
- H.adj[1][2]["foo"] = old_foo
- assert G.edges == H.edges
- old_foo = H.nodes[0]["foo"]
- H.nodes[0]["foo"] = "baz"
- assert G.nodes == H.nodes
- H.nodes[0]["foo"] = old_foo
- assert G.nodes == H.nodes
- def different_attrdict(self, H, G):
- old_foo = H[1][2]["foo"]
- H.adj[1][2]["foo"] = "baz"
- assert G._adj != H._adj
- H.adj[1][2]["foo"] = old_foo
- assert G._adj == H._adj
- old_foo = H.nodes[0]["foo"]
- H.nodes[0]["foo"] = "baz"
- assert G._node != H._node
- H.nodes[0]["foo"] = old_foo
- assert G._node == H._node
- def graphs_equal(self, H, G):
- assert G._adj == H._adj
- assert G._node == H._node
- assert G.graph == H.graph
- assert G.name == H.name
- if not G.is_directed() and not H.is_directed():
- assert H._adj[1][2] is H._adj[2][1]
- assert G._adj[1][2] is G._adj[2][1]
- else: # at least one is directed
- if not G.is_directed():
- G._pred = G._adj
- G._succ = G._adj
- if not H.is_directed():
- H._pred = H._adj
- H._succ = H._adj
- assert G._pred == H._pred
- assert G._succ == H._succ
- assert H._succ[1][2] is H._pred[2][1]
- assert G._succ[1][2] is G._pred[2][1]
- def test_graph_attr(self):
- G = self.K3.copy()
- G.graph["foo"] = "bar"
- assert isinstance(G.graph, G.graph_attr_dict_factory)
- assert G.graph["foo"] == "bar"
- del G.graph["foo"]
- assert G.graph == {}
- H = self.Graph(foo="bar")
- assert H.graph["foo"] == "bar"
- def test_node_attr(self):
- G = self.K3.copy()
- G.add_node(1, foo="bar")
- assert all(
- isinstance(d, G.node_attr_dict_factory) for u, d in G.nodes(data=True)
- )
- assert nodes_equal(G.nodes(), [0, 1, 2])
- assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "bar"}), (2, {})])
- G.nodes[1]["foo"] = "baz"
- assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "baz"}), (2, {})])
- assert nodes_equal(G.nodes(data="foo"), [(0, None), (1, "baz"), (2, None)])
- assert nodes_equal(
- G.nodes(data="foo", default="bar"), [(0, "bar"), (1, "baz"), (2, "bar")]
- )
- def test_node_attr2(self):
- G = self.K3.copy()
- a = {"foo": "bar"}
- G.add_node(3, **a)
- assert nodes_equal(G.nodes(), [0, 1, 2, 3])
- assert nodes_equal(
- G.nodes(data=True), [(0, {}), (1, {}), (2, {}), (3, {"foo": "bar"})]
- )
- def test_edge_lookup(self):
- G = self.Graph()
- G.add_edge(1, 2, foo="bar")
- assert edges_equal(G.edges[1, 2], {"foo": "bar"})
- def test_edge_attr(self):
- G = self.Graph()
- G.add_edge(1, 2, foo="bar")
- assert all(
- isinstance(d, G.edge_attr_dict_factory) for u, v, d in G.edges(data=True)
- )
- assert edges_equal(G.edges(data=True), [(1, 2, {"foo": "bar"})])
- assert edges_equal(G.edges(data="foo"), [(1, 2, "bar")])
- def test_edge_attr2(self):
- G = self.Graph()
- G.add_edges_from([(1, 2), (3, 4)], foo="foo")
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"foo": "foo"}), (3, 4, {"foo": "foo"})]
- )
- assert edges_equal(G.edges(data="foo"), [(1, 2, "foo"), (3, 4, "foo")])
- def test_edge_attr3(self):
- G = self.Graph()
- G.add_edges_from([(1, 2, {"weight": 32}), (3, 4, {"weight": 64})], foo="foo")
- assert edges_equal(
- G.edges(data=True),
- [
- (1, 2, {"foo": "foo", "weight": 32}),
- (3, 4, {"foo": "foo", "weight": 64}),
- ],
- )
- G.remove_edges_from([(1, 2), (3, 4)])
- G.add_edge(1, 2, data=7, spam="bar", bar="foo")
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
- )
- def test_edge_attr4(self):
- G = self.Graph()
- G.add_edge(1, 2, data=7, spam="bar", bar="foo")
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
- )
- G[1][2]["data"] = 10 # OK to set data like this
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"data": 10, "spam": "bar", "bar": "foo"})]
- )
- G.adj[1][2]["data"] = 20
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})]
- )
- G.edges[1, 2]["data"] = 21 # another spelling, "edge"
- assert edges_equal(
- G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})]
- )
- G.adj[1][2]["listdata"] = [20, 200]
- G.adj[1][2]["weight"] = 20
- dd = {
- "data": 21,
- "spam": "bar",
- "bar": "foo",
- "listdata": [20, 200],
- "weight": 20,
- }
- assert edges_equal(G.edges(data=True), [(1, 2, dd)])
- def test_to_undirected(self):
- G = self.K3
- self.add_attributes(G)
- H = nx.Graph(G)
- self.is_shallow_copy(H, G)
- self.different_attrdict(H, G)
- H = G.to_undirected()
- self.is_deepcopy(H, G)
- def test_to_directed_as_view(self):
- H = nx.path_graph(2, create_using=self.Graph)
- H2 = H.to_directed(as_view=True)
- assert H is H2._graph
- assert H2.has_edge(0, 1)
- assert H2.has_edge(1, 0) or H.is_directed()
- pytest.raises(nx.NetworkXError, H2.add_node, -1)
- pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
- H.add_edge(1, 2)
- assert H2.has_edge(1, 2)
- assert H2.has_edge(2, 1) or H.is_directed()
- def test_to_undirected_as_view(self):
- H = nx.path_graph(2, create_using=self.Graph)
- H2 = H.to_undirected(as_view=True)
- assert H is H2._graph
- assert H2.has_edge(0, 1)
- assert H2.has_edge(1, 0)
- pytest.raises(nx.NetworkXError, H2.add_node, -1)
- pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
- H.add_edge(1, 2)
- assert H2.has_edge(1, 2)
- assert H2.has_edge(2, 1)
- def test_directed_class(self):
- G = self.Graph()
- class newGraph(G.to_undirected_class()):
- def to_directed_class(self):
- return newDiGraph
- def to_undirected_class(self):
- return newGraph
- class newDiGraph(G.to_directed_class()):
- def to_directed_class(self):
- return newDiGraph
- def to_undirected_class(self):
- return newGraph
- G = newDiGraph() if G.is_directed() else newGraph()
- H = G.to_directed()
- assert isinstance(H, newDiGraph)
- H = G.to_undirected()
- assert isinstance(H, newGraph)
- def test_to_directed(self):
- G = self.K3
- self.add_attributes(G)
- H = nx.DiGraph(G)
- self.is_shallow_copy(H, G)
- self.different_attrdict(H, G)
- H = G.to_directed()
- self.is_deepcopy(H, G)
- def test_subgraph(self):
- G = self.K3
- self.add_attributes(G)
- H = G.subgraph([0, 1, 2, 5])
- self.graphs_equal(H, G)
- self.same_attrdict(H, G)
- self.shallow_copy_attrdict(H, G)
- H = G.subgraph(0)
- assert H.adj == {0: {}}
- H = G.subgraph([])
- assert H.adj == {}
- assert G.adj != {}
- def test_selfloops_attr(self):
- G = self.K3.copy()
- G.add_edge(0, 0)
- G.add_edge(1, 1, weight=2)
- assert edges_equal(
- nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
- )
- assert edges_equal(
- nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)]
- )
- class TestGraph(BaseAttrGraphTester):
- """Tests specific to dict-of-dict-of-dict graph data structure"""
- def setup_method(self):
- self.Graph = nx.Graph
- # build dict-of-dict-of-dict K3
- ed1, ed2, ed3 = ({}, {}, {})
- self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
- self.k3edges = [(0, 1), (0, 2), (1, 2)]
- self.k3nodes = [0, 1, 2]
- self.K3 = self.Graph()
- self.K3._adj = self.k3adj
- self.K3._node = {}
- self.K3._node[0] = {}
- self.K3._node[1] = {}
- self.K3._node[2] = {}
- def test_pickle(self):
- G = self.K3
- pg = pickle.loads(pickle.dumps(G, -1))
- self.graphs_equal(pg, G)
- pg = pickle.loads(pickle.dumps(G))
- self.graphs_equal(pg, G)
- def test_data_input(self):
- G = self.Graph({1: [2], 2: [1]}, name="test")
- assert G.name == "test"
- assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
- def test_adjacency(self):
- G = self.K3
- assert dict(G.adjacency()) == {
- 0: {1: {}, 2: {}},
- 1: {0: {}, 2: {}},
- 2: {0: {}, 1: {}},
- }
- def test_getitem(self):
- G = self.K3
- assert G.adj[0] == {1: {}, 2: {}}
- assert G[0] == {1: {}, 2: {}}
- with pytest.raises(KeyError):
- G.__getitem__("j")
- with pytest.raises(TypeError):
- G.__getitem__(["A"])
- def test_add_node(self):
- G = self.Graph()
- G.add_node(0)
- assert G.adj == {0: {}}
- # test add attributes
- G.add_node(1, c="red")
- G.add_node(2, c="blue")
- G.add_node(3, c="red")
- assert G.nodes[1]["c"] == "red"
- assert G.nodes[2]["c"] == "blue"
- assert G.nodes[3]["c"] == "red"
- # test updating attributes
- G.add_node(1, c="blue")
- G.add_node(2, c="red")
- G.add_node(3, c="blue")
- assert G.nodes[1]["c"] == "blue"
- assert G.nodes[2]["c"] == "red"
- assert G.nodes[3]["c"] == "blue"
- def test_add_nodes_from(self):
- G = self.Graph()
- G.add_nodes_from([0, 1, 2])
- assert G.adj == {0: {}, 1: {}, 2: {}}
- # test add attributes
- G.add_nodes_from([0, 1, 2], c="red")
- assert G.nodes[0]["c"] == "red"
- assert G.nodes[2]["c"] == "red"
- # test that attribute dicts are not the same
- assert G.nodes[0] is not G.nodes[1]
- # test updating attributes
- G.add_nodes_from([0, 1, 2], c="blue")
- assert G.nodes[0]["c"] == "blue"
- assert G.nodes[2]["c"] == "blue"
- assert G.nodes[0] is not G.nodes[1]
- # test tuple input
- H = self.Graph()
- H.add_nodes_from(G.nodes(data=True))
- assert H.nodes[0]["c"] == "blue"
- assert H.nodes[2]["c"] == "blue"
- assert H.nodes[0] is not H.nodes[1]
- # specific overrides general
- H.add_nodes_from([0, (1, {"c": "green"}), (3, {"c": "cyan"})], c="red")
- assert H.nodes[0]["c"] == "red"
- assert H.nodes[1]["c"] == "green"
- assert H.nodes[2]["c"] == "blue"
- assert H.nodes[3]["c"] == "cyan"
- def test_remove_node(self):
- G = self.K3.copy()
- G.remove_node(0)
- assert G.adj == {1: {2: {}}, 2: {1: {}}}
- with pytest.raises(nx.NetworkXError):
- G.remove_node(-1)
- # generator here to implement list,set,string...
- def test_remove_nodes_from(self):
- G = self.K3.copy()
- G.remove_nodes_from([0, 1])
- assert G.adj == {2: {}}
- G.remove_nodes_from([-1]) # silent fail
- def test_add_edge(self):
- G = self.Graph()
- G.add_edge(0, 1)
- assert G.adj == {0: {1: {}}, 1: {0: {}}}
- G = self.Graph()
- G.add_edge(*(0, 1))
- assert G.adj == {0: {1: {}}, 1: {0: {}}}
- G = self.Graph()
- with pytest.raises(ValueError):
- G.add_edge(None, "anything")
- def test_add_edges_from(self):
- G = self.Graph()
- G.add_edges_from([(0, 1), (0, 2, {"weight": 3})])
- assert G.adj == {
- 0: {1: {}, 2: {"weight": 3}},
- 1: {0: {}},
- 2: {0: {"weight": 3}},
- }
- G = self.Graph()
- G.add_edges_from([(0, 1), (0, 2, {"weight": 3}), (1, 2, {"data": 4})], data=2)
- assert G.adj == {
- 0: {1: {"data": 2}, 2: {"weight": 3, "data": 2}},
- 1: {0: {"data": 2}, 2: {"data": 4}},
- 2: {0: {"weight": 3, "data": 2}, 1: {"data": 4}},
- }
- with pytest.raises(nx.NetworkXError):
- G.add_edges_from([(0,)]) # too few in tuple
- with pytest.raises(nx.NetworkXError):
- G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
- with pytest.raises(TypeError):
- G.add_edges_from([0]) # not a tuple
- with pytest.raises(ValueError):
- G.add_edges_from([(None, 3), (3, 2)]) # None cannot be a node
- def test_remove_edge(self):
- G = self.K3.copy()
- G.remove_edge(0, 1)
- assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
- with pytest.raises(nx.NetworkXError):
- G.remove_edge(-1, 0)
- def test_remove_edges_from(self):
- G = self.K3.copy()
- G.remove_edges_from([(0, 1)])
- assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
- G.remove_edges_from([(0, 0)]) # silent fail
- def test_clear(self):
- G = self.K3.copy()
- G.graph["name"] = "K3"
- G.clear()
- assert list(G.nodes) == []
- assert G.adj == {}
- assert G.graph == {}
- def test_clear_edges(self):
- G = self.K3.copy()
- G.graph["name"] = "K3"
- nodes = list(G.nodes)
- G.clear_edges()
- assert list(G.nodes) == nodes
- assert G.adj == {0: {}, 1: {}, 2: {}}
- assert list(G.edges) == []
- assert G.graph["name"] == "K3"
- def test_edges_data(self):
- G = self.K3
- all_edges = [(0, 1, {}), (0, 2, {}), (1, 2, {})]
- assert edges_equal(G.edges(data=True), all_edges)
- assert edges_equal(G.edges(0, data=True), [(0, 1, {}), (0, 2, {})])
- assert edges_equal(G.edges([0, 1], data=True), all_edges)
- with pytest.raises(nx.NetworkXError):
- G.edges(-1, True)
- def test_get_edge_data(self):
- G = self.K3.copy()
- assert G.get_edge_data(0, 1) == {}
- assert G[0][1] == {}
- assert G.get_edge_data(10, 20) is None
- assert G.get_edge_data(-1, 0) is None
- assert G.get_edge_data(-1, 0, default=1) == 1
- def test_update(self):
- # specify both edgees and nodes
- G = self.K3.copy()
- G.update(nodes=[3, (4, {"size": 2})], edges=[(4, 5), (6, 7, {"weight": 2})])
- nlist = [
- (0, {}),
- (1, {}),
- (2, {}),
- (3, {}),
- (4, {"size": 2}),
- (5, {}),
- (6, {}),
- (7, {}),
- ]
- assert sorted(G.nodes.data()) == nlist
- if G.is_directed():
- elist = [
- (0, 1, {}),
- (0, 2, {}),
- (1, 0, {}),
- (1, 2, {}),
- (2, 0, {}),
- (2, 1, {}),
- (4, 5, {}),
- (6, 7, {"weight": 2}),
- ]
- else:
- elist = [
- (0, 1, {}),
- (0, 2, {}),
- (1, 2, {}),
- (4, 5, {}),
- (6, 7, {"weight": 2}),
- ]
- assert sorted(G.edges.data()) == elist
- assert G.graph == {}
- # no keywords -- order is edges, nodes
- G = self.K3.copy()
- G.update([(4, 5), (6, 7, {"weight": 2})], [3, (4, {"size": 2})])
- assert sorted(G.nodes.data()) == nlist
- assert sorted(G.edges.data()) == elist
- assert G.graph == {}
- # update using only a graph
- G = self.Graph()
- G.graph["foo"] = "bar"
- G.add_node(2, data=4)
- G.add_edge(0, 1, weight=0.5)
- GG = G.copy()
- H = self.Graph()
- GG.update(H)
- assert graphs_equal(G, GG)
- H.update(G)
- assert graphs_equal(H, G)
- # update nodes only
- H = self.Graph()
- H.update(nodes=[3, 4])
- assert H.nodes ^ {3, 4} == set()
- assert H.size() == 0
- # update edges only
- H = self.Graph()
- H.update(edges=[(3, 4)])
- assert sorted(H.edges.data()) == [(3, 4, {})]
- assert H.size() == 1
- # No inputs -> exception
- with pytest.raises(nx.NetworkXError):
- nx.Graph().update()
- class TestEdgeSubgraph:
- """Unit tests for the :meth:`Graph.edge_subgraph` method."""
- def setup_method(self):
- # Create a path graph on five nodes.
- G = nx.path_graph(5)
- # Add some node, edge, and graph attributes.
- for i in range(5):
- G.nodes[i]["name"] = f"node{i}"
- G.edges[0, 1]["name"] = "edge01"
- G.edges[3, 4]["name"] = "edge34"
- G.graph["name"] = "graph"
- # Get the subgraph induced by the first and last edges.
- self.G = G
- self.H = G.edge_subgraph([(0, 1), (3, 4)])
- def test_correct_nodes(self):
- """Tests that the subgraph has the correct nodes."""
- assert [0, 1, 3, 4] == sorted(self.H.nodes())
- def test_correct_edges(self):
- """Tests that the subgraph has the correct edges."""
- assert [(0, 1, "edge01"), (3, 4, "edge34")] == sorted(self.H.edges(data="name"))
- def test_add_node(self):
- """Tests that adding a node to the original graph does not
- affect the nodes of the subgraph.
- """
- self.G.add_node(5)
- assert [0, 1, 3, 4] == sorted(self.H.nodes())
- def test_remove_node(self):
- """Tests that removing a node in the original graph does
- affect the nodes of the subgraph.
- """
- self.G.remove_node(0)
- assert [1, 3, 4] == sorted(self.H.nodes())
- def test_node_attr_dict(self):
- """Tests that the node attribute dictionary of the two graphs is
- the same object.
- """
- for v in self.H:
- assert self.G.nodes[v] == self.H.nodes[v]
- # Making a change to G should make a change in H and vice versa.
- self.G.nodes[0]["name"] = "foo"
- assert self.G.nodes[0] == self.H.nodes[0]
- self.H.nodes[1]["name"] = "bar"
- assert self.G.nodes[1] == self.H.nodes[1]
- def test_edge_attr_dict(self):
- """Tests that the edge attribute dictionary of the two graphs is
- the same object.
- """
- for u, v in self.H.edges():
- assert self.G.edges[u, v] == self.H.edges[u, v]
- # Making a change to G should make a change in H and vice versa.
- self.G.edges[0, 1]["name"] = "foo"
- assert self.G.edges[0, 1]["name"] == self.H.edges[0, 1]["name"]
- self.H.edges[3, 4]["name"] = "bar"
- assert self.G.edges[3, 4]["name"] == self.H.edges[3, 4]["name"]
- def test_graph_attr_dict(self):
- """Tests that the graph attribute dictionary of the two graphs
- is the same object.
- """
- assert self.G.graph is self.H.graph
|