import pytest import networkx as nx from networkx.utils import nodes_equal from .test_graph import BaseAttrGraphTester, BaseGraphTester from .test_graph import TestEdgeSubgraph as _TestGraphEdgeSubgraph from .test_graph import TestGraph as _TestGraph class BaseDiGraphTester(BaseGraphTester): def test_has_successor(self): G = self.K3 assert G.has_successor(0, 1) assert not G.has_successor(0, -1) def test_successors(self): G = self.K3 assert sorted(G.successors(0)) == [1, 2] with pytest.raises(nx.NetworkXError): G.successors(-1) def test_has_predecessor(self): G = self.K3 assert G.has_predecessor(0, 1) assert not G.has_predecessor(0, -1) def test_predecessors(self): G = self.K3 assert sorted(G.predecessors(0)) == [1, 2] with pytest.raises(nx.NetworkXError): G.predecessors(-1) def test_edges(self): G = self.K3 assert sorted(G.edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] assert sorted(G.edges(0)) == [(0, 1), (0, 2)] assert sorted(G.edges([0, 1])) == [(0, 1), (0, 2), (1, 0), (1, 2)] with pytest.raises(nx.NetworkXError): G.edges(-1) def test_out_edges(self): G = self.K3 assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)] with pytest.raises(nx.NetworkXError): G.out_edges(-1) def test_out_edges_dir(self): G = self.P3 assert sorted(G.out_edges()) == [(0, 1), (1, 2)] assert sorted(G.out_edges(0)) == [(0, 1)] assert sorted(G.out_edges(2)) == [] def test_out_edges_data(self): G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})]) assert sorted(G.out_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})] assert sorted(G.out_edges(0, data=True)) == [(0, 1, {"data": 0})] assert sorted(G.out_edges(data="data")) == [(0, 1, 0), (1, 0, None)] assert sorted(G.out_edges(0, data="data")) == [(0, 1, 0)] def test_in_edges_dir(self): G = self.P3 assert sorted(G.in_edges()) == [(0, 1), (1, 2)] assert sorted(G.in_edges(0)) == [] assert sorted(G.in_edges(2)) == [(1, 2)] def test_in_edges_data(self): G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})]) assert sorted(G.in_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})] assert sorted(G.in_edges(1, data=True)) == [(0, 1, {"data": 0})] assert sorted(G.in_edges(data="data")) == [(0, 1, 0), (1, 0, None)] assert sorted(G.in_edges(1, data="data")) == [(0, 1, 0)] def test_degree(self): G = self.K3 assert sorted(G.degree()) == [(0, 4), (1, 4), (2, 4)] assert dict(G.degree()) == {0: 4, 1: 4, 2: 4} assert G.degree(0) == 4 assert list(G.degree(iter([0]))) == [(0, 4)] # run through iterator def test_in_degree(self): G = self.K3 assert sorted(G.in_degree()) == [(0, 2), (1, 2), (2, 2)] assert dict(G.in_degree()) == {0: 2, 1: 2, 2: 2} assert G.in_degree(0) == 2 assert list(G.in_degree(iter([0]))) == [(0, 2)] # run through iterator def test_out_degree(self): G = self.K3 assert sorted(G.out_degree()) == [(0, 2), (1, 2), (2, 2)] assert dict(G.out_degree()) == {0: 2, 1: 2, 2: 2} assert G.out_degree(0) == 2 assert list(G.out_degree(iter([0]))) == [(0, 2)] def test_size(self): G = self.K3 assert G.size() == 6 assert G.number_of_edges() == 6 def test_to_undirected_reciprocal(self): G = self.Graph() G.add_edge(1, 2) assert G.to_undirected().has_edge(1, 2) assert not G.to_undirected(reciprocal=True).has_edge(1, 2) G.add_edge(2, 1) assert G.to_undirected(reciprocal=True).has_edge(1, 2) def test_reverse_copy(self): G = nx.DiGraph([(0, 1), (1, 2)]) R = G.reverse() assert sorted(R.edges()) == [(1, 0), (2, 1)] R.remove_edge(1, 0) assert sorted(R.edges()) == [(2, 1)] assert sorted(G.edges()) == [(0, 1), (1, 2)] def test_reverse_nocopy(self): G = nx.DiGraph([(0, 1), (1, 2)]) R = G.reverse(copy=False) assert sorted(R.edges()) == [(1, 0), (2, 1)] with pytest.raises(nx.NetworkXError): R.remove_edge(1, 0) def test_reverse_hashable(self): class Foo: pass x = Foo() y = Foo() G = nx.DiGraph() G.add_edge(x, y) assert nodes_equal(G.nodes(), G.reverse().nodes()) assert [(y, x)] == list(G.reverse().edges()) def test_di_cache_reset(self): G = self.K3.copy() old_succ = G.succ assert id(G.succ) == id(old_succ) old_adj = G.adj assert id(G.adj) == id(old_adj) G._succ = {} assert id(G.succ) != id(old_succ) assert id(G.adj) != id(old_adj) old_pred = G.pred assert id(G.pred) == id(old_pred) G._pred = {} assert id(G.pred) != id(old_pred) def test_di_attributes_cached(self): G = self.K3.copy() assert id(G.in_edges) == id(G.in_edges) assert id(G.out_edges) == id(G.out_edges) assert id(G.in_degree) == id(G.in_degree) assert id(G.out_degree) == id(G.out_degree) assert id(G.succ) == id(G.succ) assert id(G.pred) == id(G.pred) class BaseAttrDiGraphTester(BaseDiGraphTester, BaseAttrGraphTester): def test_edges_data(self): G = self.K3 all_edges = [ (0, 1, {}), (0, 2, {}), (1, 0, {}), (1, 2, {}), (2, 0, {}), (2, 1, {}), ] assert sorted(G.edges(data=True)) == all_edges assert sorted(G.edges(0, data=True)) == all_edges[:2] assert sorted(G.edges([0, 1], data=True)) == all_edges[:4] with pytest.raises(nx.NetworkXError): G.edges(-1, True) def test_in_degree_weighted(self): G = self.K3.copy() G.add_edge(0, 1, weight=0.3, other=1.2) assert sorted(G.in_degree(weight="weight")) == [(0, 2), (1, 1.3), (2, 2)] assert dict(G.in_degree(weight="weight")) == {0: 2, 1: 1.3, 2: 2} assert G.in_degree(1, weight="weight") == 1.3 assert sorted(G.in_degree(weight="other")) == [(0, 2), (1, 2.2), (2, 2)] assert dict(G.in_degree(weight="other")) == {0: 2, 1: 2.2, 2: 2} assert G.in_degree(1, weight="other") == 2.2 assert list(G.in_degree(iter([1]), weight="other")) == [(1, 2.2)] def test_out_degree_weighted(self): G = self.K3.copy() G.add_edge(0, 1, weight=0.3, other=1.2) assert sorted(G.out_degree(weight="weight")) == [(0, 1.3), (1, 2), (2, 2)] assert dict(G.out_degree(weight="weight")) == {0: 1.3, 1: 2, 2: 2} assert G.out_degree(0, weight="weight") == 1.3 assert sorted(G.out_degree(weight="other")) == [(0, 2.2), (1, 2), (2, 2)] assert dict(G.out_degree(weight="other")) == {0: 2.2, 1: 2, 2: 2} assert G.out_degree(0, weight="other") == 2.2 assert list(G.out_degree(iter([0]), weight="other")) == [(0, 2.2)] class TestDiGraph(BaseAttrDiGraphTester, _TestGraph): """Tests specific to dict-of-dict-of-dict digraph data structure""" def setup_method(self): self.Graph = nx.DiGraph # build dict-of-dict-of-dict K3 ed1, ed2, ed3, ed4, ed5, ed6 = ({}, {}, {}, {}, {}, {}) self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed3, 2: ed4}, 2: {0: ed5, 1: ed6}} self.k3edges = [(0, 1), (0, 2), (1, 2)] self.k3nodes = [0, 1, 2] self.K3 = self.Graph() self.K3._succ = self.k3adj # K3._adj is synced with K3._succ self.K3._pred = {0: {1: ed3, 2: ed5}, 1: {0: ed1, 2: ed6}, 2: {0: ed2, 1: ed4}} self.K3._node = {} self.K3._node[0] = {} self.K3._node[1] = {} self.K3._node[2] = {} ed1, ed2 = ({}, {}) self.P3 = self.Graph() self.P3._succ = {0: {1: ed1}, 1: {2: ed2}, 2: {}} self.P3._pred = {0: {}, 1: {0: ed1}, 2: {1: ed2}} # P3._adj is synced with P3._succ self.P3._node = {} self.P3._node[0] = {} self.P3._node[1] = {} self.P3._node[2] = {} 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: {}})] assert sorted(G.succ.items()) == [(1, {2: {}}), (2, {1: {}})] assert sorted(G.pred.items()) == [(1, {2: {}}), (2, {1: {}})] def test_add_edge(self): G = self.Graph() G.add_edge(0, 1) assert G.adj == {0: {1: {}}, 1: {}} assert G.succ == {0: {1: {}}, 1: {}} assert G.pred == {0: {}, 1: {0: {}}} G = self.Graph() G.add_edge(*(0, 1)) assert G.adj == {0: {1: {}}, 1: {}} assert G.succ == {0: {1: {}}, 1: {}} assert G.pred == {0: {}, 1: {0: {}}} with pytest.raises(ValueError, match="None cannot be a node"): G.add_edge(None, 3) def test_add_edges_from(self): G = self.Graph() G.add_edges_from([(0, 1), (0, 2, {"data": 3})], data=2) assert G.adj == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}} assert G.succ == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}} assert G.pred == {0: {}, 1: {0: {"data": 2}}, 2: {0: {"data": 3}}} 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, match="None cannot be a node"): G.add_edges_from([(None, 3), (3, 2)]) def test_remove_edge(self): G = self.K3.copy() G.remove_edge(0, 1) assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}} assert G.pred == {0: {1: {}, 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.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}} assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}} G.remove_edges_from([(0, 0)]) # silent fail def test_clear(self): G = self.K3 G.graph["name"] = "K3" G.clear() assert list(G.nodes) == [] assert G.succ == {} assert G.pred == {} assert G.graph == {} def test_clear_edges(self): G = self.K3 G.graph["name"] = "K3" nodes = list(G.nodes) G.clear_edges() assert list(G.nodes) == nodes expected = {0: {}, 1: {}, 2: {}} assert G.succ == expected assert G.pred == expected assert list(G.edges) == [] assert G.graph["name"] == "K3" class TestEdgeSubgraph(_TestGraphEdgeSubgraph): """Unit tests for the :meth:`DiGraph.edge_subgraph` method.""" def setup_method(self): # Create a doubly-linked path graph on five nodes. G = nx.DiGraph(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_pred_succ(self): """Test that nodes are added to predecessors and successors. For more information, see GitHub issue #2370. """ G = nx.DiGraph() G.add_edge(0, 1) H = G.edge_subgraph([(0, 1)]) assert list(H.predecessors(0)) == [] assert list(H.successors(0)) == [1] assert list(H.predecessors(1)) == [0] assert list(H.successors(1)) == []