about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/centrality/tests
diff options
context:
space:
mode:
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)