import pytest import networkx as nx from networkx.testing.utils import assert_edges_equal from .test_graph import BaseAttrGraphTester from .test_graph import TestGraph as _TestGraph class BaseMultiGraphTester(BaseAttrGraphTester): def test_has_edge(self): G = self.K3 assert G.has_edge(0, 1) assert not G.has_edge(0, -1) assert G.has_edge(0, 1, 0) assert not G.has_edge(0, 1, 1) def test_get_edge_data(self): G = self.K3 assert G.get_edge_data(0, 1) == {0: {}} assert G[0][1] == {0: {}} assert G[0][1][0] == {} assert G.get_edge_data(10, 20) is None assert G.get_edge_data(0, 1, 0) == {} def test_adjacency(self): G = self.K3 assert dict(G.adjacency()) == { 0: {1: {0: {}}, 2: {0: {}}}, 1: {0: {0: {}}, 2: {0: {}}}, 2: {0: {0: {}}, 1: {0: {}}}, } def deepcopy_edge_attr(self, H, G): assert G[1][2][0]["foo"] == H[1][2][0]["foo"] G[1][2][0]["foo"].append(1) assert G[1][2][0]["foo"] != H[1][2][0]["foo"] def shallow_copy_edge_attr(self, H, G): assert G[1][2][0]["foo"] == H[1][2][0]["foo"] G[1][2][0]["foo"].append(1) assert G[1][2][0]["foo"] == H[1][2][0]["foo"] 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][0] is H._adj[2][1][0] assert G._adj[1][2][0] is G._adj[2][1][0] 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][0] is H._pred[2][1][0] assert G._succ[1][2][0] is G._pred[2][1][0] def same_attrdict(self, H, G): # same attrdict in the edgedata old_foo = H[1][2][0]["foo"] H.adj[1][2][0]["foo"] = "baz" assert G._adj == H._adj H.adj[1][2][0]["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 different_attrdict(self, H, G): # used by graph_equal_but_different old_foo = H[1][2][0]["foo"] H.adj[1][2][0]["foo"] = "baz" assert G._adj != H._adj H.adj[1][2][0]["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 test_to_undirected(self): G = self.K3 self.add_attributes(G) H = nx.MultiGraph(G) self.is_shallow_copy(H, G) H = G.to_undirected() self.is_deepcopy(H, G) def test_to_directed(self): G = self.K3 self.add_attributes(G) H = nx.MultiDiGraph(G) self.is_shallow_copy(H, G) H = G.to_directed() self.is_deepcopy(H, G) def test_number_of_edges_selfloops(self): G = self.K3 G.add_edge(0, 0) G.add_edge(0, 0) G.add_edge(0, 0, key="parallel edge") G.remove_edge(0, 0, key="parallel edge") assert G.number_of_edges(0, 0) == 2 G.remove_edge(0, 0) assert G.number_of_edges(0, 0) == 1 def test_edge_lookup(self): G = self.Graph() G.add_edge(1, 2, foo="bar") G.add_edge(1, 2, "key", foo="biz") assert_edges_equal(G.edges[1, 2, 0], {"foo": "bar"}) assert_edges_equal(G.edges[1, 2, "key"], {"foo": "biz"}) def test_edge_attr4(self): G = self.Graph() G.add_edge(1, 2, key=0, data=7, spam="bar", bar="foo") assert_edges_equal( G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})] ) G[1][2][0]["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][0]["data"] = 20 assert_edges_equal( G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})] ) G.edges[1, 2, 0]["data"] = 21 # another spelling, "edge" assert_edges_equal( G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})] ) G.adj[1][2][0]["listdata"] = [20, 200] G.adj[1][2][0]["weight"] = 20 assert_edges_equal( G.edges(data=True), [ ( 1, 2, { "data": 21, "spam": "bar", "bar": "foo", "listdata": [20, 200], "weight": 20, }, ) ], ) class TestMultiGraph(BaseMultiGraphTester, _TestGraph): def setup_method(self): self.Graph = nx.MultiGraph # build K3 ed1, ed2, ed3 = ({0: {}}, {0: {}}, {0: {}}) 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_data_input(self): G = self.Graph({1: [2], 2: [1]}, name="test") assert G.name == "test" expected = [(1, {2: {0: {}}}), (2, {1: {0: {}}})] assert sorted(G.adj.items()) == expected def test_getitem(self): G = self.K3 assert G[0] == {1: {0: {}}, 2: {0: {}}} with pytest.raises(KeyError): G.__getitem__("j") with pytest.raises(TypeError): G.__getitem__(["A"]) def test_remove_node(self): G = self.K3 G.remove_node(0) assert G.adj == {1: {2: {0: {}}}, 2: {1: {0: {}}}} with pytest.raises(nx.NetworkXError): G.remove_node(-1) def test_add_edge(self): G = self.Graph() G.add_edge(0, 1) assert G.adj == {0: {1: {0: {}}}, 1: {0: {0: {}}}} G = self.Graph() G.add_edge(*(0, 1)) assert G.adj == {0: {1: {0: {}}}, 1: {0: {0: {}}}} def test_add_edge_conflicting_key(self): G = self.Graph() G.add_edge(0, 1, key=1) G.add_edge(0, 1) assert G.number_of_edges() == 2 G = self.Graph() G.add_edges_from([(0, 1, 1, {})]) G.add_edges_from([(0, 1)]) assert G.number_of_edges() == 2 def test_add_edges_from(self): G = self.Graph() G.add_edges_from([(0, 1), (0, 1, {"weight": 3})]) assert G.adj == { 0: {1: {0: {}, 1: {"weight": 3}}}, 1: {0: {0: {}, 1: {"weight": 3}}}, } G.add_edges_from([(0, 1), (0, 1, {"weight": 3})], weight=2) assert G.adj == { 0: {1: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}}, 1: {0: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}}, } G = self.Graph() edges = [ (0, 1, {"weight": 3}), (0, 1, (("weight", 2),)), (0, 1, 5), (0, 1, "s"), ] G.add_edges_from(edges) keydict = {0: {"weight": 3}, 1: {"weight": 2}, 5: {}, "s": {}} assert G._adj == {0: {1: keydict}, 1: {0: keydict}} # too few in tuple with pytest.raises(nx.NetworkXError): G.add_edges_from([(0,)]) # too many in tuple with pytest.raises(nx.NetworkXError): G.add_edges_from([(0, 1, 2, 3, 4)]) # not a tuple with pytest.raises(TypeError): G.add_edges_from([0]) def test_remove_edge(self): G = self.K3 G.remove_edge(0, 1) assert G.adj == {0: {2: {0: {}}}, 1: {2: {0: {}}}, 2: {0: {0: {}}, 1: {0: {}}}} with pytest.raises(nx.NetworkXError): G.remove_edge(-1, 0) with pytest.raises(nx.NetworkXError): G.remove_edge(0, 2, key=1) def test_remove_edges_from(self): G = self.K3.copy() G.remove_edges_from([(0, 1)]) kd = {0: {}} assert G.adj == {0: {2: kd}, 1: {2: kd}, 2: {0: kd, 1: kd}} G.remove_edges_from([(0, 0)]) # silent fail self.K3.add_edge(0, 1) G = self.K3.copy() G.remove_edges_from(list(G.edges(data=True, keys=True))) assert G.adj == {0: {}, 1: {}, 2: {}} G = self.K3.copy() G.remove_edges_from(list(G.edges(data=False, keys=True))) assert G.adj == {0: {}, 1: {}, 2: {}} G = self.K3.copy() G.remove_edges_from(list(G.edges(data=False, keys=False))) assert G.adj == {0: {}, 1: {}, 2: {}} G = self.K3.copy() G.remove_edges_from([(0, 1, 0), (0, 2, 0, {}), (1, 2)]) assert G.adj == {0: {1: {1: {}}}, 1: {0: {1: {}}}, 2: {}} def test_remove_multiedge(self): G = self.K3 G.add_edge(0, 1, key="parallel edge") G.remove_edge(0, 1, key="parallel edge") assert G.adj == { 0: {1: {0: {}}, 2: {0: {}}}, 1: {0: {0: {}}, 2: {0: {}}}, 2: {0: {0: {}}, 1: {0: {}}}, } G.remove_edge(0, 1) kd = {0: {}} assert G.adj == {0: {2: kd}, 1: {2: kd}, 2: {0: kd, 1: kd}} with pytest.raises(nx.NetworkXError): G.remove_edge(-1, 0) class TestEdgeSubgraph: """Unit tests for the :meth:`MultiGraph.edge_subgraph` method.""" def setup_method(self): # Create a doubly-linked path graph on five nodes. G = nx.MultiGraph() nx.add_path(G, range(5)) nx.add_path(G, range(5)) # Add some node, edge, and graph attributes. for i in range(5): G.nodes[i]["name"] = f"node{i}" G.adj[0][1][0]["name"] = "edge010" G.adj[0][1][1]["name"] = "edge011" G.adj[3][4][0]["name"] = "edge340" G.adj[3][4][1]["name"] = "edge341" G.graph["name"] = "graph" # Get the subgraph induced by one of the first edges and one of # the last edges. self.G = G self.H = G.edge_subgraph([(0, 1, 0), (3, 4, 1)]) 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, 0, "edge010"), (3, 4, 1, "edge341")] == sorted( self.H.edges(keys=True, 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, k in self.H.edges(keys=True): assert self.G._adj[u][v][k] == self.H._adj[u][v][k] # Making a change to G should make a change in H and vice versa. self.G._adj[0][1][0]["name"] = "foo" assert self.G._adj[0][1][0]["name"] == self.H._adj[0][1][0]["name"] self.H._adj[3][4][1]["name"] = "bar" assert self.G._adj[3][4][1]["name"] == self.H._adj[3][4][1]["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