aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests
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
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')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py780
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py340
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py307
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py197
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py147
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_closeness.py43
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py144
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py73
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py187
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_group.py277
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py122
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py345
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py221
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py344
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_percolation_centrality.py87
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_reaching.py140
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_second_order_centrality.py82
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py110
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_trophic.py302
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_voterank.py64
21 files changed, 4312 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/__init__.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py
new file mode 100644
index 00000000..4c059cf9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality.py
@@ -0,0 +1,780 @@
+import pytest
+
+import networkx as nx
+
+
+def weighted_G():
+ G = nx.Graph()
+ G.add_edge(0, 1, weight=3)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(0, 3, weight=6)
+ G.add_edge(0, 4, weight=4)
+ G.add_edge(1, 3, weight=5)
+ G.add_edge(1, 5, weight=5)
+ G.add_edge(2, 4, weight=1)
+ G.add_edge(3, 4, weight=2)
+ G.add_edge(3, 5, weight=1)
+ G.add_edge(4, 5, weight=4)
+ return G
+
+
+class TestBetweennessCentrality:
+ def test_K5(self):
+ """Betweenness centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_K5_endpoints(self):
+ """Betweenness centrality: K5 endpoints"""
+ G = nx.complete_graph(5)
+ b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
+ b_answer = {0: 4.0, 1: 4.0, 2: 4.0, 3: 4.0, 4: 4.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ # normalized = True case
+ b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
+ b_answer = {0: 0.4, 1: 0.4, 2: 0.4, 3: 0.4, 4: 0.4}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P3_normalized(self):
+ """Betweenness centrality: P3 normalized"""
+ G = nx.path_graph(3)
+ b = nx.betweenness_centrality(G, weight=None, normalized=True)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P3(self):
+ """Betweenness centrality: P3"""
+ G = nx.path_graph(3)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_sample_from_P3(self):
+ """Betweenness centrality: P3 sample"""
+ G = nx.path_graph(3)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ b = nx.betweenness_centrality(G, k=3, weight=None, normalized=False, seed=1)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ b = nx.betweenness_centrality(G, k=2, weight=None, normalized=False, seed=1)
+ # python versions give different results with same seed
+ b_approx1 = {0: 0.0, 1: 1.5, 2: 0.0}
+ b_approx2 = {0: 0.0, 1: 0.75, 2: 0.0}
+ for n in sorted(G):
+ assert b[n] in (b_approx1[n], b_approx2[n])
+
+ def test_P3_endpoints(self):
+ """Betweenness centrality: P3 endpoints"""
+ G = nx.path_graph(3)
+ b_answer = {0: 2.0, 1: 3.0, 2: 2.0}
+ b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ # normalized = True case
+ b_answer = {0: 2 / 3, 1: 1.0, 2: 2 / 3}
+ b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_krackhardt_kite_graph(self):
+ """Betweenness centrality: Krackhardt kite graph"""
+ G = nx.krackhardt_kite_graph()
+ b_answer = {
+ 0: 1.667,
+ 1: 1.667,
+ 2: 0.000,
+ 3: 7.333,
+ 4: 0.000,
+ 5: 16.667,
+ 6: 16.667,
+ 7: 28.000,
+ 8: 16.000,
+ 9: 0.000,
+ }
+ for b in b_answer:
+ b_answer[b] /= 2
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_krackhardt_kite_graph_normalized(self):
+ """Betweenness centrality: Krackhardt kite graph normalized"""
+ G = nx.krackhardt_kite_graph()
+ b_answer = {
+ 0: 0.023,
+ 1: 0.023,
+ 2: 0.000,
+ 3: 0.102,
+ 4: 0.000,
+ 5: 0.231,
+ 6: 0.231,
+ 7: 0.389,
+ 8: 0.222,
+ 9: 0.000,
+ }
+ b = nx.betweenness_centrality(G, weight=None, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_florentine_families_graph(self):
+ """Betweenness centrality: Florentine families graph"""
+ G = nx.florentine_families_graph()
+ b_answer = {
+ "Acciaiuoli": 0.000,
+ "Albizzi": 0.212,
+ "Barbadori": 0.093,
+ "Bischeri": 0.104,
+ "Castellani": 0.055,
+ "Ginori": 0.000,
+ "Guadagni": 0.255,
+ "Lamberteschi": 0.000,
+ "Medici": 0.522,
+ "Pazzi": 0.000,
+ "Peruzzi": 0.022,
+ "Ridolfi": 0.114,
+ "Salviati": 0.143,
+ "Strozzi": 0.103,
+ "Tornabuoni": 0.092,
+ }
+
+ b = nx.betweenness_centrality(G, weight=None, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_les_miserables_graph(self):
+ """Betweenness centrality: Les Miserables graph"""
+ G = nx.les_miserables_graph()
+ b_answer = {
+ "Napoleon": 0.000,
+ "Myriel": 0.177,
+ "MlleBaptistine": 0.000,
+ "MmeMagloire": 0.000,
+ "CountessDeLo": 0.000,
+ "Geborand": 0.000,
+ "Champtercier": 0.000,
+ "Cravatte": 0.000,
+ "Count": 0.000,
+ "OldMan": 0.000,
+ "Valjean": 0.570,
+ "Labarre": 0.000,
+ "Marguerite": 0.000,
+ "MmeDeR": 0.000,
+ "Isabeau": 0.000,
+ "Gervais": 0.000,
+ "Listolier": 0.000,
+ "Tholomyes": 0.041,
+ "Fameuil": 0.000,
+ "Blacheville": 0.000,
+ "Favourite": 0.000,
+ "Dahlia": 0.000,
+ "Zephine": 0.000,
+ "Fantine": 0.130,
+ "MmeThenardier": 0.029,
+ "Thenardier": 0.075,
+ "Cosette": 0.024,
+ "Javert": 0.054,
+ "Fauchelevent": 0.026,
+ "Bamatabois": 0.008,
+ "Perpetue": 0.000,
+ "Simplice": 0.009,
+ "Scaufflaire": 0.000,
+ "Woman1": 0.000,
+ "Judge": 0.000,
+ "Champmathieu": 0.000,
+ "Brevet": 0.000,
+ "Chenildieu": 0.000,
+ "Cochepaille": 0.000,
+ "Pontmercy": 0.007,
+ "Boulatruelle": 0.000,
+ "Eponine": 0.011,
+ "Anzelma": 0.000,
+ "Woman2": 0.000,
+ "MotherInnocent": 0.000,
+ "Gribier": 0.000,
+ "MmeBurgon": 0.026,
+ "Jondrette": 0.000,
+ "Gavroche": 0.165,
+ "Gillenormand": 0.020,
+ "Magnon": 0.000,
+ "MlleGillenormand": 0.048,
+ "MmePontmercy": 0.000,
+ "MlleVaubois": 0.000,
+ "LtGillenormand": 0.000,
+ "Marius": 0.132,
+ "BaronessT": 0.000,
+ "Mabeuf": 0.028,
+ "Enjolras": 0.043,
+ "Combeferre": 0.001,
+ "Prouvaire": 0.000,
+ "Feuilly": 0.001,
+ "Courfeyrac": 0.005,
+ "Bahorel": 0.002,
+ "Bossuet": 0.031,
+ "Joly": 0.002,
+ "Grantaire": 0.000,
+ "MotherPlutarch": 0.000,
+ "Gueulemer": 0.005,
+ "Babet": 0.005,
+ "Claquesous": 0.005,
+ "Montparnasse": 0.004,
+ "Toussaint": 0.000,
+ "Child1": 0.000,
+ "Child2": 0.000,
+ "Brujon": 0.000,
+ "MmeHucheloup": 0.000,
+ }
+
+ b = nx.betweenness_centrality(G, weight=None, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_ladder_graph(self):
+ """Betweenness centrality: Ladder graph"""
+ G = nx.Graph() # ladder_graph(3)
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
+ b_answer = {0: 1.667, 1: 1.667, 2: 6.667, 3: 6.667, 4: 1.667, 5: 1.667}
+ for b in b_answer:
+ b_answer[b] /= 2
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_disconnected_path(self):
+ """Betweenness centrality: disconnected path"""
+ G = nx.Graph()
+ nx.add_path(G, [0, 1, 2])
+ nx.add_path(G, [3, 4, 5, 6])
+ b_answer = {0: 0, 1: 1, 2: 0, 3: 0, 4: 2, 5: 2, 6: 0}
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_disconnected_path_endpoints(self):
+ """Betweenness centrality: disconnected path endpoints"""
+ G = nx.Graph()
+ nx.add_path(G, [0, 1, 2])
+ nx.add_path(G, [3, 4, 5, 6])
+ b_answer = {0: 2, 1: 3, 2: 2, 3: 3, 4: 5, 5: 5, 6: 3}
+ b = nx.betweenness_centrality(G, weight=None, normalized=False, endpoints=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ # normalized = True case
+ b = nx.betweenness_centrality(G, weight=None, normalized=True, endpoints=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n] / 21, abs=1e-7)
+
+ def test_directed_path(self):
+ """Betweenness centrality: directed path"""
+ G = nx.DiGraph()
+ nx.add_path(G, [0, 1, 2])
+ b = nx.betweenness_centrality(G, weight=None, normalized=False)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_directed_path_normalized(self):
+ """Betweenness centrality: directed path normalized"""
+ G = nx.DiGraph()
+ nx.add_path(G, [0, 1, 2])
+ b = nx.betweenness_centrality(G, weight=None, normalized=True)
+ b_answer = {0: 0.0, 1: 0.5, 2: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+class TestWeightedBetweennessCentrality:
+ def test_K5(self):
+ """Weighted betweenness centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P3_normalized(self):
+ """Weighted betweenness centrality: P3 normalized"""
+ G = nx.path_graph(3)
+ b = nx.betweenness_centrality(G, weight="weight", normalized=True)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P3(self):
+ """Weighted betweenness centrality: P3"""
+ G = nx.path_graph(3)
+ b_answer = {0: 0.0, 1: 1.0, 2: 0.0}
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_krackhardt_kite_graph(self):
+ """Weighted betweenness centrality: Krackhardt kite graph"""
+ G = nx.krackhardt_kite_graph()
+ b_answer = {
+ 0: 1.667,
+ 1: 1.667,
+ 2: 0.000,
+ 3: 7.333,
+ 4: 0.000,
+ 5: 16.667,
+ 6: 16.667,
+ 7: 28.000,
+ 8: 16.000,
+ 9: 0.000,
+ }
+ for b in b_answer:
+ b_answer[b] /= 2
+
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_krackhardt_kite_graph_normalized(self):
+ """Weighted betweenness centrality:
+ Krackhardt kite graph normalized
+ """
+ G = nx.krackhardt_kite_graph()
+ b_answer = {
+ 0: 0.023,
+ 1: 0.023,
+ 2: 0.000,
+ 3: 0.102,
+ 4: 0.000,
+ 5: 0.231,
+ 6: 0.231,
+ 7: 0.389,
+ 8: 0.222,
+ 9: 0.000,
+ }
+ b = nx.betweenness_centrality(G, weight="weight", normalized=True)
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_florentine_families_graph(self):
+ """Weighted betweenness centrality:
+ Florentine families graph"""
+ G = nx.florentine_families_graph()
+ b_answer = {
+ "Acciaiuoli": 0.000,
+ "Albizzi": 0.212,
+ "Barbadori": 0.093,
+ "Bischeri": 0.104,
+ "Castellani": 0.055,
+ "Ginori": 0.000,
+ "Guadagni": 0.255,
+ "Lamberteschi": 0.000,
+ "Medici": 0.522,
+ "Pazzi": 0.000,
+ "Peruzzi": 0.022,
+ "Ridolfi": 0.114,
+ "Salviati": 0.143,
+ "Strozzi": 0.103,
+ "Tornabuoni": 0.092,
+ }
+
+ b = nx.betweenness_centrality(G, weight="weight", normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_les_miserables_graph(self):
+ """Weighted betweenness centrality: Les Miserables graph"""
+ G = nx.les_miserables_graph()
+ b_answer = {
+ "Napoleon": 0.000,
+ "Myriel": 0.177,
+ "MlleBaptistine": 0.000,
+ "MmeMagloire": 0.000,
+ "CountessDeLo": 0.000,
+ "Geborand": 0.000,
+ "Champtercier": 0.000,
+ "Cravatte": 0.000,
+ "Count": 0.000,
+ "OldMan": 0.000,
+ "Valjean": 0.454,
+ "Labarre": 0.000,
+ "Marguerite": 0.009,
+ "MmeDeR": 0.000,
+ "Isabeau": 0.000,
+ "Gervais": 0.000,
+ "Listolier": 0.000,
+ "Tholomyes": 0.066,
+ "Fameuil": 0.000,
+ "Blacheville": 0.000,
+ "Favourite": 0.000,
+ "Dahlia": 0.000,
+ "Zephine": 0.000,
+ "Fantine": 0.114,
+ "MmeThenardier": 0.046,
+ "Thenardier": 0.129,
+ "Cosette": 0.075,
+ "Javert": 0.193,
+ "Fauchelevent": 0.026,
+ "Bamatabois": 0.080,
+ "Perpetue": 0.000,
+ "Simplice": 0.001,
+ "Scaufflaire": 0.000,
+ "Woman1": 0.000,
+ "Judge": 0.000,
+ "Champmathieu": 0.000,
+ "Brevet": 0.000,
+ "Chenildieu": 0.000,
+ "Cochepaille": 0.000,
+ "Pontmercy": 0.023,
+ "Boulatruelle": 0.000,
+ "Eponine": 0.023,
+ "Anzelma": 0.000,
+ "Woman2": 0.000,
+ "MotherInnocent": 0.000,
+ "Gribier": 0.000,
+ "MmeBurgon": 0.026,
+ "Jondrette": 0.000,
+ "Gavroche": 0.285,
+ "Gillenormand": 0.024,
+ "Magnon": 0.005,
+ "MlleGillenormand": 0.036,
+ "MmePontmercy": 0.005,
+ "MlleVaubois": 0.000,
+ "LtGillenormand": 0.015,
+ "Marius": 0.072,
+ "BaronessT": 0.004,
+ "Mabeuf": 0.089,
+ "Enjolras": 0.003,
+ "Combeferre": 0.000,
+ "Prouvaire": 0.000,
+ "Feuilly": 0.004,
+ "Courfeyrac": 0.001,
+ "Bahorel": 0.007,
+ "Bossuet": 0.028,
+ "Joly": 0.000,
+ "Grantaire": 0.036,
+ "MotherPlutarch": 0.000,
+ "Gueulemer": 0.025,
+ "Babet": 0.015,
+ "Claquesous": 0.042,
+ "Montparnasse": 0.050,
+ "Toussaint": 0.011,
+ "Child1": 0.000,
+ "Child2": 0.000,
+ "Brujon": 0.002,
+ "MmeHucheloup": 0.034,
+ }
+
+ b = nx.betweenness_centrality(G, weight="weight", normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_ladder_graph(self):
+ """Weighted betweenness centrality: Ladder graph"""
+ G = nx.Graph() # ladder_graph(3)
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
+ b_answer = {0: 1.667, 1: 1.667, 2: 6.667, 3: 6.667, 4: 1.667, 5: 1.667}
+ for b in b_answer:
+ b_answer[b] /= 2
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_G(self):
+ """Weighted betweenness centrality: G"""
+ G = weighted_G()
+ b_answer = {0: 2.0, 1: 0.0, 2: 4.0, 3: 3.0, 4: 4.0, 5: 0.0}
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_G2(self):
+ """Weighted betweenness centrality: G2"""
+ G = nx.DiGraph()
+ G.add_weighted_edges_from(
+ [
+ ("s", "u", 10),
+ ("s", "x", 5),
+ ("u", "v", 1),
+ ("u", "x", 2),
+ ("v", "y", 1),
+ ("x", "u", 3),
+ ("x", "v", 5),
+ ("x", "y", 2),
+ ("y", "s", 7),
+ ("y", "v", 6),
+ ]
+ )
+
+ b_answer = {"y": 5.0, "x": 5.0, "s": 4.0, "u": 2.0, "v": 2.0}
+
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_G3(self):
+ """Weighted betweenness centrality: G3"""
+ G = nx.MultiGraph(weighted_G())
+ es = list(G.edges(data=True))[::2] # duplicate every other edge
+ G.add_edges_from(es)
+ b_answer = {0: 2.0, 1: 0.0, 2: 4.0, 3: 3.0, 4: 4.0, 5: 0.0}
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_G4(self):
+ """Weighted betweenness centrality: G4"""
+ G = nx.MultiDiGraph()
+ G.add_weighted_edges_from(
+ [
+ ("s", "u", 10),
+ ("s", "x", 5),
+ ("s", "x", 6),
+ ("u", "v", 1),
+ ("u", "x", 2),
+ ("v", "y", 1),
+ ("v", "y", 1),
+ ("x", "u", 3),
+ ("x", "v", 5),
+ ("x", "y", 2),
+ ("x", "y", 3),
+ ("y", "s", 7),
+ ("y", "v", 6),
+ ("y", "v", 6),
+ ]
+ )
+
+ b_answer = {"y": 5.0, "x": 5.0, "s": 4.0, "u": 2.0, "v": 2.0}
+
+ b = nx.betweenness_centrality(G, weight="weight", normalized=False)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+class TestEdgeBetweennessCentrality:
+ def test_K5(self):
+ """Edge betweenness centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
+ b_answer = dict.fromkeys(G.edges(), 1)
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_K5(self):
+ """Edge betweenness centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
+ b_answer = dict.fromkeys(G.edges(), 1 / 10)
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_C4(self):
+ """Edge betweenness centrality: C4"""
+ G = nx.cycle_graph(4)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
+ b_answer = {(0, 1): 2, (0, 3): 2, (1, 2): 2, (2, 3): 2}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n] / 6, abs=1e-7)
+
+ def test_P4(self):
+ """Edge betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
+ b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_P4(self):
+ """Edge betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=True)
+ b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n] / 6, abs=1e-7)
+
+ def test_balanced_tree(self):
+ """Edge betweenness centrality: balanced tree"""
+ G = nx.balanced_tree(r=2, h=2)
+ b = nx.edge_betweenness_centrality(G, weight=None, normalized=False)
+ b_answer = {(0, 1): 12, (0, 2): 12, (1, 3): 6, (1, 4): 6, (2, 5): 6, (2, 6): 6}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+class TestWeightedEdgeBetweennessCentrality:
+ def test_K5(self):
+ """Edge betweenness centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = dict.fromkeys(G.edges(), 1)
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_C4(self):
+ """Edge betweenness centrality: C4"""
+ G = nx.cycle_graph(4)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {(0, 1): 2, (0, 3): 2, (1, 2): 2, (2, 3): 2}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4(self):
+ """Edge betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {(0, 1): 3, (1, 2): 4, (2, 3): 3}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_balanced_tree(self):
+ """Edge betweenness centrality: balanced tree"""
+ G = nx.balanced_tree(r=2, h=2)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {(0, 1): 12, (0, 2): 12, (1, 3): 6, (1, 4): 6, (2, 5): 6, (2, 6): 6}
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_weighted_graph(self):
+ """Edge betweenness centrality: weighted"""
+ eList = [
+ (0, 1, 5),
+ (0, 2, 4),
+ (0, 3, 3),
+ (0, 4, 2),
+ (1, 2, 4),
+ (1, 3, 1),
+ (1, 4, 3),
+ (2, 4, 5),
+ (3, 4, 4),
+ ]
+ G = nx.Graph()
+ G.add_weighted_edges_from(eList)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {
+ (0, 1): 0.0,
+ (0, 2): 1.0,
+ (0, 3): 2.0,
+ (0, 4): 1.0,
+ (1, 2): 2.0,
+ (1, 3): 3.5,
+ (1, 4): 1.5,
+ (2, 4): 1.0,
+ (3, 4): 0.5,
+ }
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_weighted_graph(self):
+ """Edge betweenness centrality: normalized weighted"""
+ eList = [
+ (0, 1, 5),
+ (0, 2, 4),
+ (0, 3, 3),
+ (0, 4, 2),
+ (1, 2, 4),
+ (1, 3, 1),
+ (1, 4, 3),
+ (2, 4, 5),
+ (3, 4, 4),
+ ]
+ G = nx.Graph()
+ G.add_weighted_edges_from(eList)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=True)
+ b_answer = {
+ (0, 1): 0.0,
+ (0, 2): 1.0,
+ (0, 3): 2.0,
+ (0, 4): 1.0,
+ (1, 2): 2.0,
+ (1, 3): 3.5,
+ (1, 4): 1.5,
+ (2, 4): 1.0,
+ (3, 4): 0.5,
+ }
+ norm = len(G) * (len(G) - 1) / 2
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n] / norm, abs=1e-7)
+
+ def test_weighted_multigraph(self):
+ """Edge betweenness centrality: weighted multigraph"""
+ eList = [
+ (0, 1, 5),
+ (0, 1, 4),
+ (0, 2, 4),
+ (0, 3, 3),
+ (0, 3, 3),
+ (0, 4, 2),
+ (1, 2, 4),
+ (1, 3, 1),
+ (1, 3, 2),
+ (1, 4, 3),
+ (1, 4, 4),
+ (2, 4, 5),
+ (3, 4, 4),
+ (3, 4, 4),
+ ]
+ G = nx.MultiGraph()
+ G.add_weighted_edges_from(eList)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=False)
+ b_answer = {
+ (0, 1, 0): 0.0,
+ (0, 1, 1): 0.5,
+ (0, 2, 0): 1.0,
+ (0, 3, 0): 0.75,
+ (0, 3, 1): 0.75,
+ (0, 4, 0): 1.0,
+ (1, 2, 0): 2.0,
+ (1, 3, 0): 3.0,
+ (1, 3, 1): 0.0,
+ (1, 4, 0): 1.5,
+ (1, 4, 1): 0.0,
+ (2, 4, 0): 1.0,
+ (3, 4, 0): 0.25,
+ (3, 4, 1): 0.25,
+ }
+ for n in sorted(G.edges(keys=True)):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_weighted_multigraph(self):
+ """Edge betweenness centrality: normalized weighted multigraph"""
+ eList = [
+ (0, 1, 5),
+ (0, 1, 4),
+ (0, 2, 4),
+ (0, 3, 3),
+ (0, 3, 3),
+ (0, 4, 2),
+ (1, 2, 4),
+ (1, 3, 1),
+ (1, 3, 2),
+ (1, 4, 3),
+ (1, 4, 4),
+ (2, 4, 5),
+ (3, 4, 4),
+ (3, 4, 4),
+ ]
+ G = nx.MultiGraph()
+ G.add_weighted_edges_from(eList)
+ b = nx.edge_betweenness_centrality(G, weight="weight", normalized=True)
+ b_answer = {
+ (0, 1, 0): 0.0,
+ (0, 1, 1): 0.5,
+ (0, 2, 0): 1.0,
+ (0, 3, 0): 0.75,
+ (0, 3, 1): 0.75,
+ (0, 4, 0): 1.0,
+ (1, 2, 0): 2.0,
+ (1, 3, 0): 3.0,
+ (1, 3, 1): 0.0,
+ (1, 4, 0): 1.5,
+ (1, 4, 1): 0.0,
+ (2, 4, 0): 1.0,
+ (3, 4, 0): 0.25,
+ (3, 4, 1): 0.25,
+ }
+ norm = len(G) * (len(G) - 1) / 2
+ for n in sorted(G.edges(keys=True)):
+ assert b[n] == pytest.approx(b_answer[n] / norm, abs=1e-7)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py
new file mode 100644
index 00000000..a35a401a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py
@@ -0,0 +1,340 @@
+import pytest
+
+import networkx as nx
+
+
+class TestSubsetBetweennessCentrality:
+ def test_K5(self):
+ """Betweenness Centrality Subset: K5"""
+ G = nx.complete_graph(5)
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[1, 3], weight=None
+ )
+ b_answer = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5_directed(self):
+ """Betweenness Centrality Subset: P5 directed"""
+ G = nx.DiGraph()
+ nx.add_path(G, range(5))
+ b_answer = {0: 0, 1: 1, 2: 1, 3: 0, 4: 0, 5: 0}
+ b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5(self):
+ """Betweenness Centrality Subset: P5"""
+ G = nx.Graph()
+ nx.add_path(G, range(5))
+ b_answer = {0: 0, 1: 0.5, 2: 0.5, 3: 0, 4: 0, 5: 0}
+ b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5_multiple_target(self):
+ """Betweenness Centrality Subset: P5 multiple target"""
+ G = nx.Graph()
+ nx.add_path(G, range(5))
+ b_answer = {0: 0, 1: 1, 2: 1, 3: 0.5, 4: 0, 5: 0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box(self):
+ """Betweenness Centrality Subset: box"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
+ b_answer = {0: 0, 1: 0.25, 2: 0.25, 3: 0}
+ b = nx.betweenness_centrality_subset(G, sources=[0], targets=[3], weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box_and_path(self):
+ """Betweenness Centrality Subset: box and path"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)])
+ b_answer = {0: 0, 1: 0.5, 2: 0.5, 3: 0.5, 4: 0, 5: 0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box_and_path2(self):
+ """Betweenness Centrality Subset: box and path multiple target"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (1, 2), (2, 3), (1, 20), (20, 3), (3, 4)])
+ b_answer = {0: 0, 1: 1.0, 2: 0.5, 20: 0.5, 3: 0.5, 4: 0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_diamond_multi_path(self):
+ """Betweenness Centrality Subset: Diamond Multi Path"""
+ G = nx.Graph()
+ G.add_edges_from(
+ [
+ (1, 2),
+ (1, 3),
+ (1, 4),
+ (1, 5),
+ (1, 10),
+ (10, 11),
+ (11, 12),
+ (12, 9),
+ (2, 6),
+ (3, 6),
+ (4, 6),
+ (5, 7),
+ (7, 8),
+ (6, 8),
+ (8, 9),
+ ]
+ )
+ b = nx.betweenness_centrality_subset(G, sources=[1], targets=[9], weight=None)
+
+ expected_b = {
+ 1: 0,
+ 2: 1.0 / 10,
+ 3: 1.0 / 10,
+ 4: 1.0 / 10,
+ 5: 1.0 / 10,
+ 6: 3.0 / 10,
+ 7: 1.0 / 10,
+ 8: 4.0 / 10,
+ 9: 0,
+ 10: 1.0 / 10,
+ 11: 1.0 / 10,
+ 12: 1.0 / 10,
+ }
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(expected_b[n], abs=1e-7)
+
+ def test_normalized_p2(self):
+ """
+ Betweenness Centrality Subset: Normalized P2
+ if n <= 2: no normalization, betweenness centrality should be 0 for all nodes.
+ """
+ G = nx.Graph()
+ nx.add_path(G, range(2))
+ b_answer = {0: 0, 1: 0.0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[1], normalized=True, weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_P5_directed(self):
+ """Betweenness Centrality Subset: Normalized Directed P5"""
+ G = nx.DiGraph()
+ nx.add_path(G, range(5))
+ b_answer = {0: 0, 1: 1.0 / 12.0, 2: 1.0 / 12.0, 3: 0, 4: 0, 5: 0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[3], normalized=True, weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_weighted_graph(self):
+ """Betweenness Centrality Subset: Weighted Graph"""
+ G = nx.DiGraph()
+ G.add_edge(0, 1, weight=3)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(0, 3, weight=6)
+ G.add_edge(0, 4, weight=4)
+ G.add_edge(1, 3, weight=5)
+ G.add_edge(1, 5, weight=5)
+ G.add_edge(2, 4, weight=1)
+ G.add_edge(3, 4, weight=2)
+ G.add_edge(3, 5, weight=1)
+ G.add_edge(4, 5, weight=4)
+ b_answer = {0: 0.0, 1: 0.0, 2: 0.5, 3: 0.5, 4: 0.5, 5: 0.0}
+ b = nx.betweenness_centrality_subset(
+ G, sources=[0], targets=[5], normalized=False, weight="weight"
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+class TestEdgeSubsetBetweennessCentrality:
+ def test_K5(self):
+ """Edge betweenness subset centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[1, 3], weight=None
+ )
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 3)] = b_answer[(0, 1)] = 0.5
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5_directed(self):
+ """Edge betweenness subset centrality: P5 directed"""
+ G = nx.DiGraph()
+ nx.add_path(G, range(5))
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 1
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5(self):
+ """Edge betweenness subset centrality: P5"""
+ G = nx.Graph()
+ nx.add_path(G, range(5))
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 0.5
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P5_multiple_target(self):
+ """Edge betweenness subset centrality: P5 multiple target"""
+ G = nx.Graph()
+ nx.add_path(G, range(5))
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 1
+ b_answer[(3, 4)] = 0.5
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box(self):
+ """Edge betweenness subset centrality: box"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(0, 2)] = 0.25
+ b_answer[(1, 3)] = b_answer[(2, 3)] = 0.25
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box_and_path(self):
+ """Edge betweenness subset centrality: box and path"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (4, 5)])
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(0, 2)] = 0.5
+ b_answer[(1, 3)] = b_answer[(2, 3)] = 0.5
+ b_answer[(3, 4)] = 0.5
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_box_and_path2(self):
+ """Edge betweenness subset centrality: box and path multiple target"""
+ G = nx.Graph()
+ G.add_edges_from([(0, 1), (1, 2), (2, 3), (1, 20), (20, 3), (3, 4)])
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = 1.0
+ b_answer[(1, 20)] = b_answer[(3, 20)] = 0.5
+ b_answer[(1, 2)] = b_answer[(2, 3)] = 0.5
+ b_answer[(3, 4)] = 0.5
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3, 4], weight=None
+ )
+ for n in sorted(G.edges()):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_diamond_multi_path(self):
+ """Edge betweenness subset centrality: Diamond Multi Path"""
+ G = nx.Graph()
+ G.add_edges_from(
+ [
+ (1, 2),
+ (1, 3),
+ (1, 4),
+ (1, 5),
+ (1, 10),
+ (10, 11),
+ (11, 12),
+ (12, 9),
+ (2, 6),
+ (3, 6),
+ (4, 6),
+ (5, 7),
+ (7, 8),
+ (6, 8),
+ (8, 9),
+ ]
+ )
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(8, 9)] = 0.4
+ b_answer[(6, 8)] = b_answer[(7, 8)] = 0.2
+ b_answer[(2, 6)] = b_answer[(3, 6)] = b_answer[(4, 6)] = 0.2 / 3.0
+ b_answer[(1, 2)] = b_answer[(1, 3)] = b_answer[(1, 4)] = 0.2 / 3.0
+ b_answer[(5, 7)] = 0.2
+ b_answer[(1, 5)] = 0.2
+ b_answer[(9, 12)] = 0.1
+ b_answer[(11, 12)] = b_answer[(10, 11)] = b_answer[(1, 10)] = 0.1
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[1], targets=[9], weight=None
+ )
+ for n in G.edges():
+ sort_n = tuple(sorted(n))
+ assert b[n] == pytest.approx(b_answer[sort_n], abs=1e-7)
+
+ def test_normalized_p1(self):
+ """
+ Edge betweenness subset centrality: P1
+ if n <= 1: no normalization b=0 for all nodes
+ """
+ G = nx.Graph()
+ nx.add_path(G, range(1))
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[0], normalized=True, weight=None
+ )
+ for n in G.edges():
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_normalized_P5_directed(self):
+ """Edge betweenness subset centrality: Normalized Directed P5"""
+ G = nx.DiGraph()
+ nx.add_path(G, range(5))
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 1)] = b_answer[(1, 2)] = b_answer[(2, 3)] = 0.05
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[3], normalized=True, weight=None
+ )
+ for n in G.edges():
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_weighted_graph(self):
+ """Edge betweenness subset centrality: Weighted Graph"""
+ G = nx.DiGraph()
+ G.add_edge(0, 1, weight=3)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(0, 3, weight=6)
+ G.add_edge(0, 4, weight=4)
+ G.add_edge(1, 3, weight=5)
+ G.add_edge(1, 5, weight=5)
+ G.add_edge(2, 4, weight=1)
+ G.add_edge(3, 4, weight=2)
+ G.add_edge(3, 5, weight=1)
+ G.add_edge(4, 5, weight=4)
+ b_answer = dict.fromkeys(G.edges(), 0)
+ b_answer[(0, 2)] = b_answer[(2, 4)] = b_answer[(4, 5)] = 0.5
+ b_answer[(0, 3)] = b_answer[(3, 5)] = 0.5
+ b = nx.edge_betweenness_centrality_subset(
+ G, sources=[0], targets=[5], normalized=False, weight="weight"
+ )
+ for n in G.edges():
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py
new file mode 100644
index 00000000..7bdb7e7c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_closeness_centrality.py
@@ -0,0 +1,307 @@
+"""
+Tests for closeness centrality.
+"""
+
+import pytest
+
+import networkx as nx
+
+
+class TestClosenessCentrality:
+ @classmethod
+ def setup_class(cls):
+ cls.K = nx.krackhardt_kite_graph()
+ cls.P3 = nx.path_graph(3)
+ cls.P4 = nx.path_graph(4)
+ cls.K5 = nx.complete_graph(5)
+
+ cls.C4 = nx.cycle_graph(4)
+ cls.T = nx.balanced_tree(r=2, h=2)
+ cls.Gb = nx.Graph()
+ cls.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
+
+ F = nx.florentine_families_graph()
+ cls.F = F
+
+ cls.LM = nx.les_miserables_graph()
+
+ # Create random undirected, unweighted graph for testing incremental version
+ cls.undirected_G = nx.fast_gnp_random_graph(n=100, p=0.6, seed=123)
+ cls.undirected_G_cc = nx.closeness_centrality(cls.undirected_G)
+
+ def test_wf_improved(self):
+ G = nx.union(self.P4, nx.path_graph([4, 5, 6]))
+ c = nx.closeness_centrality(G)
+ cwf = nx.closeness_centrality(G, wf_improved=False)
+ res = {0: 0.25, 1: 0.375, 2: 0.375, 3: 0.25, 4: 0.222, 5: 0.333, 6: 0.222}
+ wf_res = {0: 0.5, 1: 0.75, 2: 0.75, 3: 0.5, 4: 0.667, 5: 1.0, 6: 0.667}
+ for n in G:
+ assert c[n] == pytest.approx(res[n], abs=1e-3)
+ assert cwf[n] == pytest.approx(wf_res[n], abs=1e-3)
+
+ def test_digraph(self):
+ G = nx.path_graph(3, create_using=nx.DiGraph())
+ c = nx.closeness_centrality(G)
+ cr = nx.closeness_centrality(G.reverse())
+ d = {0: 0.0, 1: 0.500, 2: 0.667}
+ dr = {0: 0.667, 1: 0.500, 2: 0.0}
+ for n in sorted(self.P3):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+ assert cr[n] == pytest.approx(dr[n], abs=1e-3)
+
+ def test_k5_closeness(self):
+ c = nx.closeness_centrality(self.K5)
+ d = {0: 1.000, 1: 1.000, 2: 1.000, 3: 1.000, 4: 1.000}
+ for n in sorted(self.K5):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_p3_closeness(self):
+ c = nx.closeness_centrality(self.P3)
+ d = {0: 0.667, 1: 1.000, 2: 0.667}
+ for n in sorted(self.P3):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_krackhardt_closeness(self):
+ c = nx.closeness_centrality(self.K)
+ d = {
+ 0: 0.529,
+ 1: 0.529,
+ 2: 0.500,
+ 3: 0.600,
+ 4: 0.500,
+ 5: 0.643,
+ 6: 0.643,
+ 7: 0.600,
+ 8: 0.429,
+ 9: 0.310,
+ }
+ for n in sorted(self.K):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_florentine_families_closeness(self):
+ c = nx.closeness_centrality(self.F)
+ d = {
+ "Acciaiuoli": 0.368,
+ "Albizzi": 0.483,
+ "Barbadori": 0.4375,
+ "Bischeri": 0.400,
+ "Castellani": 0.389,
+ "Ginori": 0.333,
+ "Guadagni": 0.467,
+ "Lamberteschi": 0.326,
+ "Medici": 0.560,
+ "Pazzi": 0.286,
+ "Peruzzi": 0.368,
+ "Ridolfi": 0.500,
+ "Salviati": 0.389,
+ "Strozzi": 0.4375,
+ "Tornabuoni": 0.483,
+ }
+ for n in sorted(self.F):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_les_miserables_closeness(self):
+ c = nx.closeness_centrality(self.LM)
+ d = {
+ "Napoleon": 0.302,
+ "Myriel": 0.429,
+ "MlleBaptistine": 0.413,
+ "MmeMagloire": 0.413,
+ "CountessDeLo": 0.302,
+ "Geborand": 0.302,
+ "Champtercier": 0.302,
+ "Cravatte": 0.302,
+ "Count": 0.302,
+ "OldMan": 0.302,
+ "Valjean": 0.644,
+ "Labarre": 0.394,
+ "Marguerite": 0.413,
+ "MmeDeR": 0.394,
+ "Isabeau": 0.394,
+ "Gervais": 0.394,
+ "Listolier": 0.341,
+ "Tholomyes": 0.392,
+ "Fameuil": 0.341,
+ "Blacheville": 0.341,
+ "Favourite": 0.341,
+ "Dahlia": 0.341,
+ "Zephine": 0.341,
+ "Fantine": 0.461,
+ "MmeThenardier": 0.461,
+ "Thenardier": 0.517,
+ "Cosette": 0.478,
+ "Javert": 0.517,
+ "Fauchelevent": 0.402,
+ "Bamatabois": 0.427,
+ "Perpetue": 0.318,
+ "Simplice": 0.418,
+ "Scaufflaire": 0.394,
+ "Woman1": 0.396,
+ "Judge": 0.404,
+ "Champmathieu": 0.404,
+ "Brevet": 0.404,
+ "Chenildieu": 0.404,
+ "Cochepaille": 0.404,
+ "Pontmercy": 0.373,
+ "Boulatruelle": 0.342,
+ "Eponine": 0.396,
+ "Anzelma": 0.352,
+ "Woman2": 0.402,
+ "MotherInnocent": 0.398,
+ "Gribier": 0.288,
+ "MmeBurgon": 0.344,
+ "Jondrette": 0.257,
+ "Gavroche": 0.514,
+ "Gillenormand": 0.442,
+ "Magnon": 0.335,
+ "MlleGillenormand": 0.442,
+ "MmePontmercy": 0.315,
+ "MlleVaubois": 0.308,
+ "LtGillenormand": 0.365,
+ "Marius": 0.531,
+ "BaronessT": 0.352,
+ "Mabeuf": 0.396,
+ "Enjolras": 0.481,
+ "Combeferre": 0.392,
+ "Prouvaire": 0.357,
+ "Feuilly": 0.392,
+ "Courfeyrac": 0.400,
+ "Bahorel": 0.394,
+ "Bossuet": 0.475,
+ "Joly": 0.394,
+ "Grantaire": 0.358,
+ "MotherPlutarch": 0.285,
+ "Gueulemer": 0.463,
+ "Babet": 0.463,
+ "Claquesous": 0.452,
+ "Montparnasse": 0.458,
+ "Toussaint": 0.402,
+ "Child1": 0.342,
+ "Child2": 0.342,
+ "Brujon": 0.380,
+ "MmeHucheloup": 0.353,
+ }
+ for n in sorted(self.LM):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_weighted_closeness(self):
+ edges = [
+ ("s", "u", 10),
+ ("s", "x", 5),
+ ("u", "v", 1),
+ ("u", "x", 2),
+ ("v", "y", 1),
+ ("x", "u", 3),
+ ("x", "v", 5),
+ ("x", "y", 2),
+ ("y", "s", 7),
+ ("y", "v", 6),
+ ]
+ XG = nx.Graph()
+ XG.add_weighted_edges_from(edges)
+ c = nx.closeness_centrality(XG, distance="weight")
+ d = {"y": 0.200, "x": 0.286, "s": 0.138, "u": 0.235, "v": 0.200}
+ for n in sorted(XG):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ #
+ # Tests for incremental closeness centrality.
+ #
+ @staticmethod
+ def pick_add_edge(g):
+ u = nx.utils.arbitrary_element(g)
+ possible_nodes = set(g.nodes())
+ neighbors = list(g.neighbors(u)) + [u]
+ possible_nodes.difference_update(neighbors)
+ v = nx.utils.arbitrary_element(possible_nodes)
+ return (u, v)
+
+ @staticmethod
+ def pick_remove_edge(g):
+ u = nx.utils.arbitrary_element(g)
+ possible_nodes = list(g.neighbors(u))
+ v = nx.utils.arbitrary_element(possible_nodes)
+ return (u, v)
+
+ def test_directed_raises(self):
+ with pytest.raises(nx.NetworkXNotImplemented):
+ dir_G = nx.gn_graph(n=5)
+ prev_cc = None
+ edge = self.pick_add_edge(dir_G)
+ insert = True
+ nx.incremental_closeness_centrality(dir_G, edge, prev_cc, insert)
+
+ def test_wrong_size_prev_cc_raises(self):
+ with pytest.raises(nx.NetworkXError):
+ G = self.undirected_G.copy()
+ edge = self.pick_add_edge(G)
+ insert = True
+ prev_cc = self.undirected_G_cc.copy()
+ prev_cc.pop(0)
+ nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
+
+ def test_wrong_nodes_prev_cc_raises(self):
+ with pytest.raises(nx.NetworkXError):
+ G = self.undirected_G.copy()
+ edge = self.pick_add_edge(G)
+ insert = True
+ prev_cc = self.undirected_G_cc.copy()
+ num_nodes = len(prev_cc)
+ prev_cc.pop(0)
+ prev_cc[num_nodes] = 0.5
+ nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
+
+ def test_zero_centrality(self):
+ G = nx.path_graph(3)
+ prev_cc = nx.closeness_centrality(G)
+ edge = self.pick_remove_edge(G)
+ test_cc = nx.incremental_closeness_centrality(G, edge, prev_cc, insertion=False)
+ G.remove_edges_from([edge])
+ real_cc = nx.closeness_centrality(G)
+ shared_items = set(test_cc.items()) & set(real_cc.items())
+ assert len(shared_items) == len(real_cc)
+ assert 0 in test_cc.values()
+
+ def test_incremental(self):
+ # Check that incremental and regular give same output
+ G = self.undirected_G.copy()
+ prev_cc = None
+ for i in range(5):
+ if i % 2 == 0:
+ # Remove an edge
+ insert = False
+ edge = self.pick_remove_edge(G)
+ else:
+ # Add an edge
+ insert = True
+ edge = self.pick_add_edge(G)
+
+ # start = timeit.default_timer()
+ test_cc = nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
+ # inc_elapsed = (timeit.default_timer() - start)
+ # print(f"incremental time: {inc_elapsed}")
+
+ if insert:
+ G.add_edges_from([edge])
+ else:
+ G.remove_edges_from([edge])
+
+ # start = timeit.default_timer()
+ real_cc = nx.closeness_centrality(G)
+ # reg_elapsed = (timeit.default_timer() - start)
+ # print(f"regular time: {reg_elapsed}")
+ # Example output:
+ # incremental time: 0.208
+ # regular time: 0.276
+ # incremental time: 0.00683
+ # regular time: 0.260
+ # incremental time: 0.0224
+ # regular time: 0.278
+ # incremental time: 0.00804
+ # regular time: 0.208
+ # incremental time: 0.00947
+ # regular time: 0.188
+
+ assert set(test_cc.items()) == set(real_cc.items())
+
+ prev_cc = test_cc
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py
new file mode 100644
index 00000000..4e3d4385
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py
@@ -0,0 +1,197 @@
+import pytest
+
+import networkx as nx
+from networkx import approximate_current_flow_betweenness_centrality as approximate_cfbc
+from networkx import edge_current_flow_betweenness_centrality as edge_current_flow
+
+np = pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+
+class TestFlowBetweennessCentrality:
+ def test_K4_normalized(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ b_answer = {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ G.add_edge(0, 1, weight=0.5, other=0.3)
+ b = nx.current_flow_betweenness_centrality(G, normalized=True, weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ wb_answer = {0: 0.2222222, 1: 0.2222222, 2: 0.30555555, 3: 0.30555555}
+ b = nx.current_flow_betweenness_centrality(G, normalized=True, weight="weight")
+ for n in sorted(G):
+ assert b[n] == pytest.approx(wb_answer[n], abs=1e-7)
+ wb_answer = {0: 0.2051282, 1: 0.2051282, 2: 0.33974358, 3: 0.33974358}
+ b = nx.current_flow_betweenness_centrality(G, normalized=True, weight="other")
+ for n in sorted(G):
+ assert b[n] == pytest.approx(wb_answer[n], abs=1e-7)
+
+ def test_K4(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ for solver in ["full", "lu", "cg"]:
+ b = nx.current_flow_betweenness_centrality(
+ G, normalized=False, solver=solver
+ )
+ b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4_normalized(self):
+ """Betweenness centrality: P4 normalized"""
+ G = nx.path_graph(4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ b_answer = {0: 0, 1: 2.0 / 3, 2: 2.0 / 3, 3: 0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4(self):
+ """Betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=False)
+ b_answer = {0: 0, 1: 2, 2: 2, 3: 0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_star(self):
+ """Betweenness centrality: star"""
+ G = nx.Graph()
+ nx.add_star(G, ["a", "b", "c", "d"])
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ b_answer = {"a": 1.0, "b": 0.0, "c": 0.0, "d": 0.0}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_solvers2(self):
+ """Betweenness centrality: alternate solvers"""
+ G = nx.complete_graph(4)
+ for solver in ["full", "lu", "cg"]:
+ b = nx.current_flow_betweenness_centrality(
+ G, normalized=False, solver=solver
+ )
+ b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+class TestApproximateFlowBetweennessCentrality:
+ def test_K4_normalized(self):
+ "Approximate current-flow betweenness centrality: K4 normalized"
+ G = nx.complete_graph(4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ epsilon = 0.1
+ ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
+
+ def test_K4(self):
+ "Approximate current-flow betweenness centrality: K4"
+ G = nx.complete_graph(4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=False)
+ epsilon = 0.1
+ ba = approximate_cfbc(G, normalized=False, epsilon=0.5 * epsilon)
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], ba[n], atol=epsilon * len(G) ** 2)
+
+ def test_star(self):
+ "Approximate current-flow betweenness centrality: star"
+ G = nx.Graph()
+ nx.add_star(G, ["a", "b", "c", "d"])
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ epsilon = 0.1
+ ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
+
+ def test_grid(self):
+ "Approximate current-flow betweenness centrality: 2d grid"
+ G = nx.grid_2d_graph(4, 4)
+ b = nx.current_flow_betweenness_centrality(G, normalized=True)
+ epsilon = 0.1
+ ba = approximate_cfbc(G, normalized=True, epsilon=0.5 * epsilon)
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], ba[n], atol=epsilon)
+
+ def test_seed(self):
+ G = nx.complete_graph(4)
+ b = approximate_cfbc(G, normalized=False, epsilon=0.05, seed=1)
+ b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], b_answer[n], atol=0.1)
+
+ def test_solvers(self):
+ "Approximate current-flow betweenness centrality: solvers"
+ G = nx.complete_graph(4)
+ epsilon = 0.1
+ for solver in ["full", "lu", "cg"]:
+ b = approximate_cfbc(
+ G, normalized=False, solver=solver, epsilon=0.5 * epsilon
+ )
+ b_answer = {0: 0.75, 1: 0.75, 2: 0.75, 3: 0.75}
+ for n in sorted(G):
+ np.testing.assert_allclose(b[n], b_answer[n], atol=epsilon)
+
+ def test_lower_kmax(self):
+ G = nx.complete_graph(4)
+ with pytest.raises(nx.NetworkXError, match="Increase kmax or epsilon"):
+ nx.approximate_current_flow_betweenness_centrality(G, kmax=4)
+
+
+class TestWeightedFlowBetweennessCentrality:
+ pass
+
+
+class TestEdgeFlowBetweennessCentrality:
+ def test_K4(self):
+ """Edge flow betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = edge_current_flow(G, normalized=True)
+ b_answer = dict.fromkeys(G.edges(), 0.25)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_K4_normalized(self):
+ """Edge flow betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = edge_current_flow(G, normalized=False)
+ b_answer = dict.fromkeys(G.edges(), 0.75)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_C4(self):
+ """Edge flow betweenness centrality: C4"""
+ G = nx.cycle_graph(4)
+ b = edge_current_flow(G, normalized=False)
+ b_answer = {(0, 1): 1.25, (0, 3): 1.25, (1, 2): 1.25, (2, 3): 1.25}
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_P4(self):
+ """Edge betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = edge_current_flow(G, normalized=False)
+ b_answer = {(0, 1): 1.5, (1, 2): 2.0, (2, 3): 1.5}
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+
+@pytest.mark.parametrize(
+ "centrality_func",
+ (
+ nx.current_flow_betweenness_centrality,
+ nx.edge_current_flow_betweenness_centrality,
+ nx.approximate_current_flow_betweenness_centrality,
+ ),
+)
+def test_unconnected_graphs_betweenness_centrality(centrality_func):
+ G = nx.Graph([(1, 2), (3, 4)])
+ G.add_node(5)
+ with pytest.raises(nx.NetworkXError, match="Graph not connected"):
+ centrality_func(G)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py
new file mode 100644
index 00000000..7b1611b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py
@@ -0,0 +1,147 @@
+import pytest
+
+pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+import networkx as nx
+from networkx import edge_current_flow_betweenness_centrality as edge_current_flow
+from networkx import (
+ edge_current_flow_betweenness_centrality_subset as edge_current_flow_subset,
+)
+
+
+class TestFlowBetweennessCentrality:
+ def test_K4_normalized(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_K4(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ # test weighted network
+ G.add_edge(0, 1, weight=0.5, other=0.3)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True, weight=None
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True, weight="other"
+ )
+ b_answer = nx.current_flow_betweenness_centrality(
+ G, normalized=True, weight="other"
+ )
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4_normalized(self):
+ """Betweenness centrality: P4 normalized"""
+ G = nx.path_graph(4)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4(self):
+ """Betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_star(self):
+ """Betweenness centrality: star"""
+ G = nx.Graph()
+ nx.add_star(G, ["a", "b", "c", "d"])
+ b = nx.current_flow_betweenness_centrality_subset(
+ G, list(G), list(G), normalized=True
+ )
+ b_answer = nx.current_flow_betweenness_centrality(G, normalized=True)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+
+# class TestWeightedFlowBetweennessCentrality():
+# pass
+
+
+class TestEdgeFlowBetweennessCentrality:
+ def test_K4_normalized(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
+ b_answer = edge_current_flow(G, normalized=True)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_K4(self):
+ """Betweenness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=False)
+ b_answer = edge_current_flow(G, normalized=False)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+ # test weighted network
+ G.add_edge(0, 1, weight=0.5, other=0.3)
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=False, weight=None)
+ # weight is None => same as unweighted network
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=False)
+ b_answer = edge_current_flow(G, normalized=False)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ b = edge_current_flow_subset(
+ G, list(G), list(G), normalized=False, weight="other"
+ )
+ b_answer = edge_current_flow(G, normalized=False, weight="other")
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_C4(self):
+ """Edge betweenness centrality: C4"""
+ G = nx.cycle_graph(4)
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
+ b_answer = edge_current_flow(G, normalized=True)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
+
+ def test_P4(self):
+ """Edge betweenness centrality: P4"""
+ G = nx.path_graph(4)
+ b = edge_current_flow_subset(G, list(G), list(G), normalized=True)
+ b_answer = edge_current_flow(G, normalized=True)
+ for (s, t), v1 in b_answer.items():
+ v2 = b.get((s, t), b.get((t, s)))
+ assert v1 == pytest.approx(v2, abs=1e-7)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_closeness.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_closeness.py
new file mode 100644
index 00000000..2528d622
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_current_flow_closeness.py
@@ -0,0 +1,43 @@
+import pytest
+
+pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+import networkx as nx
+
+
+class TestFlowClosenessCentrality:
+ def test_K4(self):
+ """Closeness centrality: K4"""
+ G = nx.complete_graph(4)
+ b = nx.current_flow_closeness_centrality(G)
+ b_answer = {0: 2.0 / 3, 1: 2.0 / 3, 2: 2.0 / 3, 3: 2.0 / 3}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P4(self):
+ """Closeness centrality: P4"""
+ G = nx.path_graph(4)
+ b = nx.current_flow_closeness_centrality(G)
+ b_answer = {0: 1.0 / 6, 1: 1.0 / 4, 2: 1.0 / 4, 3: 1.0 / 6}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_star(self):
+ """Closeness centrality: star"""
+ G = nx.Graph()
+ nx.add_star(G, ["a", "b", "c", "d"])
+ b = nx.current_flow_closeness_centrality(G)
+ b_answer = {"a": 1.0 / 3, "b": 0.6 / 3, "c": 0.6 / 3, "d": 0.6 / 3}
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_current_flow_closeness_centrality_not_connected(self):
+ G = nx.Graph()
+ G.add_nodes_from([1, 2, 3])
+ with pytest.raises(nx.NetworkXError):
+ nx.current_flow_closeness_centrality(G)
+
+
+class TestWeightedFlowClosenessCentrality:
+ pass
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py
new file mode 100644
index 00000000..e39aa3b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_degree_centrality.py
@@ -0,0 +1,144 @@
+"""
+Unit tests for degree centrality.
+"""
+
+import pytest
+
+import networkx as nx
+
+
+class TestDegreeCentrality:
+ def setup_method(self):
+ self.K = nx.krackhardt_kite_graph()
+ self.P3 = nx.path_graph(3)
+ self.K5 = nx.complete_graph(5)
+
+ F = nx.Graph() # Florentine families
+ F.add_edge("Acciaiuoli", "Medici")
+ F.add_edge("Castellani", "Peruzzi")
+ F.add_edge("Castellani", "Strozzi")
+ F.add_edge("Castellani", "Barbadori")
+ F.add_edge("Medici", "Barbadori")
+ F.add_edge("Medici", "Ridolfi")
+ F.add_edge("Medici", "Tornabuoni")
+ F.add_edge("Medici", "Albizzi")
+ F.add_edge("Medici", "Salviati")
+ F.add_edge("Salviati", "Pazzi")
+ F.add_edge("Peruzzi", "Strozzi")
+ F.add_edge("Peruzzi", "Bischeri")
+ F.add_edge("Strozzi", "Ridolfi")
+ F.add_edge("Strozzi", "Bischeri")
+ F.add_edge("Ridolfi", "Tornabuoni")
+ F.add_edge("Tornabuoni", "Guadagni")
+ F.add_edge("Albizzi", "Ginori")
+ F.add_edge("Albizzi", "Guadagni")
+ F.add_edge("Bischeri", "Guadagni")
+ F.add_edge("Guadagni", "Lamberteschi")
+ self.F = F
+
+ G = nx.DiGraph()
+ G.add_edge(0, 5)
+ G.add_edge(1, 5)
+ G.add_edge(2, 5)
+ G.add_edge(3, 5)
+ G.add_edge(4, 5)
+ G.add_edge(5, 6)
+ G.add_edge(5, 7)
+ G.add_edge(5, 8)
+ self.G = G
+
+ def test_degree_centrality_1(self):
+ d = nx.degree_centrality(self.K5)
+ exact = dict(zip(range(5), [1] * 5))
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ def test_degree_centrality_2(self):
+ d = nx.degree_centrality(self.P3)
+ exact = {0: 0.5, 1: 1, 2: 0.5}
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ def test_degree_centrality_3(self):
+ d = nx.degree_centrality(self.K)
+ exact = {
+ 0: 0.444,
+ 1: 0.444,
+ 2: 0.333,
+ 3: 0.667,
+ 4: 0.333,
+ 5: 0.556,
+ 6: 0.556,
+ 7: 0.333,
+ 8: 0.222,
+ 9: 0.111,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(float(f"{dc:.3f}"), abs=1e-7)
+
+ def test_degree_centrality_4(self):
+ d = nx.degree_centrality(self.F)
+ names = sorted(self.F.nodes())
+ dcs = [
+ 0.071,
+ 0.214,
+ 0.143,
+ 0.214,
+ 0.214,
+ 0.071,
+ 0.286,
+ 0.071,
+ 0.429,
+ 0.071,
+ 0.214,
+ 0.214,
+ 0.143,
+ 0.286,
+ 0.214,
+ ]
+ exact = dict(zip(names, dcs))
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(float(f"{dc:.3f}"), abs=1e-7)
+
+ def test_indegree_centrality(self):
+ d = nx.in_degree_centrality(self.G)
+ exact = {
+ 0: 0.0,
+ 1: 0.0,
+ 2: 0.0,
+ 3: 0.0,
+ 4: 0.0,
+ 5: 0.625,
+ 6: 0.125,
+ 7: 0.125,
+ 8: 0.125,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ def test_outdegree_centrality(self):
+ d = nx.out_degree_centrality(self.G)
+ exact = {
+ 0: 0.125,
+ 1: 0.125,
+ 2: 0.125,
+ 3: 0.125,
+ 4: 0.125,
+ 5: 0.375,
+ 6: 0.0,
+ 7: 0.0,
+ 8: 0.0,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ def test_small_graph_centrality(self):
+ G = nx.empty_graph(create_using=nx.DiGraph)
+ assert {} == nx.degree_centrality(G)
+ assert {} == nx.out_degree_centrality(G)
+ assert {} == nx.in_degree_centrality(G)
+
+ G = nx.empty_graph(1, create_using=nx.DiGraph)
+ assert {0: 1} == nx.degree_centrality(G)
+ assert {0: 1} == nx.out_degree_centrality(G)
+ assert {0: 1} == nx.in_degree_centrality(G)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py
new file mode 100644
index 00000000..05de1c43
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_dispersion.py
@@ -0,0 +1,73 @@
+import networkx as nx
+
+
+def small_ego_G():
+ """The sample network from https://arxiv.org/pdf/1310.6753v1.pdf"""
+ edges = [
+ ("a", "b"),
+ ("a", "c"),
+ ("b", "c"),
+ ("b", "d"),
+ ("b", "e"),
+ ("b", "f"),
+ ("c", "d"),
+ ("c", "f"),
+ ("c", "h"),
+ ("d", "f"),
+ ("e", "f"),
+ ("f", "h"),
+ ("h", "j"),
+ ("h", "k"),
+ ("i", "j"),
+ ("i", "k"),
+ ("j", "k"),
+ ("u", "a"),
+ ("u", "b"),
+ ("u", "c"),
+ ("u", "d"),
+ ("u", "e"),
+ ("u", "f"),
+ ("u", "g"),
+ ("u", "h"),
+ ("u", "i"),
+ ("u", "j"),
+ ("u", "k"),
+ ]
+ G = nx.Graph()
+ G.add_edges_from(edges)
+
+ return G
+
+
+class TestDispersion:
+ def test_article(self):
+ """our algorithm matches article's"""
+ G = small_ego_G()
+ disp_uh = nx.dispersion(G, "u", "h", normalized=False)
+ disp_ub = nx.dispersion(G, "u", "b", normalized=False)
+ assert disp_uh == 4
+ assert disp_ub == 1
+
+ def test_results_length(self):
+ """there is a result for every node"""
+ G = small_ego_G()
+ disp = nx.dispersion(G)
+ disp_Gu = nx.dispersion(G, "u")
+ disp_uv = nx.dispersion(G, "u", "h")
+ assert len(disp) == len(G)
+ assert len(disp_Gu) == len(G) - 1
+ assert isinstance(disp_uv, float)
+
+ def test_dispersion_v_only(self):
+ G = small_ego_G()
+ disp_G_h = nx.dispersion(G, v="h", normalized=False)
+ disp_G_h_normalized = nx.dispersion(G, v="h", normalized=True)
+ assert disp_G_h == {"c": 0, "f": 0, "j": 0, "k": 0, "u": 4}
+ assert disp_G_h_normalized == {"c": 0.0, "f": 0.0, "j": 0.0, "k": 0.0, "u": 1.0}
+
+ def test_impossible_things(self):
+ G = nx.karate_club_graph()
+ disp = nx.dispersion(G)
+ for u in disp:
+ for v in disp[u]:
+ assert disp[u][v] >= 0
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py
new file mode 100644
index 00000000..cfc9ee79
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py
@@ -0,0 +1,187 @@
+import math
+
+import pytest
+
+np = pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+
+import networkx as nx
+
+
+class TestEigenvectorCentrality:
+ def test_K5(self):
+ """Eigenvector centrality: K5"""
+ G = nx.complete_graph(5)
+ b = nx.eigenvector_centrality(G)
+ v = math.sqrt(1 / 5.0)
+ b_answer = dict.fromkeys(G, v)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ nstart = {n: 1 for n in G}
+ b = nx.eigenvector_centrality(G, nstart=nstart)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ b = nx.eigenvector_centrality_numpy(G)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_P3(self):
+ """Eigenvector centrality: P3"""
+ G = nx.path_graph(3)
+ b_answer = {0: 0.5, 1: 0.7071, 2: 0.5}
+ b = nx.eigenvector_centrality_numpy(G)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+ b = nx.eigenvector_centrality(G)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_P3_unweighted(self):
+ """Eigenvector centrality: P3"""
+ G = nx.path_graph(3)
+ b_answer = {0: 0.5, 1: 0.7071, 2: 0.5}
+ b = nx.eigenvector_centrality_numpy(G, weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_maxiter(self):
+ with pytest.raises(nx.PowerIterationFailedConvergence):
+ G = nx.path_graph(3)
+ nx.eigenvector_centrality(G, max_iter=0)
+
+
+class TestEigenvectorCentralityDirected:
+ @classmethod
+ def setup_class(cls):
+ G = nx.DiGraph()
+
+ edges = [
+ (1, 2),
+ (1, 3),
+ (2, 4),
+ (3, 2),
+ (3, 5),
+ (4, 2),
+ (4, 5),
+ (4, 6),
+ (5, 6),
+ (5, 7),
+ (5, 8),
+ (6, 8),
+ (7, 1),
+ (7, 5),
+ (7, 8),
+ (8, 6),
+ (8, 7),
+ ]
+
+ G.add_edges_from(edges, weight=2.0)
+ cls.G = G.reverse()
+ cls.G.evc = [
+ 0.25368793,
+ 0.19576478,
+ 0.32817092,
+ 0.40430835,
+ 0.48199885,
+ 0.15724483,
+ 0.51346196,
+ 0.32475403,
+ ]
+
+ H = nx.DiGraph()
+
+ edges = [
+ (1, 2),
+ (1, 3),
+ (2, 4),
+ (3, 2),
+ (3, 5),
+ (4, 2),
+ (4, 5),
+ (4, 6),
+ (5, 6),
+ (5, 7),
+ (5, 8),
+ (6, 8),
+ (7, 1),
+ (7, 5),
+ (7, 8),
+ (8, 6),
+ (8, 7),
+ ]
+
+ G.add_edges_from(edges)
+ cls.H = G.reverse()
+ cls.H.evc = [
+ 0.25368793,
+ 0.19576478,
+ 0.32817092,
+ 0.40430835,
+ 0.48199885,
+ 0.15724483,
+ 0.51346196,
+ 0.32475403,
+ ]
+
+ def test_eigenvector_centrality_weighted(self):
+ G = self.G
+ p = nx.eigenvector_centrality(G)
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-4)
+
+ def test_eigenvector_centrality_weighted_numpy(self):
+ G = self.G
+ p = nx.eigenvector_centrality_numpy(G)
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+ def test_eigenvector_centrality_unweighted(self):
+ G = self.H
+ p = nx.eigenvector_centrality(G)
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-4)
+
+ def test_eigenvector_centrality_unweighted_numpy(self):
+ G = self.H
+ p = nx.eigenvector_centrality_numpy(G)
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+
+class TestEigenvectorCentralityExceptions:
+ def test_multigraph(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.eigenvector_centrality(nx.MultiGraph())
+
+ def test_multigraph_numpy(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.eigenvector_centrality_numpy(nx.MultiGraph())
+
+ def test_null(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.eigenvector_centrality(nx.Graph())
+
+ def test_null_numpy(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.eigenvector_centrality_numpy(nx.Graph())
+
+ @pytest.mark.parametrize(
+ "G",
+ [
+ nx.empty_graph(3),
+ nx.DiGraph([(0, 1), (1, 2)]),
+ ],
+ )
+ def test_disconnected_numpy(self, G):
+ msg = "does not give consistent results for disconnected"
+ with pytest.raises(nx.AmbiguousSolution, match=msg):
+ nx.eigenvector_centrality_numpy(G)
+
+ def test_zero_nstart(self):
+ G = nx.Graph([(1, 2), (1, 3), (2, 3)])
+ with pytest.raises(
+ nx.NetworkXException, match="initial vector cannot have all zero values"
+ ):
+ nx.eigenvector_centrality(G, nstart={v: 0 for v in G})
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_group.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_group.py
new file mode 100644
index 00000000..82343f28
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_group.py
@@ -0,0 +1,277 @@
+"""
+Tests for Group Centrality Measures
+"""
+
+import pytest
+
+import networkx as nx
+
+
+class TestGroupBetweennessCentrality:
+ def test_group_betweenness_single_node(self):
+ """
+ Group betweenness centrality for single node group
+ """
+ G = nx.path_graph(5)
+ C = [1]
+ b = nx.group_betweenness_centrality(
+ G, C, weight=None, normalized=False, endpoints=False
+ )
+ b_answer = 3.0
+ assert b == b_answer
+
+ def test_group_betweenness_with_endpoints(self):
+ """
+ Group betweenness centrality for single node group
+ """
+ G = nx.path_graph(5)
+ C = [1]
+ b = nx.group_betweenness_centrality(
+ G, C, weight=None, normalized=False, endpoints=True
+ )
+ b_answer = 7.0
+ assert b == b_answer
+
+ def test_group_betweenness_normalized(self):
+ """
+ Group betweenness centrality for group with more than
+ 1 node and normalized
+ """
+ G = nx.path_graph(5)
+ C = [1, 3]
+ b = nx.group_betweenness_centrality(
+ G, C, weight=None, normalized=True, endpoints=False
+ )
+ b_answer = 1.0
+ assert b == b_answer
+
+ def test_two_group_betweenness_value_zero(self):
+ """
+ Group betweenness centrality value of 0
+ """
+ G = nx.cycle_graph(7)
+ C = [[0, 1, 6], [0, 1, 5]]
+ b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
+ b_answer = [0.0, 3.0]
+ assert b == b_answer
+
+ def test_group_betweenness_value_zero(self):
+ """
+ Group betweenness centrality value of 0
+ """
+ G = nx.cycle_graph(6)
+ C = [0, 1, 5]
+ b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
+ b_answer = 0.0
+ assert b == b_answer
+
+ def test_group_betweenness_disconnected_graph(self):
+ """
+ Group betweenness centrality in a disconnected graph
+ """
+ G = nx.path_graph(5)
+ G.remove_edge(0, 1)
+ C = [1]
+ b = nx.group_betweenness_centrality(G, C, weight=None, normalized=False)
+ b_answer = 0.0
+ assert b == b_answer
+
+ def test_group_betweenness_node_not_in_graph(self):
+ """
+ Node(s) in C not in graph, raises NodeNotFound exception
+ """
+ with pytest.raises(nx.NodeNotFound):
+ nx.group_betweenness_centrality(nx.path_graph(5), [4, 7, 8])
+
+ def test_group_betweenness_directed_weighted(self):
+ """
+ Group betweenness centrality in a directed and weighted graph
+ """
+ G = nx.DiGraph()
+ G.add_edge(1, 0, weight=1)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(1, 2, weight=3)
+ G.add_edge(3, 1, weight=4)
+ G.add_edge(2, 3, weight=1)
+ G.add_edge(4, 3, weight=6)
+ G.add_edge(2, 4, weight=7)
+ C = [1, 2]
+ b = nx.group_betweenness_centrality(G, C, weight="weight", normalized=False)
+ b_answer = 5.0
+ assert b == b_answer
+
+
+class TestProminentGroup:
+ np = pytest.importorskip("numpy")
+ pd = pytest.importorskip("pandas")
+
+ def test_prominent_group_single_node(self):
+ """
+ Prominent group for single node
+ """
+ G = nx.path_graph(5)
+ k = 1
+ b, g = nx.prominent_group(G, k, normalized=False, endpoints=False)
+ b_answer, g_answer = 4.0, [2]
+ assert b == b_answer and g == g_answer
+
+ def test_prominent_group_with_c(self):
+ """
+ Prominent group without some nodes
+ """
+ G = nx.path_graph(5)
+ k = 1
+ b, g = nx.prominent_group(G, k, normalized=False, C=[2])
+ b_answer, g_answer = 3.0, [1]
+ assert b == b_answer and g == g_answer
+
+ def test_prominent_group_normalized_endpoints(self):
+ """
+ Prominent group with normalized result, with endpoints
+ """
+ G = nx.cycle_graph(7)
+ k = 2
+ b, g = nx.prominent_group(G, k, normalized=True, endpoints=True)
+ b_answer, g_answer = 1.7, [2, 5]
+ assert b == b_answer and g == g_answer
+
+ def test_prominent_group_disconnected_graph(self):
+ """
+ Prominent group of disconnected graph
+ """
+ G = nx.path_graph(6)
+ G.remove_edge(0, 1)
+ k = 1
+ b, g = nx.prominent_group(G, k, weight=None, normalized=False)
+ b_answer, g_answer = 4.0, [3]
+ assert b == b_answer and g == g_answer
+
+ def test_prominent_group_node_not_in_graph(self):
+ """
+ Node(s) in C not in graph, raises NodeNotFound exception
+ """
+ with pytest.raises(nx.NodeNotFound):
+ nx.prominent_group(nx.path_graph(5), 1, C=[10])
+
+ def test_group_betweenness_directed_weighted(self):
+ """
+ Group betweenness centrality in a directed and weighted graph
+ """
+ G = nx.DiGraph()
+ G.add_edge(1, 0, weight=1)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(1, 2, weight=3)
+ G.add_edge(3, 1, weight=4)
+ G.add_edge(2, 3, weight=1)
+ G.add_edge(4, 3, weight=6)
+ G.add_edge(2, 4, weight=7)
+ k = 2
+ b, g = nx.prominent_group(G, k, weight="weight", normalized=False)
+ b_answer, g_answer = 5.0, [1, 2]
+ assert b == b_answer and g == g_answer
+
+ def test_prominent_group_greedy_algorithm(self):
+ """
+ Group betweenness centrality in a greedy algorithm
+ """
+ G = nx.cycle_graph(7)
+ k = 2
+ b, g = nx.prominent_group(G, k, normalized=True, endpoints=True, greedy=True)
+ b_answer, g_answer = 1.7, [6, 3]
+ assert b == b_answer and g == g_answer
+
+
+class TestGroupClosenessCentrality:
+ def test_group_closeness_single_node(self):
+ """
+ Group closeness centrality for a single node group
+ """
+ G = nx.path_graph(5)
+ c = nx.group_closeness_centrality(G, [1])
+ c_answer = nx.closeness_centrality(G, 1)
+ assert c == c_answer
+
+ def test_group_closeness_disconnected(self):
+ """
+ Group closeness centrality for a disconnected graph
+ """
+ G = nx.Graph()
+ G.add_nodes_from([1, 2, 3, 4])
+ c = nx.group_closeness_centrality(G, [1, 2])
+ c_answer = 0
+ assert c == c_answer
+
+ def test_group_closeness_multiple_node(self):
+ """
+ Group closeness centrality for a group with more than
+ 1 node
+ """
+ G = nx.path_graph(4)
+ c = nx.group_closeness_centrality(G, [1, 2])
+ c_answer = 1
+ assert c == c_answer
+
+ def test_group_closeness_node_not_in_graph(self):
+ """
+ Node(s) in S not in graph, raises NodeNotFound exception
+ """
+ with pytest.raises(nx.NodeNotFound):
+ nx.group_closeness_centrality(nx.path_graph(5), [6, 7, 8])
+
+
+class TestGroupDegreeCentrality:
+ def test_group_degree_centrality_single_node(self):
+ """
+ Group degree centrality for a single node group
+ """
+ G = nx.path_graph(4)
+ d = nx.group_degree_centrality(G, [1])
+ d_answer = nx.degree_centrality(G)[1]
+ assert d == d_answer
+
+ def test_group_degree_centrality_multiple_node(self):
+ """
+ Group degree centrality for group with more than
+ 1 node
+ """
+ G = nx.Graph()
+ G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
+ G.add_edges_from(
+ [(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
+ )
+ d = nx.group_degree_centrality(G, [1, 2])
+ d_answer = 1
+ assert d == d_answer
+
+ def test_group_in_degree_centrality(self):
+ """
+ Group in-degree centrality in a DiGraph
+ """
+ G = nx.DiGraph()
+ G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
+ G.add_edges_from(
+ [(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
+ )
+ d = nx.group_in_degree_centrality(G, [1, 2])
+ d_answer = 0
+ assert d == d_answer
+
+ def test_group_out_degree_centrality(self):
+ """
+ Group out-degree centrality in a DiGraph
+ """
+ G = nx.DiGraph()
+ G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8])
+ G.add_edges_from(
+ [(1, 2), (1, 3), (1, 6), (1, 7), (1, 8), (2, 3), (2, 4), (2, 5)]
+ )
+ d = nx.group_out_degree_centrality(G, [1, 2])
+ d_answer = 1
+ assert d == d_answer
+
+ def test_group_degree_centrality_node_not_in_graph(self):
+ """
+ Node(s) in S not in graph, raises NetworkXError
+ """
+ with pytest.raises(nx.NetworkXError):
+ nx.group_degree_centrality(nx.path_graph(5), [6, 7, 8])
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py
new file mode 100644
index 00000000..4b3dc4ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_harmonic_centrality.py
@@ -0,0 +1,122 @@
+"""
+Tests for degree centrality.
+"""
+
+import pytest
+
+import networkx as nx
+from networkx.algorithms.centrality import harmonic_centrality
+
+
+class TestClosenessCentrality:
+ @classmethod
+ def setup_class(cls):
+ cls.P3 = nx.path_graph(3)
+ cls.P4 = nx.path_graph(4)
+ cls.K5 = nx.complete_graph(5)
+
+ cls.C4 = nx.cycle_graph(4)
+ cls.C4_directed = nx.cycle_graph(4, create_using=nx.DiGraph)
+
+ cls.C5 = nx.cycle_graph(5)
+
+ cls.T = nx.balanced_tree(r=2, h=2)
+
+ cls.Gb = nx.DiGraph()
+ cls.Gb.add_edges_from([(0, 1), (0, 2), (0, 4), (2, 1), (2, 3), (4, 3)])
+
+ def test_p3_harmonic(self):
+ c = harmonic_centrality(self.P3)
+ d = {0: 1.5, 1: 2, 2: 1.5}
+ for n in sorted(self.P3):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_p4_harmonic(self):
+ c = harmonic_centrality(self.P4)
+ d = {0: 1.8333333, 1: 2.5, 2: 2.5, 3: 1.8333333}
+ for n in sorted(self.P4):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_clique_complete(self):
+ c = harmonic_centrality(self.K5)
+ d = {0: 4, 1: 4, 2: 4, 3: 4, 4: 4}
+ for n in sorted(self.P3):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_cycle_C4(self):
+ c = harmonic_centrality(self.C4)
+ d = {0: 2.5, 1: 2.5, 2: 2.5, 3: 2.5}
+ for n in sorted(self.C4):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_cycle_C5(self):
+ c = harmonic_centrality(self.C5)
+ d = {0: 3, 1: 3, 2: 3, 3: 3, 4: 3, 5: 4}
+ for n in sorted(self.C5):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_bal_tree(self):
+ c = harmonic_centrality(self.T)
+ d = {0: 4.0, 1: 4.1666, 2: 4.1666, 3: 2.8333, 4: 2.8333, 5: 2.8333, 6: 2.8333}
+ for n in sorted(self.T):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_exampleGraph(self):
+ c = harmonic_centrality(self.Gb)
+ d = {0: 0, 1: 2, 2: 1, 3: 2.5, 4: 1}
+ for n in sorted(self.Gb):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_weighted_harmonic(self):
+ XG = nx.DiGraph()
+ XG.add_weighted_edges_from(
+ [
+ ("a", "b", 10),
+ ("d", "c", 5),
+ ("a", "c", 1),
+ ("e", "f", 2),
+ ("f", "c", 1),
+ ("a", "f", 3),
+ ]
+ )
+ c = harmonic_centrality(XG, distance="weight")
+ d = {"a": 0, "b": 0.1, "c": 2.533, "d": 0, "e": 0, "f": 0.83333}
+ for n in sorted(XG):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_empty(self):
+ G = nx.DiGraph()
+ c = harmonic_centrality(G, distance="weight")
+ d = {}
+ assert c == d
+
+ def test_singleton(self):
+ G = nx.DiGraph()
+ G.add_node(0)
+ c = harmonic_centrality(G, distance="weight")
+ d = {0: 0}
+ assert c == d
+
+ def test_cycle_c4_directed(self):
+ c = harmonic_centrality(self.C4_directed, nbunch=[0, 1], sources=[1, 2])
+ d = {0: 0.833, 1: 0.333}
+ for n in [0, 1]:
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_cycle_c4_directed_subset(self):
+ c = harmonic_centrality(self.C4_directed, nbunch=[0, 1])
+ d = 1.833
+ for n in [0, 1]:
+ assert c[n] == pytest.approx(d, abs=1e-3)
+
+ def test_p3_harmonic_subset(self):
+ c = harmonic_centrality(self.P3, sources=[0, 1])
+ d = {0: 1, 1: 1, 2: 1.5}
+ for n in self.P3:
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_p4_harmonic_subset(self):
+ c = harmonic_centrality(self.P4, nbunch=[2, 3], sources=[0, 1])
+ d = {2: 1.5, 3: 0.8333333}
+ for n in [2, 3]:
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py
new file mode 100644
index 00000000..0927f00b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_katz_centrality.py
@@ -0,0 +1,345 @@
+import math
+
+import pytest
+
+import networkx as nx
+
+
+class TestKatzCentrality:
+ def test_K5(self):
+ """Katz centrality: K5"""
+ G = nx.complete_graph(5)
+ alpha = 0.1
+ b = nx.katz_centrality(G, alpha)
+ v = math.sqrt(1 / 5.0)
+ b_answer = dict.fromkeys(G, v)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ nstart = {n: 1 for n in G}
+ b = nx.katz_centrality(G, alpha, nstart=nstart)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+
+ def test_P3(self):
+ """Katz centrality: P3"""
+ alpha = 0.1
+ G = nx.path_graph(3)
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ b = nx.katz_centrality(G, alpha)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_maxiter(self):
+ with pytest.raises(nx.PowerIterationFailedConvergence):
+ nx.katz_centrality(nx.path_graph(3), 0.1, max_iter=0)
+
+ def test_beta_as_scalar(self):
+ alpha = 0.1
+ beta = 0.1
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ G = nx.path_graph(3)
+ b = nx.katz_centrality(G, alpha, beta)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_beta_as_dict(self):
+ alpha = 0.1
+ beta = {0: 1.0, 1: 1.0, 2: 1.0}
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ G = nx.path_graph(3)
+ b = nx.katz_centrality(G, alpha, beta)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_multiple_alpha(self):
+ alpha_list = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
+ for alpha in alpha_list:
+ b_answer = {
+ 0.1: {
+ 0: 0.5598852584152165,
+ 1: 0.6107839182711449,
+ 2: 0.5598852584152162,
+ },
+ 0.2: {
+ 0: 0.5454545454545454,
+ 1: 0.6363636363636365,
+ 2: 0.5454545454545454,
+ },
+ 0.3: {
+ 0: 0.5333964609104419,
+ 1: 0.6564879518897746,
+ 2: 0.5333964609104419,
+ },
+ 0.4: {
+ 0: 0.5232045649263551,
+ 1: 0.6726915834767423,
+ 2: 0.5232045649263551,
+ },
+ 0.5: {
+ 0: 0.5144957746691622,
+ 1: 0.6859943117075809,
+ 2: 0.5144957746691622,
+ },
+ 0.6: {
+ 0: 0.5069794004195823,
+ 1: 0.6970966755769258,
+ 2: 0.5069794004195823,
+ },
+ }
+ G = nx.path_graph(3)
+ b = nx.katz_centrality(G, alpha)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[alpha][n], abs=1e-4)
+
+ def test_multigraph(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.katz_centrality(nx.MultiGraph(), 0.1)
+
+ def test_empty(self):
+ e = nx.katz_centrality(nx.Graph(), 0.1)
+ assert e == {}
+
+ def test_bad_beta(self):
+ with pytest.raises(nx.NetworkXException):
+ G = nx.Graph([(0, 1)])
+ beta = {0: 77}
+ nx.katz_centrality(G, 0.1, beta=beta)
+
+ def test_bad_beta_number(self):
+ with pytest.raises(nx.NetworkXException):
+ G = nx.Graph([(0, 1)])
+ nx.katz_centrality(G, 0.1, beta="foo")
+
+
+class TestKatzCentralityNumpy:
+ @classmethod
+ def setup_class(cls):
+ global np
+ np = pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+
+ def test_K5(self):
+ """Katz centrality: K5"""
+ G = nx.complete_graph(5)
+ alpha = 0.1
+ b = nx.katz_centrality(G, alpha)
+ v = math.sqrt(1 / 5.0)
+ b_answer = dict.fromkeys(G, v)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ b = nx.eigenvector_centrality_numpy(G)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_P3(self):
+ """Katz centrality: P3"""
+ alpha = 0.1
+ G = nx.path_graph(3)
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ b = nx.katz_centrality_numpy(G, alpha)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_beta_as_scalar(self):
+ alpha = 0.1
+ beta = 0.1
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ G = nx.path_graph(3)
+ b = nx.katz_centrality_numpy(G, alpha, beta)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_beta_as_dict(self):
+ alpha = 0.1
+ beta = {0: 1.0, 1: 1.0, 2: 1.0}
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ G = nx.path_graph(3)
+ b = nx.katz_centrality_numpy(G, alpha, beta)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+ def test_multiple_alpha(self):
+ alpha_list = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
+ for alpha in alpha_list:
+ b_answer = {
+ 0.1: {
+ 0: 0.5598852584152165,
+ 1: 0.6107839182711449,
+ 2: 0.5598852584152162,
+ },
+ 0.2: {
+ 0: 0.5454545454545454,
+ 1: 0.6363636363636365,
+ 2: 0.5454545454545454,
+ },
+ 0.3: {
+ 0: 0.5333964609104419,
+ 1: 0.6564879518897746,
+ 2: 0.5333964609104419,
+ },
+ 0.4: {
+ 0: 0.5232045649263551,
+ 1: 0.6726915834767423,
+ 2: 0.5232045649263551,
+ },
+ 0.5: {
+ 0: 0.5144957746691622,
+ 1: 0.6859943117075809,
+ 2: 0.5144957746691622,
+ },
+ 0.6: {
+ 0: 0.5069794004195823,
+ 1: 0.6970966755769258,
+ 2: 0.5069794004195823,
+ },
+ }
+ G = nx.path_graph(3)
+ b = nx.katz_centrality_numpy(G, alpha)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[alpha][n], abs=1e-4)
+
+ def test_multigraph(self):
+ with pytest.raises(nx.NetworkXException):
+ nx.katz_centrality(nx.MultiGraph(), 0.1)
+
+ def test_empty(self):
+ e = nx.katz_centrality(nx.Graph(), 0.1)
+ assert e == {}
+
+ def test_bad_beta(self):
+ with pytest.raises(nx.NetworkXException):
+ G = nx.Graph([(0, 1)])
+ beta = {0: 77}
+ nx.katz_centrality_numpy(G, 0.1, beta=beta)
+
+ def test_bad_beta_numbe(self):
+ with pytest.raises(nx.NetworkXException):
+ G = nx.Graph([(0, 1)])
+ nx.katz_centrality_numpy(G, 0.1, beta="foo")
+
+ def test_K5_unweighted(self):
+ """Katz centrality: K5"""
+ G = nx.complete_graph(5)
+ alpha = 0.1
+ b = nx.katz_centrality(G, alpha, weight=None)
+ v = math.sqrt(1 / 5.0)
+ b_answer = dict.fromkeys(G, v)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-7)
+ b = nx.eigenvector_centrality_numpy(G, weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-3)
+
+ def test_P3_unweighted(self):
+ """Katz centrality: P3"""
+ alpha = 0.1
+ G = nx.path_graph(3)
+ b_answer = {0: 0.5598852584152165, 1: 0.6107839182711449, 2: 0.5598852584152162}
+ b = nx.katz_centrality_numpy(G, alpha, weight=None)
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-4)
+
+
+class TestKatzCentralityDirected:
+ @classmethod
+ def setup_class(cls):
+ G = nx.DiGraph()
+ edges = [
+ (1, 2),
+ (1, 3),
+ (2, 4),
+ (3, 2),
+ (3, 5),
+ (4, 2),
+ (4, 5),
+ (4, 6),
+ (5, 6),
+ (5, 7),
+ (5, 8),
+ (6, 8),
+ (7, 1),
+ (7, 5),
+ (7, 8),
+ (8, 6),
+ (8, 7),
+ ]
+ G.add_edges_from(edges, weight=2.0)
+ cls.G = G.reverse()
+ cls.G.alpha = 0.1
+ cls.G.evc = [
+ 0.3289589783189635,
+ 0.2832077296243516,
+ 0.3425906003685471,
+ 0.3970420865198392,
+ 0.41074871061646284,
+ 0.272257430756461,
+ 0.4201989685435462,
+ 0.34229059218038554,
+ ]
+
+ H = nx.DiGraph(edges)
+ cls.H = G.reverse()
+ cls.H.alpha = 0.1
+ cls.H.evc = [
+ 0.3289589783189635,
+ 0.2832077296243516,
+ 0.3425906003685471,
+ 0.3970420865198392,
+ 0.41074871061646284,
+ 0.272257430756461,
+ 0.4201989685435462,
+ 0.34229059218038554,
+ ]
+
+ def test_katz_centrality_weighted(self):
+ G = self.G
+ alpha = self.G.alpha
+ p = nx.katz_centrality(G, alpha, weight="weight")
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+ def test_katz_centrality_unweighted(self):
+ H = self.H
+ alpha = self.H.alpha
+ p = nx.katz_centrality(H, alpha, weight="weight")
+ for a, b in zip(list(p.values()), self.H.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+
+class TestKatzCentralityDirectedNumpy(TestKatzCentralityDirected):
+ @classmethod
+ def setup_class(cls):
+ global np
+ np = pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+ super().setup_class()
+
+ def test_katz_centrality_weighted(self):
+ G = self.G
+ alpha = self.G.alpha
+ p = nx.katz_centrality_numpy(G, alpha, weight="weight")
+ for a, b in zip(list(p.values()), self.G.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+ def test_katz_centrality_unweighted(self):
+ H = self.H
+ alpha = self.H.alpha
+ p = nx.katz_centrality_numpy(H, alpha, weight="weight")
+ for a, b in zip(list(p.values()), self.H.evc):
+ assert a == pytest.approx(b, abs=1e-7)
+
+
+class TestKatzEigenvectorVKatz:
+ @classmethod
+ def setup_class(cls):
+ global np
+ np = pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+
+ def test_eigenvector_v_katz_random(self):
+ G = nx.gnp_random_graph(10, 0.5, seed=1234)
+ l = max(np.linalg.eigvals(nx.adjacency_matrix(G).todense()))
+ e = nx.eigenvector_centrality_numpy(G)
+ k = nx.katz_centrality_numpy(G, 1.0 / l)
+ for n in G:
+ assert e[n] == pytest.approx(k[n], abs=1e-7)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py
new file mode 100644
index 00000000..21aa28b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_laplacian_centrality.py
@@ -0,0 +1,221 @@
+import pytest
+
+import networkx as nx
+
+np = pytest.importorskip("numpy")
+sp = pytest.importorskip("scipy")
+
+
+def test_laplacian_centrality_null_graph():
+ G = nx.Graph()
+ with pytest.raises(nx.NetworkXPointlessConcept):
+ d = nx.laplacian_centrality(G, normalized=False)
+
+
+def test_laplacian_centrality_single_node():
+ """See gh-6571"""
+ G = nx.empty_graph(1)
+ assert nx.laplacian_centrality(G, normalized=False) == {0: 0}
+ with pytest.raises(ZeroDivisionError):
+ nx.laplacian_centrality(G, normalized=True)
+
+
+def test_laplacian_centrality_unconnected_nodes():
+ """laplacian_centrality on a unconnected node graph should return 0
+
+ For graphs without edges, the Laplacian energy is 0 and is unchanged with
+ node removal, so::
+
+ LC(v) = LE(G) - LE(G - v) = 0 - 0 = 0
+ """
+ G = nx.empty_graph(3)
+ assert nx.laplacian_centrality(G, normalized=False) == {0: 0, 1: 0, 2: 0}
+
+
+def test_laplacian_centrality_empty_graph():
+ G = nx.empty_graph(3)
+ with pytest.raises(ZeroDivisionError):
+ d = nx.laplacian_centrality(G, normalized=True)
+
+
+def test_laplacian_centrality_E():
+ E = nx.Graph()
+ E.add_weighted_edges_from(
+ [(0, 1, 4), (4, 5, 1), (0, 2, 2), (2, 1, 1), (1, 3, 2), (1, 4, 2)]
+ )
+ d = nx.laplacian_centrality(E)
+ exact = {
+ 0: 0.700000,
+ 1: 0.900000,
+ 2: 0.280000,
+ 3: 0.220000,
+ 4: 0.260000,
+ 5: 0.040000,
+ }
+
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ # Check not normalized
+ full_energy = 200
+ dnn = nx.laplacian_centrality(E, normalized=False)
+ for n, dc in dnn.items():
+ assert exact[n] * full_energy == pytest.approx(dc, abs=1e-7)
+
+ # Check unweighted not-normalized version
+ duw_nn = nx.laplacian_centrality(E, normalized=False, weight=None)
+ print(duw_nn)
+ exact_uw_nn = {
+ 0: 18,
+ 1: 34,
+ 2: 18,
+ 3: 10,
+ 4: 16,
+ 5: 6,
+ }
+ for n, dc in duw_nn.items():
+ assert exact_uw_nn[n] == pytest.approx(dc, abs=1e-7)
+
+ # Check unweighted version
+ duw = nx.laplacian_centrality(E, weight=None)
+ full_energy = 42
+ for n, dc in duw.items():
+ assert exact_uw_nn[n] / full_energy == pytest.approx(dc, abs=1e-7)
+
+
+def test_laplacian_centrality_KC():
+ KC = nx.karate_club_graph()
+ d = nx.laplacian_centrality(KC)
+ exact = {
+ 0: 0.2543593,
+ 1: 0.1724524,
+ 2: 0.2166053,
+ 3: 0.0964646,
+ 4: 0.0350344,
+ 5: 0.0571109,
+ 6: 0.0540713,
+ 7: 0.0788674,
+ 8: 0.1222204,
+ 9: 0.0217565,
+ 10: 0.0308751,
+ 11: 0.0215965,
+ 12: 0.0174372,
+ 13: 0.118861,
+ 14: 0.0366341,
+ 15: 0.0548712,
+ 16: 0.0172772,
+ 17: 0.0191969,
+ 18: 0.0225564,
+ 19: 0.0331147,
+ 20: 0.0279955,
+ 21: 0.0246361,
+ 22: 0.0382339,
+ 23: 0.1294193,
+ 24: 0.0227164,
+ 25: 0.0644697,
+ 26: 0.0281555,
+ 27: 0.075188,
+ 28: 0.0364742,
+ 29: 0.0707087,
+ 30: 0.0708687,
+ 31: 0.131019,
+ 32: 0.2370821,
+ 33: 0.3066709,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ # Check not normalized
+ full_energy = 12502
+ dnn = nx.laplacian_centrality(KC, normalized=False)
+ for n, dc in dnn.items():
+ assert exact[n] * full_energy == pytest.approx(dc, abs=1e-3)
+
+
+def test_laplacian_centrality_K():
+ K = nx.krackhardt_kite_graph()
+ d = nx.laplacian_centrality(K)
+ exact = {
+ 0: 0.3010753,
+ 1: 0.3010753,
+ 2: 0.2258065,
+ 3: 0.483871,
+ 4: 0.2258065,
+ 5: 0.3870968,
+ 6: 0.3870968,
+ 7: 0.1935484,
+ 8: 0.0752688,
+ 9: 0.0322581,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ # Check not normalized
+ full_energy = 186
+ dnn = nx.laplacian_centrality(K, normalized=False)
+ for n, dc in dnn.items():
+ assert exact[n] * full_energy == pytest.approx(dc, abs=1e-3)
+
+
+def test_laplacian_centrality_P3():
+ P3 = nx.path_graph(3)
+ d = nx.laplacian_centrality(P3)
+ exact = {0: 0.6, 1: 1.0, 2: 0.6}
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+
+def test_laplacian_centrality_K5():
+ K5 = nx.complete_graph(5)
+ d = nx.laplacian_centrality(K5)
+ exact = {0: 0.52, 1: 0.52, 2: 0.52, 3: 0.52, 4: 0.52}
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+
+def test_laplacian_centrality_FF():
+ FF = nx.florentine_families_graph()
+ d = nx.laplacian_centrality(FF)
+ exact = {
+ "Acciaiuoli": 0.0804598,
+ "Medici": 0.4022989,
+ "Castellani": 0.1724138,
+ "Peruzzi": 0.183908,
+ "Strozzi": 0.2528736,
+ "Barbadori": 0.137931,
+ "Ridolfi": 0.2183908,
+ "Tornabuoni": 0.2183908,
+ "Albizzi": 0.1954023,
+ "Salviati": 0.1149425,
+ "Pazzi": 0.0344828,
+ "Bischeri": 0.1954023,
+ "Guadagni": 0.2298851,
+ "Ginori": 0.045977,
+ "Lamberteschi": 0.0574713,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+
+def test_laplacian_centrality_DG():
+ DG = nx.DiGraph([(0, 5), (1, 5), (2, 5), (3, 5), (4, 5), (5, 6), (5, 7), (5, 8)])
+ d = nx.laplacian_centrality(DG)
+ exact = {
+ 0: 0.2123352,
+ 5: 0.515391,
+ 1: 0.2123352,
+ 2: 0.2123352,
+ 3: 0.2123352,
+ 4: 0.2123352,
+ 6: 0.2952031,
+ 7: 0.2952031,
+ 8: 0.2952031,
+ }
+ for n, dc in d.items():
+ assert exact[n] == pytest.approx(dc, abs=1e-7)
+
+ # Check not normalized
+ full_energy = 9.50704
+ dnn = nx.laplacian_centrality(DG, normalized=False)
+ for n, dc in dnn.items():
+ assert exact[n] * full_energy == pytest.approx(dc, abs=1e-4)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py
new file mode 100644
index 00000000..bf096039
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_load_centrality.py
@@ -0,0 +1,344 @@
+import pytest
+
+import networkx as nx
+
+
+class TestLoadCentrality:
+ @classmethod
+ def setup_class(cls):
+ G = nx.Graph()
+ G.add_edge(0, 1, weight=3)
+ G.add_edge(0, 2, weight=2)
+ G.add_edge(0, 3, weight=6)
+ G.add_edge(0, 4, weight=4)
+ G.add_edge(1, 3, weight=5)
+ G.add_edge(1, 5, weight=5)
+ G.add_edge(2, 4, weight=1)
+ G.add_edge(3, 4, weight=2)
+ G.add_edge(3, 5, weight=1)
+ G.add_edge(4, 5, weight=4)
+ cls.G = G
+ cls.exact_weighted = {0: 4.0, 1: 0.0, 2: 8.0, 3: 6.0, 4: 8.0, 5: 0.0}
+ cls.K = nx.krackhardt_kite_graph()
+ cls.P3 = nx.path_graph(3)
+ cls.P4 = nx.path_graph(4)
+ cls.K5 = nx.complete_graph(5)
+ cls.P2 = nx.path_graph(2)
+
+ cls.C4 = nx.cycle_graph(4)
+ cls.T = nx.balanced_tree(r=2, h=2)
+ cls.Gb = nx.Graph()
+ cls.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
+ cls.F = nx.florentine_families_graph()
+ cls.LM = nx.les_miserables_graph()
+ cls.D = nx.cycle_graph(3, create_using=nx.DiGraph())
+ cls.D.add_edges_from([(3, 0), (4, 3)])
+
+ def test_not_strongly_connected(self):
+ b = nx.load_centrality(self.D)
+ result = {0: 5.0 / 12, 1: 1.0 / 4, 2: 1.0 / 12, 3: 1.0 / 4, 4: 0.000}
+ for n in sorted(self.D):
+ assert result[n] == pytest.approx(b[n], abs=1e-3)
+ assert result[n] == pytest.approx(nx.load_centrality(self.D, n), abs=1e-3)
+
+ def test_P2_normalized_load(self):
+ G = self.P2
+ c = nx.load_centrality(G, normalized=True)
+ d = {0: 0.000, 1: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_weighted_load(self):
+ b = nx.load_centrality(self.G, weight="weight", normalized=False)
+ for n in sorted(self.G):
+ assert b[n] == self.exact_weighted[n]
+
+ def test_k5_load(self):
+ G = self.K5
+ c = nx.load_centrality(G)
+ d = {0: 0.000, 1: 0.000, 2: 0.000, 3: 0.000, 4: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_p3_load(self):
+ G = self.P3
+ c = nx.load_centrality(G)
+ d = {0: 0.000, 1: 1.000, 2: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+ c = nx.load_centrality(G, v=1)
+ assert c == pytest.approx(1.0, abs=1e-7)
+ c = nx.load_centrality(G, v=1, normalized=True)
+ assert c == pytest.approx(1.0, abs=1e-7)
+
+ def test_p2_load(self):
+ G = nx.path_graph(2)
+ c = nx.load_centrality(G)
+ d = {0: 0.000, 1: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_krackhardt_load(self):
+ G = self.K
+ c = nx.load_centrality(G)
+ d = {
+ 0: 0.023,
+ 1: 0.023,
+ 2: 0.000,
+ 3: 0.102,
+ 4: 0.000,
+ 5: 0.231,
+ 6: 0.231,
+ 7: 0.389,
+ 8: 0.222,
+ 9: 0.000,
+ }
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_florentine_families_load(self):
+ G = self.F
+ c = nx.load_centrality(G)
+ d = {
+ "Acciaiuoli": 0.000,
+ "Albizzi": 0.211,
+ "Barbadori": 0.093,
+ "Bischeri": 0.104,
+ "Castellani": 0.055,
+ "Ginori": 0.000,
+ "Guadagni": 0.251,
+ "Lamberteschi": 0.000,
+ "Medici": 0.522,
+ "Pazzi": 0.000,
+ "Peruzzi": 0.022,
+ "Ridolfi": 0.117,
+ "Salviati": 0.143,
+ "Strozzi": 0.106,
+ "Tornabuoni": 0.090,
+ }
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_les_miserables_load(self):
+ G = self.LM
+ c = nx.load_centrality(G)
+ d = {
+ "Napoleon": 0.000,
+ "Myriel": 0.177,
+ "MlleBaptistine": 0.000,
+ "MmeMagloire": 0.000,
+ "CountessDeLo": 0.000,
+ "Geborand": 0.000,
+ "Champtercier": 0.000,
+ "Cravatte": 0.000,
+ "Count": 0.000,
+ "OldMan": 0.000,
+ "Valjean": 0.567,
+ "Labarre": 0.000,
+ "Marguerite": 0.000,
+ "MmeDeR": 0.000,
+ "Isabeau": 0.000,
+ "Gervais": 0.000,
+ "Listolier": 0.000,
+ "Tholomyes": 0.043,
+ "Fameuil": 0.000,
+ "Blacheville": 0.000,
+ "Favourite": 0.000,
+ "Dahlia": 0.000,
+ "Zephine": 0.000,
+ "Fantine": 0.128,
+ "MmeThenardier": 0.029,
+ "Thenardier": 0.075,
+ "Cosette": 0.024,
+ "Javert": 0.054,
+ "Fauchelevent": 0.026,
+ "Bamatabois": 0.008,
+ "Perpetue": 0.000,
+ "Simplice": 0.009,
+ "Scaufflaire": 0.000,
+ "Woman1": 0.000,
+ "Judge": 0.000,
+ "Champmathieu": 0.000,
+ "Brevet": 0.000,
+ "Chenildieu": 0.000,
+ "Cochepaille": 0.000,
+ "Pontmercy": 0.007,
+ "Boulatruelle": 0.000,
+ "Eponine": 0.012,
+ "Anzelma": 0.000,
+ "Woman2": 0.000,
+ "MotherInnocent": 0.000,
+ "Gribier": 0.000,
+ "MmeBurgon": 0.026,
+ "Jondrette": 0.000,
+ "Gavroche": 0.164,
+ "Gillenormand": 0.021,
+ "Magnon": 0.000,
+ "MlleGillenormand": 0.047,
+ "MmePontmercy": 0.000,
+ "MlleVaubois": 0.000,
+ "LtGillenormand": 0.000,
+ "Marius": 0.133,
+ "BaronessT": 0.000,
+ "Mabeuf": 0.028,
+ "Enjolras": 0.041,
+ "Combeferre": 0.001,
+ "Prouvaire": 0.000,
+ "Feuilly": 0.001,
+ "Courfeyrac": 0.006,
+ "Bahorel": 0.002,
+ "Bossuet": 0.032,
+ "Joly": 0.002,
+ "Grantaire": 0.000,
+ "MotherPlutarch": 0.000,
+ "Gueulemer": 0.005,
+ "Babet": 0.005,
+ "Claquesous": 0.005,
+ "Montparnasse": 0.004,
+ "Toussaint": 0.000,
+ "Child1": 0.000,
+ "Child2": 0.000,
+ "Brujon": 0.000,
+ "MmeHucheloup": 0.000,
+ }
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_unnormalized_k5_load(self):
+ G = self.K5
+ c = nx.load_centrality(G, normalized=False)
+ d = {0: 0.000, 1: 0.000, 2: 0.000, 3: 0.000, 4: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_unnormalized_p3_load(self):
+ G = self.P3
+ c = nx.load_centrality(G, normalized=False)
+ d = {0: 0.000, 1: 2.000, 2: 0.000}
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_unnormalized_krackhardt_load(self):
+ G = self.K
+ c = nx.load_centrality(G, normalized=False)
+ d = {
+ 0: 1.667,
+ 1: 1.667,
+ 2: 0.000,
+ 3: 7.333,
+ 4: 0.000,
+ 5: 16.667,
+ 6: 16.667,
+ 7: 28.000,
+ 8: 16.000,
+ 9: 0.000,
+ }
+
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_unnormalized_florentine_families_load(self):
+ G = self.F
+ c = nx.load_centrality(G, normalized=False)
+
+ d = {
+ "Acciaiuoli": 0.000,
+ "Albizzi": 38.333,
+ "Barbadori": 17.000,
+ "Bischeri": 19.000,
+ "Castellani": 10.000,
+ "Ginori": 0.000,
+ "Guadagni": 45.667,
+ "Lamberteschi": 0.000,
+ "Medici": 95.000,
+ "Pazzi": 0.000,
+ "Peruzzi": 4.000,
+ "Ridolfi": 21.333,
+ "Salviati": 26.000,
+ "Strozzi": 19.333,
+ "Tornabuoni": 16.333,
+ }
+ for n in sorted(G):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_load_betweenness_difference(self):
+ # Difference Between Load and Betweenness
+ # --------------------------------------- The smallest graph
+ # that shows the difference between load and betweenness is
+ # G=ladder_graph(3) (Graph B below)
+
+ # Graph A and B are from Tao Zhou, Jian-Guo Liu, Bing-Hong
+ # Wang: Comment on "Scientific collaboration
+ # networks. II. Shortest paths, weighted networks, and
+ # centrality". https://arxiv.org/pdf/physics/0511084
+
+ # Notice that unlike here, their calculation adds to 1 to the
+ # betweenness of every node i for every path from i to every
+ # other node. This is exactly what it should be, based on
+ # Eqn. (1) in their paper: the eqn is B(v) = \sum_{s\neq t,
+ # s\neq v}{\frac{\sigma_{st}(v)}{\sigma_{st}}}, therefore,
+ # they allow v to be the target node.
+
+ # We follow Brandes 2001, who follows Freeman 1977 that make
+ # the sum for betweenness of v exclude paths where v is either
+ # the source or target node. To agree with their numbers, we
+ # must additionally, remove edge (4,8) from the graph, see AC
+ # example following (there is a mistake in the figure in their
+ # paper - personal communication).
+
+ # A = nx.Graph()
+ # A.add_edges_from([(0,1), (1,2), (1,3), (2,4),
+ # (3,5), (4,6), (4,7), (4,8),
+ # (5,8), (6,9), (7,9), (8,9)])
+ B = nx.Graph() # ladder_graph(3)
+ B.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (4, 5), (3, 5)])
+ c = nx.load_centrality(B, normalized=False)
+ d = {0: 1.750, 1: 1.750, 2: 6.500, 3: 6.500, 4: 1.750, 5: 1.750}
+ for n in sorted(B):
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_c4_edge_load(self):
+ G = self.C4
+ c = nx.edge_load_centrality(G)
+ d = {(0, 1): 6.000, (0, 3): 6.000, (1, 2): 6.000, (2, 3): 6.000}
+ for n in G.edges():
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_p4_edge_load(self):
+ G = self.P4
+ c = nx.edge_load_centrality(G)
+ d = {(0, 1): 6.000, (1, 2): 8.000, (2, 3): 6.000}
+ for n in G.edges():
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_k5_edge_load(self):
+ G = self.K5
+ c = nx.edge_load_centrality(G)
+ d = {
+ (0, 1): 5.000,
+ (0, 2): 5.000,
+ (0, 3): 5.000,
+ (0, 4): 5.000,
+ (1, 2): 5.000,
+ (1, 3): 5.000,
+ (1, 4): 5.000,
+ (2, 3): 5.000,
+ (2, 4): 5.000,
+ (3, 4): 5.000,
+ }
+ for n in G.edges():
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
+
+ def test_tree_edge_load(self):
+ G = self.T
+ c = nx.edge_load_centrality(G)
+ d = {
+ (0, 1): 24.000,
+ (0, 2): 24.000,
+ (1, 3): 12.000,
+ (1, 4): 12.000,
+ (2, 5): 12.000,
+ (2, 6): 12.000,
+ }
+ for n in G.edges():
+ assert c[n] == pytest.approx(d[n], abs=1e-3)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_percolation_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_percolation_centrality.py
new file mode 100644
index 00000000..0cb8f529
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_percolation_centrality.py
@@ -0,0 +1,87 @@
+import pytest
+
+import networkx as nx
+
+
+def example1a_G():
+ G = nx.Graph()
+ G.add_node(1, percolation=0.1)
+ G.add_node(2, percolation=0.2)
+ G.add_node(3, percolation=0.2)
+ G.add_node(4, percolation=0.2)
+ G.add_node(5, percolation=0.3)
+ G.add_node(6, percolation=0.2)
+ G.add_node(7, percolation=0.5)
+ G.add_node(8, percolation=0.5)
+ G.add_edges_from([(1, 4), (2, 4), (3, 4), (4, 5), (5, 6), (6, 7), (6, 8)])
+ return G
+
+
+def example1b_G():
+ G = nx.Graph()
+ G.add_node(1, percolation=0.3)
+ G.add_node(2, percolation=0.5)
+ G.add_node(3, percolation=0.5)
+ G.add_node(4, percolation=0.2)
+ G.add_node(5, percolation=0.3)
+ G.add_node(6, percolation=0.2)
+ G.add_node(7, percolation=0.1)
+ G.add_node(8, percolation=0.1)
+ G.add_edges_from([(1, 4), (2, 4), (3, 4), (4, 5), (5, 6), (6, 7), (6, 8)])
+ return G
+
+
+def test_percolation_example1a():
+ """percolation centrality: example 1a"""
+ G = example1a_G()
+ p = nx.percolation_centrality(G)
+ p_answer = {4: 0.625, 6: 0.667}
+ for n, k in p_answer.items():
+ assert p[n] == pytest.approx(k, abs=1e-3)
+
+
+def test_percolation_example1b():
+ """percolation centrality: example 1a"""
+ G = example1b_G()
+ p = nx.percolation_centrality(G)
+ p_answer = {4: 0.825, 6: 0.4}
+ for n, k in p_answer.items():
+ assert p[n] == pytest.approx(k, abs=1e-3)
+
+
+def test_converge_to_betweenness():
+ """percolation centrality: should converge to betweenness
+ centrality when all nodes are percolated the same"""
+ # taken from betweenness test test_florentine_families_graph
+ G = nx.florentine_families_graph()
+ b_answer = {
+ "Acciaiuoli": 0.000,
+ "Albizzi": 0.212,
+ "Barbadori": 0.093,
+ "Bischeri": 0.104,
+ "Castellani": 0.055,
+ "Ginori": 0.000,
+ "Guadagni": 0.255,
+ "Lamberteschi": 0.000,
+ "Medici": 0.522,
+ "Pazzi": 0.000,
+ "Peruzzi": 0.022,
+ "Ridolfi": 0.114,
+ "Salviati": 0.143,
+ "Strozzi": 0.103,
+ "Tornabuoni": 0.092,
+ }
+
+ # If no initial state is provided, state for
+ # every node defaults to 1
+ p_answer = nx.percolation_centrality(G)
+ assert p_answer == pytest.approx(b_answer, abs=1e-3)
+
+ p_states = {k: 0.3 for k, v in b_answer.items()}
+ p_answer = nx.percolation_centrality(G, states=p_states)
+ assert p_answer == pytest.approx(b_answer, abs=1e-3)
+
+
+def test_default_percolation():
+ G = nx.erdos_renyi_graph(42, 0.42, seed=42)
+ assert nx.percolation_centrality(G) == pytest.approx(nx.betweenness_centrality(G))
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_reaching.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_reaching.py
new file mode 100644
index 00000000..35d50e70
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_reaching.py
@@ -0,0 +1,140 @@
+"""Unit tests for the :mod:`networkx.algorithms.centrality.reaching` module."""
+
+import pytest
+
+import networkx as nx
+
+
+class TestGlobalReachingCentrality:
+ """Unit tests for the global reaching centrality function."""
+
+ def test_non_positive_weights(self):
+ with pytest.raises(nx.NetworkXError):
+ G = nx.DiGraph()
+ nx.global_reaching_centrality(G, weight="weight")
+
+ def test_negatively_weighted(self):
+ with pytest.raises(nx.NetworkXError):
+ G = nx.Graph()
+ G.add_weighted_edges_from([(0, 1, -2), (1, 2, +1)])
+ nx.global_reaching_centrality(G, weight="weight")
+
+ def test_directed_star(self):
+ G = nx.DiGraph()
+ G.add_weighted_edges_from([(1, 2, 0.5), (1, 3, 0.5)])
+ grc = nx.global_reaching_centrality
+ assert grc(G, normalized=False, weight="weight") == 0.5
+ assert grc(G) == 1
+
+ def test_undirected_unweighted_star(self):
+ G = nx.star_graph(2)
+ grc = nx.global_reaching_centrality
+ assert grc(G, normalized=False, weight=None) == 0.25
+
+ def test_undirected_weighted_star(self):
+ G = nx.Graph()
+ G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
+ grc = nx.global_reaching_centrality
+ assert grc(G, normalized=False, weight="weight") == 0.375
+
+ def test_cycle_directed_unweighted(self):
+ G = nx.DiGraph()
+ G.add_edge(1, 2)
+ G.add_edge(2, 1)
+ assert nx.global_reaching_centrality(G, weight=None) == 0
+
+ def test_cycle_undirected_unweighted(self):
+ G = nx.Graph()
+ G.add_edge(1, 2)
+ assert nx.global_reaching_centrality(G, weight=None) == 0
+
+ def test_cycle_directed_weighted(self):
+ G = nx.DiGraph()
+ G.add_weighted_edges_from([(1, 2, 1), (2, 1, 1)])
+ assert nx.global_reaching_centrality(G) == 0
+
+ def test_cycle_undirected_weighted(self):
+ G = nx.Graph()
+ G.add_edge(1, 2, weight=1)
+ grc = nx.global_reaching_centrality
+ assert grc(G, normalized=False) == 0
+
+ def test_directed_weighted(self):
+ G = nx.DiGraph()
+ G.add_edge("A", "B", weight=5)
+ G.add_edge("B", "C", weight=1)
+ G.add_edge("B", "D", weight=0.25)
+ G.add_edge("D", "E", weight=1)
+
+ denom = len(G) - 1
+ A_local = sum([5, 3, 2.625, 2.0833333333333]) / denom
+ B_local = sum([1, 0.25, 0.625]) / denom
+ C_local = 0
+ D_local = sum([1]) / denom
+ E_local = 0
+
+ local_reach_ctrs = [A_local, C_local, B_local, D_local, E_local]
+ max_local = max(local_reach_ctrs)
+ expected = sum(max_local - lrc for lrc in local_reach_ctrs) / denom
+ grc = nx.global_reaching_centrality
+ actual = grc(G, normalized=False, weight="weight")
+ assert expected == pytest.approx(actual, abs=1e-7)
+
+ def test_single_node_with_cycle(self):
+ G = nx.DiGraph([(1, 1)])
+ with pytest.raises(nx.NetworkXError, match="local_reaching_centrality"):
+ nx.global_reaching_centrality(G)
+
+ def test_single_node_with_weighted_cycle(self):
+ G = nx.DiGraph()
+ G.add_weighted_edges_from([(1, 1, 2)])
+ with pytest.raises(nx.NetworkXError, match="local_reaching_centrality"):
+ nx.global_reaching_centrality(G, weight="weight")
+
+
+class TestLocalReachingCentrality:
+ """Unit tests for the local reaching centrality function."""
+
+ def test_non_positive_weights(self):
+ with pytest.raises(nx.NetworkXError):
+ G = nx.DiGraph()
+ G.add_weighted_edges_from([(0, 1, 0)])
+ nx.local_reaching_centrality(G, 0, weight="weight")
+
+ def test_negatively_weighted(self):
+ with pytest.raises(nx.NetworkXError):
+ G = nx.Graph()
+ G.add_weighted_edges_from([(0, 1, -2), (1, 2, +1)])
+ nx.local_reaching_centrality(G, 0, weight="weight")
+
+ def test_undirected_unweighted_star(self):
+ G = nx.star_graph(2)
+ grc = nx.local_reaching_centrality
+ assert grc(G, 1, weight=None, normalized=False) == 0.75
+
+ def test_undirected_weighted_star(self):
+ G = nx.Graph()
+ G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
+ centrality = nx.local_reaching_centrality(
+ G, 1, normalized=False, weight="weight"
+ )
+ assert centrality == 1.5
+
+ def test_undirected_weighted_normalized(self):
+ G = nx.Graph()
+ G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2)])
+ centrality = nx.local_reaching_centrality(
+ G, 1, normalized=True, weight="weight"
+ )
+ assert centrality == 1.0
+
+ def test_single_node_with_cycle(self):
+ G = nx.DiGraph([(1, 1)])
+ with pytest.raises(nx.NetworkXError, match="local_reaching_centrality"):
+ nx.local_reaching_centrality(G, 1)
+
+ def test_single_node_with_weighted_cycle(self):
+ G = nx.DiGraph()
+ G.add_weighted_edges_from([(1, 1, 2)])
+ with pytest.raises(nx.NetworkXError, match="local_reaching_centrality"):
+ nx.local_reaching_centrality(G, 1, weight="weight")
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_second_order_centrality.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_second_order_centrality.py
new file mode 100644
index 00000000..cc304786
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_second_order_centrality.py
@@ -0,0 +1,82 @@
+"""
+Tests for second order centrality.
+"""
+
+import pytest
+
+pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+import networkx as nx
+
+
+def test_empty():
+ with pytest.raises(nx.NetworkXException):
+ G = nx.empty_graph()
+ nx.second_order_centrality(G)
+
+
+def test_non_connected():
+ with pytest.raises(nx.NetworkXException):
+ G = nx.Graph()
+ G.add_node(0)
+ G.add_node(1)
+ nx.second_order_centrality(G)
+
+
+def test_non_negative_edge_weights():
+ with pytest.raises(nx.NetworkXException):
+ G = nx.path_graph(2)
+ G.add_edge(0, 1, weight=-1)
+ nx.second_order_centrality(G)
+
+
+def test_weight_attribute():
+ G = nx.Graph()
+ G.add_weighted_edges_from([(0, 1, 1.0), (1, 2, 3.5)], weight="w")
+ expected = {0: 3.431, 1: 3.082, 2: 5.612}
+ b = nx.second_order_centrality(G, weight="w")
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(expected[n], abs=1e-2)
+
+
+def test_one_node_graph():
+ """Second order centrality: single node"""
+ G = nx.Graph()
+ G.add_node(0)
+ G.add_edge(0, 0)
+ assert nx.second_order_centrality(G)[0] == 0
+
+
+def test_P3():
+ """Second order centrality: line graph, as defined in paper"""
+ G = nx.path_graph(3)
+ b_answer = {0: 3.741, 1: 1.414, 2: 3.741}
+
+ b = nx.second_order_centrality(G)
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
+
+
+def test_K3():
+ """Second order centrality: complete graph, as defined in paper"""
+ G = nx.complete_graph(3)
+ b_answer = {0: 1.414, 1: 1.414, 2: 1.414}
+
+ b = nx.second_order_centrality(G)
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
+
+
+def test_ring_graph():
+ """Second order centrality: ring graph, as defined in paper"""
+ G = nx.cycle_graph(5)
+ b_answer = {0: 4.472, 1: 4.472, 2: 4.472, 3: 4.472, 4: 4.472}
+
+ b = nx.second_order_centrality(G)
+
+ for n in sorted(G):
+ assert b[n] == pytest.approx(b_answer[n], abs=1e-2)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py
new file mode 100644
index 00000000..71092751
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_subgraph.py
@@ -0,0 +1,110 @@
+import pytest
+
+pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+import networkx as nx
+from networkx.algorithms.centrality.subgraph_alg import (
+ communicability_betweenness_centrality,
+ estrada_index,
+ subgraph_centrality,
+ subgraph_centrality_exp,
+)
+
+
+class TestSubgraph:
+ def test_subgraph_centrality(self):
+ answer = {0: 1.5430806348152433, 1: 1.5430806348152433}
+ result = subgraph_centrality(nx.path_graph(2))
+ for k, v in result.items():
+ assert answer[k] == pytest.approx(v, abs=1e-7)
+
+ answer1 = {
+ "1": 1.6445956054135658,
+ "Albert": 2.4368257358712189,
+ "Aric": 2.4368257358712193,
+ "Dan": 3.1306328496328168,
+ "Franck": 2.3876142275231915,
+ }
+ G1 = nx.Graph(
+ [
+ ("Franck", "Aric"),
+ ("Aric", "Dan"),
+ ("Dan", "Albert"),
+ ("Albert", "Franck"),
+ ("Dan", "1"),
+ ("Franck", "Albert"),
+ ]
+ )
+ result1 = subgraph_centrality(G1)
+ for k, v in result1.items():
+ assert answer1[k] == pytest.approx(v, abs=1e-7)
+ result1 = subgraph_centrality_exp(G1)
+ for k, v in result1.items():
+ assert answer1[k] == pytest.approx(v, abs=1e-7)
+
+ def test_subgraph_centrality_big_graph(self):
+ g199 = nx.complete_graph(199)
+ g200 = nx.complete_graph(200)
+
+ comm199 = nx.subgraph_centrality(g199)
+ comm199_exp = nx.subgraph_centrality_exp(g199)
+
+ comm200 = nx.subgraph_centrality(g200)
+ comm200_exp = nx.subgraph_centrality_exp(g200)
+
+ def test_communicability_betweenness_centrality_small(self):
+ result = communicability_betweenness_centrality(nx.path_graph(2))
+ assert result == {0: 0, 1: 0}
+
+ result = communicability_betweenness_centrality(nx.path_graph(1))
+ assert result == {0: 0}
+
+ result = communicability_betweenness_centrality(nx.path_graph(0))
+ assert result == {}
+
+ answer = {0: 0.1411224421177313, 1: 1.0, 2: 0.1411224421177313}
+ result = communicability_betweenness_centrality(nx.path_graph(3))
+ for k, v in result.items():
+ assert answer[k] == pytest.approx(v, abs=1e-7)
+
+ result = communicability_betweenness_centrality(nx.complete_graph(3))
+ for k, v in result.items():
+ assert 0.49786143366223296 == pytest.approx(v, abs=1e-7)
+
+ def test_communicability_betweenness_centrality(self):
+ answer = {
+ 0: 0.07017447951484615,
+ 1: 0.71565598701107991,
+ 2: 0.71565598701107991,
+ 3: 0.07017447951484615,
+ }
+ result = communicability_betweenness_centrality(nx.path_graph(4))
+ for k, v in result.items():
+ assert answer[k] == pytest.approx(v, abs=1e-7)
+
+ answer1 = {
+ "1": 0.060039074193949521,
+ "Albert": 0.315470761661372,
+ "Aric": 0.31547076166137211,
+ "Dan": 0.68297778678316201,
+ "Franck": 0.21977926617449497,
+ }
+ G1 = nx.Graph(
+ [
+ ("Franck", "Aric"),
+ ("Aric", "Dan"),
+ ("Dan", "Albert"),
+ ("Albert", "Franck"),
+ ("Dan", "1"),
+ ("Franck", "Albert"),
+ ]
+ )
+ result1 = communicability_betweenness_centrality(G1)
+ for k, v in result1.items():
+ assert answer1[k] == pytest.approx(v, abs=1e-7)
+
+ def test_estrada_index(self):
+ answer = 1041.2470334195475
+ result = estrada_index(nx.karate_club_graph())
+ assert answer == pytest.approx(result, abs=1e-7)
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)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_voterank.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_voterank.py
new file mode 100644
index 00000000..a5cfb610
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests/test_voterank.py
@@ -0,0 +1,64 @@
+"""
+Unit tests for VoteRank.
+"""
+
+import networkx as nx
+
+
+class TestVoteRankCentrality:
+ # Example Graph present in reference paper
+ def test_voterank_centrality_1(self):
+ G = nx.Graph()
+ G.add_edges_from(
+ [
+ (7, 8),
+ (7, 5),
+ (7, 9),
+ (5, 0),
+ (0, 1),
+ (0, 2),
+ (0, 3),
+ (0, 4),
+ (1, 6),
+ (2, 6),
+ (3, 6),
+ (4, 6),
+ ]
+ )
+ assert [0, 7, 6] == nx.voterank(G)
+
+ def test_voterank_emptygraph(self):
+ G = nx.Graph()
+ assert [] == nx.voterank(G)
+
+ # Graph unit test
+ def test_voterank_centrality_2(self):
+ G = nx.florentine_families_graph()
+ d = nx.voterank(G, 4)
+ exact = ["Medici", "Strozzi", "Guadagni", "Castellani"]
+ assert exact == d
+
+ # DiGraph unit test
+ def test_voterank_centrality_3(self):
+ G = nx.gnc_graph(10, seed=7)
+ d = nx.voterank(G, 4)
+ exact = [3, 6, 8]
+ assert exact == d
+
+ # MultiGraph unit test
+ def test_voterank_centrality_4(self):
+ G = nx.MultiGraph()
+ G.add_edges_from(
+ [(0, 1), (0, 1), (1, 2), (2, 5), (2, 5), (5, 6), (5, 6), (2, 4), (4, 3)]
+ )
+ exact = [2, 1, 5, 4]
+ assert exact == nx.voterank(G)
+
+ # MultiDiGraph unit test
+ def test_voterank_centrality_5(self):
+ G = nx.MultiDiGraph()
+ G.add_edges_from(
+ [(0, 1), (0, 1), (1, 2), (2, 5), (2, 5), (5, 6), (5, 6), (2, 4), (4, 3)]
+ )
+ exact = [2, 0, 5, 4]
+ assert exact == nx.voterank(G)