aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py302
1 files changed, 302 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
new file mode 100644
index 00000000..e6880d52
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py
@@ -0,0 +1,302 @@
+"""Test trophic levels, trophic differences and trophic coherence"""
+
+import pytest
+
+np = pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+import networkx as nx
+
+
+def test_trophic_levels():
+ """Trivial example"""
+ G = nx.DiGraph()
+ G.add_edge("a", "b")
+ G.add_edge("b", "c")
+
+ d = nx.trophic_levels(G)
+ assert d == {"a": 1, "b": 2, "c": 3}
+
+
+def test_trophic_levels_levine():
+ """Example from Figure 5 in Stephen Levine (1980) J. theor. Biol. 83,
+ 195-207
+ """
+ S = nx.DiGraph()
+ S.add_edge(1, 2, weight=1.0)
+ S.add_edge(1, 3, weight=0.2)
+ S.add_edge(1, 4, weight=0.8)
+ S.add_edge(2, 3, weight=0.2)
+ S.add_edge(2, 5, weight=0.3)
+ S.add_edge(4, 3, weight=0.6)
+ S.add_edge(4, 5, weight=0.7)
+ S.add_edge(5, 4, weight=0.2)
+
+ # save copy for later, test intermediate implementation details first
+ S2 = S.copy()
+
+ # drop nodes of in-degree zero
+ z = [nid for nid, d in S.in_degree if d == 0]
+ for nid in z:
+ S.remove_node(nid)
+
+ # find adjacency matrix
+ q = nx.linalg.graphmatrix.adjacency_matrix(S).T
+
+ # fmt: off
+ expected_q = np.array([
+ [0, 0, 0., 0],
+ [0.2, 0, 0.6, 0],
+ [0, 0, 0, 0.2],
+ [0.3, 0, 0.7, 0]
+ ])
+ # fmt: on
+ assert np.array_equal(q.todense(), expected_q)
+
+ # must be square, size of number of nodes
+ assert len(q.shape) == 2
+ assert q.shape[0] == q.shape[1]
+ assert q.shape[0] == len(S)
+
+ nn = q.shape[0]
+
+ i = np.eye(nn)
+ n = np.linalg.inv(i - q)
+ y = np.asarray(n) @ np.ones(nn)
+
+ expected_y = np.array([1, 2.07906977, 1.46511628, 2.3255814])
+ assert np.allclose(y, expected_y)
+
+ expected_d = {1: 1, 2: 2, 3: 3.07906977, 4: 2.46511628, 5: 3.3255814}
+
+ d = nx.trophic_levels(S2)
+
+ for nid, level in d.items():
+ expected_level = expected_d[nid]
+ assert expected_level == pytest.approx(level, abs=1e-7)
+
+
+def test_trophic_levels_simple():
+ matrix_a = np.array([[0, 0], [1, 0]])
+ G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
+ d = nx.trophic_levels(G)
+ assert d[0] == pytest.approx(2, abs=1e-7)
+ assert d[1] == pytest.approx(1, abs=1e-7)
+
+
+def test_trophic_levels_more_complex():
+ # fmt: off
+ matrix = np.array([
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
+ d = nx.trophic_levels(G)
+ expected_result = [1, 2, 3, 4]
+ for ind in range(4):
+ assert d[ind] == pytest.approx(expected_result[ind], abs=1e-7)
+
+ # fmt: off
+ matrix = np.array([
+ [0, 1, 1, 0],
+ [0, 0, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
+ d = nx.trophic_levels(G)
+
+ expected_result = [1, 2, 2.5, 3.25]
+ print("Calculated result: ", d)
+ print("Expected Result: ", expected_result)
+
+ for ind in range(4):
+ assert d[ind] == pytest.approx(expected_result[ind], abs=1e-7)
+
+
+def test_trophic_levels_even_more_complex():
+ # fmt: off
+ # Another, bigger matrix
+ matrix = np.array([
+ [0, 0, 0, 0, 0],
+ [0, 1, 0, 1, 0],
+ [1, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0],
+ [0, 0, 0, 1, 0]
+ ])
+ # Generated this linear system using pen and paper:
+ K = np.array([
+ [1, 0, -1, 0, 0],
+ [0, 0.5, 0, -0.5, 0],
+ [0, 0, 1, 0, 0],
+ [0, -0.5, 0, 1, -0.5],
+ [0, 0, 0, 0, 1],
+ ])
+ # fmt: on
+ result_1 = np.ravel(np.linalg.inv(K) @ np.ones(5))
+ G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
+ result_2 = nx.trophic_levels(G)
+
+ for ind in range(5):
+ assert result_1[ind] == pytest.approx(result_2[ind], abs=1e-7)
+
+
+def test_trophic_levels_singular_matrix():
+ """Should raise an error with graphs with only non-basal nodes"""
+ matrix = np.identity(4)
+ G = nx.from_numpy_array(matrix, create_using=nx.DiGraph)
+ with pytest.raises(nx.NetworkXError) as e:
+ nx.trophic_levels(G)
+ msg = (
+ "Trophic levels are only defined for graphs where every node "
+ + "has a path from a basal node (basal nodes are nodes with no "
+ + "incoming edges)."
+ )
+ assert msg in str(e.value)
+
+
+def test_trophic_levels_singular_with_basal():
+ """Should fail to compute if there are any parts of the graph which are not
+ reachable from any basal node (with in-degree zero).
+ """
+ G = nx.DiGraph()
+ # a has in-degree zero
+ G.add_edge("a", "b")
+
+ # b is one level above a, c and d
+ G.add_edge("c", "b")
+ G.add_edge("d", "b")
+
+ # c and d form a loop, neither are reachable from a
+ G.add_edge("c", "d")
+ G.add_edge("d", "c")
+
+ with pytest.raises(nx.NetworkXError) as e:
+ nx.trophic_levels(G)
+ msg = (
+ "Trophic levels are only defined for graphs where every node "
+ + "has a path from a basal node (basal nodes are nodes with no "
+ + "incoming edges)."
+ )
+ assert msg in str(e.value)
+
+ # if self-loops are allowed, smaller example:
+ G = nx.DiGraph()
+ G.add_edge("a", "b") # a has in-degree zero
+ G.add_edge("c", "b") # b is one level above a and c
+ G.add_edge("c", "c") # c has a self-loop
+ with pytest.raises(nx.NetworkXError) as e:
+ nx.trophic_levels(G)
+ msg = (
+ "Trophic levels are only defined for graphs where every node "
+ + "has a path from a basal node (basal nodes are nodes with no "
+ + "incoming edges)."
+ )
+ assert msg in str(e.value)
+
+
+def test_trophic_differences():
+ matrix_a = np.array([[0, 1], [0, 0]])
+ G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
+ diffs = nx.trophic_differences(G)
+ assert diffs[(0, 1)] == pytest.approx(1, abs=1e-7)
+
+ # fmt: off
+ matrix_b = np.array([
+ [0, 1, 1, 0],
+ [0, 0, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
+ diffs = nx.trophic_differences(G)
+
+ assert diffs[(0, 1)] == pytest.approx(1, abs=1e-7)
+ assert diffs[(0, 2)] == pytest.approx(1.5, abs=1e-7)
+ assert diffs[(1, 2)] == pytest.approx(0.5, abs=1e-7)
+ assert diffs[(1, 3)] == pytest.approx(1.25, abs=1e-7)
+ assert diffs[(2, 3)] == pytest.approx(0.75, abs=1e-7)
+
+
+def test_trophic_incoherence_parameter_no_cannibalism():
+ matrix_a = np.array([[0, 1], [0, 0]])
+ G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=False)
+ assert q == pytest.approx(0, abs=1e-7)
+
+ # fmt: off
+ matrix_b = np.array([
+ [0, 1, 1, 0],
+ [0, 0, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=False)
+ assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
+
+ # fmt: off
+ matrix_c = np.array([
+ [0, 1, 1, 0],
+ [0, 1, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 1]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_c, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=False)
+ # Ignore the -link
+ assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
+
+ # no self-loops case
+ # fmt: off
+ matrix_d = np.array([
+ [0, 1, 1, 0],
+ [0, 0, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_d, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=False)
+ # Ignore the -link
+ assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)
+
+
+def test_trophic_incoherence_parameter_cannibalism():
+ matrix_a = np.array([[0, 1], [0, 0]])
+ G = nx.from_numpy_array(matrix_a, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=True)
+ assert q == pytest.approx(0, abs=1e-7)
+
+ # fmt: off
+ matrix_b = np.array([
+ [0, 0, 0, 0, 0],
+ [0, 1, 0, 1, 0],
+ [1, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0],
+ [0, 0, 0, 1, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_b, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=True)
+ assert q == pytest.approx(2, abs=1e-7)
+
+ # fmt: off
+ matrix_c = np.array([
+ [0, 1, 1, 0],
+ [0, 0, 1, 1],
+ [0, 0, 0, 1],
+ [0, 0, 0, 0]
+ ])
+ # fmt: on
+ G = nx.from_numpy_array(matrix_c, create_using=nx.DiGraph)
+ q = nx.trophic_incoherence_parameter(G, cannibalism=True)
+ # Ignore the -link
+ assert q == pytest.approx(np.std([1, 1.5, 0.5, 0.75, 1.25]), abs=1e-7)