aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/generators/tests
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/networkx/generators/tests
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/generators/tests')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_atlas.py75
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_classic.py640
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_cographs.py18
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_community.py362
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_degree_seq.py230
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_directed.py163
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_duplication.py103
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_ego.py39
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_expanders.py162
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_geometric.py488
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_harary_graph.py133
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_internet_as_graphs.py176
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_intersection.py28
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_interval_graph.py144
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_joint_degree_seq.py125
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_lattice.py246
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_line.py309
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_mycielski.py30
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_nonisomorphic_trees.py68
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_clustered.py33
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_graphs.py478
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_small.py208
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_spectral_graph_forge.py49
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_stochastic.py72
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_sudoku.py92
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_time_series.py64
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_trees.py195
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/generators/tests/test_triads.py15
29 files changed, 4745 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/__init__.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_atlas.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_atlas.py
new file mode 100644
index 00000000..add4741c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_atlas.py
@@ -0,0 +1,75 @@
+from itertools import groupby
+
+import pytest
+
+import networkx as nx
+from networkx import graph_atlas, graph_atlas_g
+from networkx.generators.atlas import NUM_GRAPHS
+from networkx.utils import edges_equal, nodes_equal, pairwise
+
+
+class TestAtlasGraph:
+ """Unit tests for the :func:`~networkx.graph_atlas` function."""
+
+ def test_index_too_small(self):
+ with pytest.raises(ValueError):
+ graph_atlas(-1)
+
+ def test_index_too_large(self):
+ with pytest.raises(ValueError):
+ graph_atlas(NUM_GRAPHS)
+
+ def test_graph(self):
+ G = graph_atlas(6)
+ assert nodes_equal(G.nodes(), range(3))
+ assert edges_equal(G.edges(), [(0, 1), (0, 2)])
+
+
+class TestAtlasGraphG:
+ """Unit tests for the :func:`~networkx.graph_atlas_g` function."""
+
+ @classmethod
+ def setup_class(cls):
+ cls.GAG = graph_atlas_g()
+
+ def test_sizes(self):
+ G = self.GAG[0]
+ assert G.number_of_nodes() == 0
+ assert G.number_of_edges() == 0
+
+ G = self.GAG[7]
+ assert G.number_of_nodes() == 3
+ assert G.number_of_edges() == 3
+
+ def test_names(self):
+ for i, G in enumerate(self.GAG):
+ assert int(G.name[1:]) == i
+
+ def test_nondecreasing_nodes(self):
+ # check for nondecreasing number of nodes
+ for n1, n2 in pairwise(map(len, self.GAG)):
+ assert n2 <= n1 + 1
+
+ def test_nondecreasing_edges(self):
+ # check for nondecreasing number of edges (for fixed number of
+ # nodes)
+ for n, group in groupby(self.GAG, key=nx.number_of_nodes):
+ for m1, m2 in pairwise(map(nx.number_of_edges, group)):
+ assert m2 <= m1 + 1
+
+ def test_nondecreasing_degree_sequence(self):
+ # Check for lexicographically nondecreasing degree sequences
+ # (for fixed number of nodes and edges).
+ #
+ # There are three exceptions to this rule in the order given in
+ # the "Atlas of Graphs" book, so we need to manually exclude
+ # those.
+ exceptions = [("G55", "G56"), ("G1007", "G1008"), ("G1012", "G1013")]
+ for n, group in groupby(self.GAG, key=nx.number_of_nodes):
+ for m, group in groupby(group, key=nx.number_of_edges):
+ for G1, G2 in pairwise(group):
+ if (G1.name, G2.name) in exceptions:
+ continue
+ d1 = sorted(d for v, d in G1.degree())
+ d2 = sorted(d for v, d in G2.degree())
+ assert d1 <= d2
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_classic.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_classic.py
new file mode 100644
index 00000000..9353c7f5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_classic.py
@@ -0,0 +1,640 @@
+"""
+====================
+Generators - Classic
+====================
+
+Unit tests for various classic graph generators in generators/classic.py
+"""
+
+import itertools
+import typing
+
+import pytest
+
+import networkx as nx
+from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic
+from networkx.utils import edges_equal, nodes_equal
+
+is_isomorphic = graph_could_be_isomorphic
+
+
+class TestGeneratorClassic:
+ def test_balanced_tree(self):
+ # balanced_tree(r,h) is a tree with (r**(h+1)-1)/(r-1) edges
+ for r, h in [(2, 2), (3, 3), (6, 2)]:
+ t = nx.balanced_tree(r, h)
+ order = t.order()
+ assert order == (r ** (h + 1) - 1) / (r - 1)
+ assert nx.is_connected(t)
+ assert t.size() == order - 1
+ dh = nx.degree_histogram(t)
+ assert dh[0] == 0 # no nodes of 0
+ assert dh[1] == r**h # nodes of degree 1 are leaves
+ assert dh[r] == 1 # root is degree r
+ assert dh[r + 1] == order - r**h - 1 # everyone else is degree r+1
+ assert len(dh) == r + 2
+
+ def test_balanced_tree_star(self):
+ # balanced_tree(r,1) is the r-star
+ t = nx.balanced_tree(r=2, h=1)
+ assert is_isomorphic(t, nx.star_graph(2))
+ t = nx.balanced_tree(r=5, h=1)
+ assert is_isomorphic(t, nx.star_graph(5))
+ t = nx.balanced_tree(r=10, h=1)
+ assert is_isomorphic(t, nx.star_graph(10))
+
+ def test_balanced_tree_path(self):
+ """Tests that the balanced tree with branching factor one is the
+ path graph.
+
+ """
+ # A tree of height four has five levels.
+ T = nx.balanced_tree(1, 4)
+ P = nx.path_graph(5)
+ assert is_isomorphic(T, P)
+
+ def test_full_rary_tree(self):
+ r = 2
+ n = 9
+ t = nx.full_rary_tree(r, n)
+ assert t.order() == n
+ assert nx.is_connected(t)
+ dh = nx.degree_histogram(t)
+ assert dh[0] == 0 # no nodes of 0
+ assert dh[1] == 5 # nodes of degree 1 are leaves
+ assert dh[r] == 1 # root is degree r
+ assert dh[r + 1] == 9 - 5 - 1 # everyone else is degree r+1
+ assert len(dh) == r + 2
+
+ def test_full_rary_tree_balanced(self):
+ t = nx.full_rary_tree(2, 15)
+ th = nx.balanced_tree(2, 3)
+ assert is_isomorphic(t, th)
+
+ def test_full_rary_tree_path(self):
+ t = nx.full_rary_tree(1, 10)
+ assert is_isomorphic(t, nx.path_graph(10))
+
+ def test_full_rary_tree_empty(self):
+ t = nx.full_rary_tree(0, 10)
+ assert is_isomorphic(t, nx.empty_graph(10))
+ t = nx.full_rary_tree(3, 0)
+ assert is_isomorphic(t, nx.empty_graph(0))
+
+ def test_full_rary_tree_3_20(self):
+ t = nx.full_rary_tree(3, 20)
+ assert t.order() == 20
+
+ def test_barbell_graph(self):
+ # number of nodes = 2*m1 + m2 (2 m1-complete graphs + m2-path + 2 edges)
+ # number of edges = 2*(nx.number_of_edges(m1-complete graph) + m2 + 1
+ m1 = 3
+ m2 = 5
+ b = nx.barbell_graph(m1, m2)
+ assert nx.number_of_nodes(b) == 2 * m1 + m2
+ assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
+
+ m1 = 4
+ m2 = 10
+ b = nx.barbell_graph(m1, m2)
+ assert nx.number_of_nodes(b) == 2 * m1 + m2
+ assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
+
+ m1 = 3
+ m2 = 20
+ b = nx.barbell_graph(m1, m2)
+ assert nx.number_of_nodes(b) == 2 * m1 + m2
+ assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
+
+ # Raise NetworkXError if m1<2
+ m1 = 1
+ m2 = 20
+ pytest.raises(nx.NetworkXError, nx.barbell_graph, m1, m2)
+
+ # Raise NetworkXError if m2<0
+ m1 = 5
+ m2 = -2
+ pytest.raises(nx.NetworkXError, nx.barbell_graph, m1, m2)
+
+ # nx.barbell_graph(2,m) = nx.path_graph(m+4)
+ m1 = 2
+ m2 = 5
+ b = nx.barbell_graph(m1, m2)
+ assert is_isomorphic(b, nx.path_graph(m2 + 4))
+
+ m1 = 2
+ m2 = 10
+ b = nx.barbell_graph(m1, m2)
+ assert is_isomorphic(b, nx.path_graph(m2 + 4))
+
+ m1 = 2
+ m2 = 20
+ b = nx.barbell_graph(m1, m2)
+ assert is_isomorphic(b, nx.path_graph(m2 + 4))
+
+ pytest.raises(
+ nx.NetworkXError, nx.barbell_graph, m1, m2, create_using=nx.DiGraph()
+ )
+
+ mb = nx.barbell_graph(m1, m2, create_using=nx.MultiGraph())
+ assert edges_equal(mb.edges(), b.edges())
+
+ def test_binomial_tree(self):
+ graphs = (None, nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
+ for create_using in graphs:
+ for n in range(4):
+ b = nx.binomial_tree(n, create_using)
+ assert nx.number_of_nodes(b) == 2**n
+ assert nx.number_of_edges(b) == (2**n - 1)
+
+ def test_complete_graph(self):
+ # complete_graph(m) is a connected graph with
+ # m nodes and m*(m+1)/2 edges
+ for m in [0, 1, 3, 5]:
+ g = nx.complete_graph(m)
+ assert nx.number_of_nodes(g) == m
+ assert nx.number_of_edges(g) == m * (m - 1) // 2
+
+ mg = nx.complete_graph(m, create_using=nx.MultiGraph)
+ assert edges_equal(mg.edges(), g.edges())
+
+ g = nx.complete_graph("abc")
+ assert nodes_equal(g.nodes(), ["a", "b", "c"])
+ assert g.size() == 3
+
+ # creates a self-loop... should it? <backward compatible says yes>
+ g = nx.complete_graph("abcb")
+ assert nodes_equal(g.nodes(), ["a", "b", "c"])
+ assert g.size() == 4
+
+ g = nx.complete_graph("abcb", create_using=nx.MultiGraph)
+ assert nodes_equal(g.nodes(), ["a", "b", "c"])
+ assert g.size() == 6
+
+ def test_complete_digraph(self):
+ # complete_graph(m) is a connected graph with
+ # m nodes and m*(m+1)/2 edges
+ for m in [0, 1, 3, 5]:
+ g = nx.complete_graph(m, create_using=nx.DiGraph)
+ assert nx.number_of_nodes(g) == m
+ assert nx.number_of_edges(g) == m * (m - 1)
+
+ g = nx.complete_graph("abc", create_using=nx.DiGraph)
+ assert len(g) == 3
+ assert g.size() == 6
+ assert g.is_directed()
+
+ def test_circular_ladder_graph(self):
+ G = nx.circular_ladder_graph(5)
+ pytest.raises(
+ nx.NetworkXError, nx.circular_ladder_graph, 5, create_using=nx.DiGraph
+ )
+ mG = nx.circular_ladder_graph(5, create_using=nx.MultiGraph)
+ assert edges_equal(mG.edges(), G.edges())
+
+ def test_circulant_graph(self):
+ # Ci_n(1) is the cycle graph for all n
+ Ci6_1 = nx.circulant_graph(6, [1])
+ C6 = nx.cycle_graph(6)
+ assert edges_equal(Ci6_1.edges(), C6.edges())
+
+ # Ci_n(1, 2, ..., n div 2) is the complete graph for all n
+ Ci7 = nx.circulant_graph(7, [1, 2, 3])
+ K7 = nx.complete_graph(7)
+ assert edges_equal(Ci7.edges(), K7.edges())
+
+ # Ci_6(1, 3) is K_3,3 i.e. the utility graph
+ Ci6_1_3 = nx.circulant_graph(6, [1, 3])
+ K3_3 = nx.complete_bipartite_graph(3, 3)
+ assert is_isomorphic(Ci6_1_3, K3_3)
+
+ def test_cycle_graph(self):
+ G = nx.cycle_graph(4)
+ assert edges_equal(G.edges(), [(0, 1), (0, 3), (1, 2), (2, 3)])
+ mG = nx.cycle_graph(4, create_using=nx.MultiGraph)
+ assert edges_equal(mG.edges(), [(0, 1), (0, 3), (1, 2), (2, 3)])
+ G = nx.cycle_graph(4, create_using=nx.DiGraph)
+ assert not G.has_edge(2, 1)
+ assert G.has_edge(1, 2)
+ assert G.is_directed()
+
+ G = nx.cycle_graph("abc")
+ assert len(G) == 3
+ assert G.size() == 3
+ G = nx.cycle_graph("abcb")
+ assert len(G) == 3
+ assert G.size() == 2
+ g = nx.cycle_graph("abc", nx.DiGraph)
+ assert len(g) == 3
+ assert g.size() == 3
+ assert g.is_directed()
+ g = nx.cycle_graph("abcb", nx.DiGraph)
+ assert len(g) == 3
+ assert g.size() == 4
+
+ def test_dorogovtsev_goltsev_mendes_graph(self):
+ G = nx.dorogovtsev_goltsev_mendes_graph(0)
+ assert edges_equal(G.edges(), [(0, 1)])
+ assert nodes_equal(list(G), [0, 1])
+ G = nx.dorogovtsev_goltsev_mendes_graph(1)
+ assert edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
+ assert nx.average_clustering(G) == 1.0
+ assert nx.average_shortest_path_length(G) == 1.0
+ assert sorted(nx.triangles(G).values()) == [1, 1, 1]
+ assert nx.is_planar(G)
+ G = nx.dorogovtsev_goltsev_mendes_graph(2)
+ assert nx.number_of_nodes(G) == 6
+ assert nx.number_of_edges(G) == 9
+ assert nx.average_clustering(G) == 0.75
+ assert nx.average_shortest_path_length(G) == 1.4
+ assert nx.is_planar(G)
+ G = nx.dorogovtsev_goltsev_mendes_graph(10)
+ assert nx.number_of_nodes(G) == 29526
+ assert nx.number_of_edges(G) == 59049
+ assert G.degree(0) == 1024
+ assert G.degree(1) == 1024
+ assert G.degree(2) == 1024
+
+ with pytest.raises(nx.NetworkXError, match=r"n must be greater than"):
+ nx.dorogovtsev_goltsev_mendes_graph(-1)
+ with pytest.raises(nx.NetworkXError, match=r"directed graph not supported"):
+ nx.dorogovtsev_goltsev_mendes_graph(7, create_using=nx.DiGraph)
+ with pytest.raises(nx.NetworkXError, match=r"multigraph not supported"):
+ nx.dorogovtsev_goltsev_mendes_graph(7, create_using=nx.MultiGraph)
+ with pytest.raises(nx.NetworkXError):
+ nx.dorogovtsev_goltsev_mendes_graph(7, create_using=nx.MultiDiGraph)
+
+ def test_create_using(self):
+ G = nx.empty_graph()
+ assert isinstance(G, nx.Graph)
+ pytest.raises(TypeError, nx.empty_graph, create_using=0.0)
+ pytest.raises(TypeError, nx.empty_graph, create_using="Graph")
+
+ G = nx.empty_graph(create_using=nx.MultiGraph)
+ assert isinstance(G, nx.MultiGraph)
+ G = nx.empty_graph(create_using=nx.DiGraph)
+ assert isinstance(G, nx.DiGraph)
+
+ G = nx.empty_graph(create_using=nx.DiGraph, default=nx.MultiGraph)
+ assert isinstance(G, nx.DiGraph)
+ G = nx.empty_graph(create_using=None, default=nx.MultiGraph)
+ assert isinstance(G, nx.MultiGraph)
+ G = nx.empty_graph(default=nx.MultiGraph)
+ assert isinstance(G, nx.MultiGraph)
+
+ G = nx.path_graph(5)
+ H = nx.empty_graph(create_using=G)
+ assert not H.is_multigraph()
+ assert not H.is_directed()
+ assert len(H) == 0
+ assert G is H
+
+ H = nx.empty_graph(create_using=nx.MultiGraph())
+ assert H.is_multigraph()
+ assert not H.is_directed()
+ assert G is not H
+
+ # test for subclasses that also use typing.Protocol. See gh-6243
+ class Mixin(typing.Protocol):
+ pass
+
+ class MyGraph(Mixin, nx.DiGraph):
+ pass
+
+ G = nx.empty_graph(create_using=MyGraph)
+
+ def test_empty_graph(self):
+ G = nx.empty_graph()
+ assert nx.number_of_nodes(G) == 0
+ G = nx.empty_graph(42)
+ assert nx.number_of_nodes(G) == 42
+ assert nx.number_of_edges(G) == 0
+
+ G = nx.empty_graph("abc")
+ assert len(G) == 3
+ assert G.size() == 0
+
+ # create empty digraph
+ G = nx.empty_graph(42, create_using=nx.DiGraph(name="duh"))
+ assert nx.number_of_nodes(G) == 42
+ assert nx.number_of_edges(G) == 0
+ assert isinstance(G, nx.DiGraph)
+
+ # create empty multigraph
+ G = nx.empty_graph(42, create_using=nx.MultiGraph(name="duh"))
+ assert nx.number_of_nodes(G) == 42
+ assert nx.number_of_edges(G) == 0
+ assert isinstance(G, nx.MultiGraph)
+
+ # create empty graph from another
+ pete = nx.petersen_graph()
+ G = nx.empty_graph(42, create_using=pete)
+ assert nx.number_of_nodes(G) == 42
+ assert nx.number_of_edges(G) == 0
+ assert isinstance(G, nx.Graph)
+
+ def test_ladder_graph(self):
+ for i, G in [
+ (0, nx.empty_graph(0)),
+ (1, nx.path_graph(2)),
+ (2, nx.hypercube_graph(2)),
+ (10, nx.grid_graph([2, 10])),
+ ]:
+ assert is_isomorphic(nx.ladder_graph(i), G)
+
+ pytest.raises(nx.NetworkXError, nx.ladder_graph, 2, create_using=nx.DiGraph)
+
+ g = nx.ladder_graph(2)
+ mg = nx.ladder_graph(2, create_using=nx.MultiGraph)
+ assert edges_equal(mg.edges(), g.edges())
+
+ @pytest.mark.parametrize(("m", "n"), [(3, 5), (4, 10), (3, 20)])
+ def test_lollipop_graph_right_sizes(self, m, n):
+ G = nx.lollipop_graph(m, n)
+ assert nx.number_of_nodes(G) == m + n
+ assert nx.number_of_edges(G) == m * (m - 1) / 2 + n
+
+ @pytest.mark.parametrize(("m", "n"), [("ab", ""), ("abc", "defg")])
+ def test_lollipop_graph_size_node_sequence(self, m, n):
+ G = nx.lollipop_graph(m, n)
+ assert nx.number_of_nodes(G) == len(m) + len(n)
+ assert nx.number_of_edges(G) == len(m) * (len(m) - 1) / 2 + len(n)
+
+ def test_lollipop_graph_exceptions(self):
+ # Raise NetworkXError if m<2
+ pytest.raises(nx.NetworkXError, nx.lollipop_graph, -1, 2)
+ pytest.raises(nx.NetworkXError, nx.lollipop_graph, 1, 20)
+ pytest.raises(nx.NetworkXError, nx.lollipop_graph, "", 20)
+ pytest.raises(nx.NetworkXError, nx.lollipop_graph, "a", 20)
+
+ # Raise NetworkXError if n<0
+ pytest.raises(nx.NetworkXError, nx.lollipop_graph, 5, -2)
+
+ # raise NetworkXError if create_using is directed
+ with pytest.raises(nx.NetworkXError):
+ nx.lollipop_graph(2, 20, create_using=nx.DiGraph)
+ with pytest.raises(nx.NetworkXError):
+ nx.lollipop_graph(2, 20, create_using=nx.MultiDiGraph)
+
+ @pytest.mark.parametrize(("m", "n"), [(2, 0), (2, 5), (2, 10), ("ab", 20)])
+ def test_lollipop_graph_same_as_path_when_m1_is_2(self, m, n):
+ G = nx.lollipop_graph(m, n)
+ assert is_isomorphic(G, nx.path_graph(n + 2))
+
+ def test_lollipop_graph_for_multigraph(self):
+ G = nx.lollipop_graph(5, 20)
+ MG = nx.lollipop_graph(5, 20, create_using=nx.MultiGraph)
+ assert edges_equal(MG.edges(), G.edges())
+
+ @pytest.mark.parametrize(
+ ("m", "n"),
+ [(4, "abc"), ("abcd", 3), ([1, 2, 3, 4], "abc"), ("abcd", [1, 2, 3])],
+ )
+ def test_lollipop_graph_mixing_input_types(self, m, n):
+ expected = nx.compose(nx.complete_graph(4), nx.path_graph(range(100, 103)))
+ expected.add_edge(0, 100) # Connect complete graph and path graph
+ assert is_isomorphic(nx.lollipop_graph(m, n), expected)
+
+ def test_lollipop_graph_non_builtin_ints(self):
+ np = pytest.importorskip("numpy")
+ G = nx.lollipop_graph(np.int32(4), np.int64(3))
+ expected = nx.compose(nx.complete_graph(4), nx.path_graph(range(100, 103)))
+ expected.add_edge(0, 100) # Connect complete graph and path graph
+ assert is_isomorphic(G, expected)
+
+ def test_null_graph(self):
+ assert nx.number_of_nodes(nx.null_graph()) == 0
+
+ def test_path_graph(self):
+ p = nx.path_graph(0)
+ assert is_isomorphic(p, nx.null_graph())
+
+ p = nx.path_graph(1)
+ assert is_isomorphic(p, nx.empty_graph(1))
+
+ p = nx.path_graph(10)
+ assert nx.is_connected(p)
+ assert sorted(d for n, d in p.degree()) == [1, 1, 2, 2, 2, 2, 2, 2, 2, 2]
+ assert p.order() - 1 == p.size()
+
+ dp = nx.path_graph(3, create_using=nx.DiGraph)
+ assert dp.has_edge(0, 1)
+ assert not dp.has_edge(1, 0)
+
+ mp = nx.path_graph(10, create_using=nx.MultiGraph)
+ assert edges_equal(mp.edges(), p.edges())
+
+ G = nx.path_graph("abc")
+ assert len(G) == 3
+ assert G.size() == 2
+ G = nx.path_graph("abcb")
+ assert len(G) == 3
+ assert G.size() == 2
+ g = nx.path_graph("abc", nx.DiGraph)
+ assert len(g) == 3
+ assert g.size() == 2
+ assert g.is_directed()
+ g = nx.path_graph("abcb", nx.DiGraph)
+ assert len(g) == 3
+ assert g.size() == 3
+
+ G = nx.path_graph((1, 2, 3, 2, 4))
+ assert G.has_edge(2, 4)
+
+ def test_star_graph(self):
+ assert is_isomorphic(nx.star_graph(""), nx.empty_graph(0))
+ assert is_isomorphic(nx.star_graph([]), nx.empty_graph(0))
+ assert is_isomorphic(nx.star_graph(0), nx.empty_graph(1))
+ assert is_isomorphic(nx.star_graph(1), nx.path_graph(2))
+ assert is_isomorphic(nx.star_graph(2), nx.path_graph(3))
+ assert is_isomorphic(nx.star_graph(5), nx.complete_bipartite_graph(1, 5))
+
+ s = nx.star_graph(10)
+ assert sorted(d for n, d in s.degree()) == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10]
+
+ pytest.raises(nx.NetworkXError, nx.star_graph, 10, create_using=nx.DiGraph)
+
+ ms = nx.star_graph(10, create_using=nx.MultiGraph)
+ assert edges_equal(ms.edges(), s.edges())
+
+ G = nx.star_graph("abc")
+ assert len(G) == 3
+ assert G.size() == 2
+
+ G = nx.star_graph("abcb")
+ assert len(G) == 3
+ assert G.size() == 2
+ G = nx.star_graph("abcb", create_using=nx.MultiGraph)
+ assert len(G) == 3
+ assert G.size() == 3
+
+ G = nx.star_graph("abcdefg")
+ assert len(G) == 7
+ assert G.size() == 6
+
+ def test_non_int_integers_for_star_graph(self):
+ np = pytest.importorskip("numpy")
+ G = nx.star_graph(np.int32(3))
+ assert len(G) == 4
+ assert G.size() == 3
+
+ @pytest.mark.parametrize(("m", "n"), [(3, 0), (3, 5), (4, 10), (3, 20)])
+ def test_tadpole_graph_right_sizes(self, m, n):
+ G = nx.tadpole_graph(m, n)
+ assert nx.number_of_nodes(G) == m + n
+ assert nx.number_of_edges(G) == m + n - (m == 2)
+
+ @pytest.mark.parametrize(("m", "n"), [("ab", ""), ("ab", "c"), ("abc", "defg")])
+ def test_tadpole_graph_size_node_sequences(self, m, n):
+ G = nx.tadpole_graph(m, n)
+ assert nx.number_of_nodes(G) == len(m) + len(n)
+ assert nx.number_of_edges(G) == len(m) + len(n) - (len(m) == 2)
+
+ def test_tadpole_graph_exceptions(self):
+ # Raise NetworkXError if m<2
+ pytest.raises(nx.NetworkXError, nx.tadpole_graph, -1, 3)
+ pytest.raises(nx.NetworkXError, nx.tadpole_graph, 0, 3)
+ pytest.raises(nx.NetworkXError, nx.tadpole_graph, 1, 3)
+
+ # Raise NetworkXError if n<0
+ pytest.raises(nx.NetworkXError, nx.tadpole_graph, 5, -2)
+
+ # Raise NetworkXError for digraphs
+ with pytest.raises(nx.NetworkXError):
+ nx.tadpole_graph(2, 20, create_using=nx.DiGraph)
+ with pytest.raises(nx.NetworkXError):
+ nx.tadpole_graph(2, 20, create_using=nx.MultiDiGraph)
+
+ @pytest.mark.parametrize(("m", "n"), [(2, 0), (2, 5), (2, 10), ("ab", 20)])
+ def test_tadpole_graph_same_as_path_when_m_is_2(self, m, n):
+ G = nx.tadpole_graph(m, n)
+ assert is_isomorphic(G, nx.path_graph(n + 2))
+
+ @pytest.mark.parametrize("m", [4, 7])
+ def test_tadpole_graph_same_as_cycle_when_m2_is_0(self, m):
+ G = nx.tadpole_graph(m, 0)
+ assert is_isomorphic(G, nx.cycle_graph(m))
+
+ def test_tadpole_graph_for_multigraph(self):
+ G = nx.tadpole_graph(5, 20)
+ MG = nx.tadpole_graph(5, 20, create_using=nx.MultiGraph)
+ assert edges_equal(MG.edges(), G.edges())
+
+ @pytest.mark.parametrize(
+ ("m", "n"),
+ [(4, "abc"), ("abcd", 3), ([1, 2, 3, 4], "abc"), ("abcd", [1, 2, 3])],
+ )
+ def test_tadpole_graph_mixing_input_types(self, m, n):
+ expected = nx.compose(nx.cycle_graph(4), nx.path_graph(range(100, 103)))
+ expected.add_edge(0, 100) # Connect cycle and path
+ assert is_isomorphic(nx.tadpole_graph(m, n), expected)
+
+ def test_tadpole_graph_non_builtin_integers(self):
+ np = pytest.importorskip("numpy")
+ G = nx.tadpole_graph(np.int32(4), np.int64(3))
+ expected = nx.compose(nx.cycle_graph(4), nx.path_graph(range(100, 103)))
+ expected.add_edge(0, 100) # Connect cycle and path
+ assert is_isomorphic(G, expected)
+
+ def test_trivial_graph(self):
+ assert nx.number_of_nodes(nx.trivial_graph()) == 1
+
+ def test_turan_graph(self):
+ assert nx.number_of_edges(nx.turan_graph(13, 4)) == 63
+ assert is_isomorphic(
+ nx.turan_graph(13, 4), nx.complete_multipartite_graph(3, 4, 3, 3)
+ )
+
+ def test_wheel_graph(self):
+ for n, G in [
+ ("", nx.null_graph()),
+ (0, nx.null_graph()),
+ (1, nx.empty_graph(1)),
+ (2, nx.path_graph(2)),
+ (3, nx.complete_graph(3)),
+ (4, nx.complete_graph(4)),
+ ]:
+ g = nx.wheel_graph(n)
+ assert is_isomorphic(g, G)
+
+ g = nx.wheel_graph(10)
+ assert sorted(d for n, d in g.degree()) == [3, 3, 3, 3, 3, 3, 3, 3, 3, 9]
+
+ pytest.raises(nx.NetworkXError, nx.wheel_graph, 10, create_using=nx.DiGraph)
+
+ mg = nx.wheel_graph(10, create_using=nx.MultiGraph())
+ assert edges_equal(mg.edges(), g.edges())
+
+ G = nx.wheel_graph("abc")
+ assert len(G) == 3
+ assert G.size() == 3
+
+ G = nx.wheel_graph("abcb")
+ assert len(G) == 3
+ assert G.size() == 4
+ G = nx.wheel_graph("abcb", nx.MultiGraph)
+ assert len(G) == 3
+ assert G.size() == 6
+
+ def test_non_int_integers_for_wheel_graph(self):
+ np = pytest.importorskip("numpy")
+ G = nx.wheel_graph(np.int32(3))
+ assert len(G) == 3
+ assert G.size() == 3
+
+ def test_complete_0_partite_graph(self):
+ """Tests that the complete 0-partite graph is the null graph."""
+ G = nx.complete_multipartite_graph()
+ H = nx.null_graph()
+ assert nodes_equal(G, H)
+ assert edges_equal(G.edges(), H.edges())
+
+ def test_complete_1_partite_graph(self):
+ """Tests that the complete 1-partite graph is the empty graph."""
+ G = nx.complete_multipartite_graph(3)
+ H = nx.empty_graph(3)
+ assert nodes_equal(G, H)
+ assert edges_equal(G.edges(), H.edges())
+
+ def test_complete_2_partite_graph(self):
+ """Tests that the complete 2-partite graph is the complete bipartite
+ graph.
+
+ """
+ G = nx.complete_multipartite_graph(2, 3)
+ H = nx.complete_bipartite_graph(2, 3)
+ assert nodes_equal(G, H)
+ assert edges_equal(G.edges(), H.edges())
+
+ def test_complete_multipartite_graph(self):
+ """Tests for generating the complete multipartite graph."""
+ G = nx.complete_multipartite_graph(2, 3, 4)
+ blocks = [(0, 1), (2, 3, 4), (5, 6, 7, 8)]
+ # Within each block, no two vertices should be adjacent.
+ for block in blocks:
+ for u, v in itertools.combinations_with_replacement(block, 2):
+ assert v not in G[u]
+ assert G.nodes[u] == G.nodes[v]
+ # Across blocks, all vertices should be adjacent.
+ for block1, block2 in itertools.combinations(blocks, 2):
+ for u, v in itertools.product(block1, block2):
+ assert v in G[u]
+ assert G.nodes[u] != G.nodes[v]
+ with pytest.raises(nx.NetworkXError, match="Negative number of nodes"):
+ nx.complete_multipartite_graph(2, -3, 4)
+
+ def test_kneser_graph(self):
+ # the petersen graph is a special case of the kneser graph when n=5 and k=2
+ assert is_isomorphic(nx.kneser_graph(5, 2), nx.petersen_graph())
+
+ # when k is 1, the kneser graph returns a complete graph with n vertices
+ for i in range(1, 7):
+ assert is_isomorphic(nx.kneser_graph(i, 1), nx.complete_graph(i))
+
+ # the kneser graph of n and n-1 is the empty graph with n vertices
+ for j in range(3, 7):
+ assert is_isomorphic(nx.kneser_graph(j, j - 1), nx.empty_graph(j))
+
+ # in general the number of edges of the kneser graph is equal to
+ # (n choose k) times (n-k choose k) divided by 2
+ assert nx.number_of_edges(nx.kneser_graph(8, 3)) == 280
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_cographs.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_cographs.py
new file mode 100644
index 00000000..a71849b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_cographs.py
@@ -0,0 +1,18 @@
+"""Unit tests for the :mod:`networkx.generators.cographs` module."""
+
+import networkx as nx
+
+
+def test_random_cograph():
+ n = 3
+ G = nx.random_cograph(n)
+
+ assert len(G) == 2**n
+
+ # Every connected subgraph of G has diameter <= 2
+ if nx.is_connected(G):
+ assert nx.diameter(G) <= 2
+ else:
+ components = nx.connected_components(G)
+ for component in components:
+ assert nx.diameter(G.subgraph(component)) <= 2
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_community.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_community.py
new file mode 100644
index 00000000..2fa107f6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_community.py
@@ -0,0 +1,362 @@
+import pytest
+
+import networkx as nx
+
+
+def test_random_partition_graph():
+ G = nx.random_partition_graph([3, 3, 3], 1, 0, seed=42)
+ C = G.graph["partition"]
+ assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
+ assert len(G) == 9
+ assert len(list(G.edges())) == 9
+
+ G = nx.random_partition_graph([3, 3, 3], 0, 1)
+ C = G.graph["partition"]
+ assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
+ assert len(G) == 9
+ assert len(list(G.edges())) == 27
+
+ G = nx.random_partition_graph([3, 3, 3], 1, 0, directed=True)
+ C = G.graph["partition"]
+ assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
+ assert len(G) == 9
+ assert len(list(G.edges())) == 18
+
+ G = nx.random_partition_graph([3, 3, 3], 0, 1, directed=True)
+ C = G.graph["partition"]
+ assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
+ assert len(G) == 9
+ assert len(list(G.edges())) == 54
+
+ G = nx.random_partition_graph([1, 2, 3, 4, 5], 0.5, 0.1)
+ C = G.graph["partition"]
+ assert C == [{0}, {1, 2}, {3, 4, 5}, {6, 7, 8, 9}, {10, 11, 12, 13, 14}]
+ assert len(G) == 15
+
+ rpg = nx.random_partition_graph
+ pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 1.1, 0.1)
+ pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], -0.1, 0.1)
+ pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 0.1, 1.1)
+ pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 0.1, -0.1)
+
+
+def test_planted_partition_graph():
+ G = nx.planted_partition_graph(4, 3, 1, 0, seed=42)
+ C = G.graph["partition"]
+ assert len(C) == 4
+ assert len(G) == 12
+ assert len(list(G.edges())) == 12
+
+ G = nx.planted_partition_graph(4, 3, 0, 1)
+ C = G.graph["partition"]
+ assert len(C) == 4
+ assert len(G) == 12
+ assert len(list(G.edges())) == 54
+
+ G = nx.planted_partition_graph(10, 4, 0.5, 0.1, seed=42)
+ C = G.graph["partition"]
+ assert len(C) == 10
+ assert len(G) == 40
+
+ G = nx.planted_partition_graph(4, 3, 1, 0, directed=True)
+ C = G.graph["partition"]
+ assert len(C) == 4
+ assert len(G) == 12
+ assert len(list(G.edges())) == 24
+
+ G = nx.planted_partition_graph(4, 3, 0, 1, directed=True)
+ C = G.graph["partition"]
+ assert len(C) == 4
+ assert len(G) == 12
+ assert len(list(G.edges())) == 108
+
+ G = nx.planted_partition_graph(10, 4, 0.5, 0.1, seed=42, directed=True)
+ C = G.graph["partition"]
+ assert len(C) == 10
+ assert len(G) == 40
+
+ ppg = nx.planted_partition_graph
+ pytest.raises(nx.NetworkXError, ppg, 3, 3, 1.1, 0.1)
+ pytest.raises(nx.NetworkXError, ppg, 3, 3, -0.1, 0.1)
+ pytest.raises(nx.NetworkXError, ppg, 3, 3, 0.1, 1.1)
+ pytest.raises(nx.NetworkXError, ppg, 3, 3, 0.1, -0.1)
+
+
+def test_relaxed_caveman_graph():
+ G = nx.relaxed_caveman_graph(4, 3, 0)
+ assert len(G) == 12
+ G = nx.relaxed_caveman_graph(4, 3, 1)
+ assert len(G) == 12
+ G = nx.relaxed_caveman_graph(4, 3, 0.5)
+ assert len(G) == 12
+ G = nx.relaxed_caveman_graph(4, 3, 0.5, seed=42)
+ assert len(G) == 12
+
+
+def test_connected_caveman_graph():
+ G = nx.connected_caveman_graph(4, 3)
+ assert len(G) == 12
+
+ G = nx.connected_caveman_graph(1, 5)
+ K5 = nx.complete_graph(5)
+ K5.remove_edge(3, 4)
+ assert nx.is_isomorphic(G, K5)
+
+ # need at least 2 nodes in each clique
+ pytest.raises(nx.NetworkXError, nx.connected_caveman_graph, 4, 1)
+
+
+def test_caveman_graph():
+ G = nx.caveman_graph(4, 3)
+ assert len(G) == 12
+
+ G = nx.caveman_graph(5, 1)
+ E5 = nx.empty_graph(5)
+ assert nx.is_isomorphic(G, E5)
+
+ G = nx.caveman_graph(1, 5)
+ K5 = nx.complete_graph(5)
+ assert nx.is_isomorphic(G, K5)
+
+
+def test_gaussian_random_partition_graph():
+ G = nx.gaussian_random_partition_graph(100, 10, 10, 0.3, 0.01)
+ assert len(G) == 100
+ G = nx.gaussian_random_partition_graph(100, 10, 10, 0.3, 0.01, directed=True)
+ assert len(G) == 100
+ G = nx.gaussian_random_partition_graph(
+ 100, 10, 10, 0.3, 0.01, directed=False, seed=42
+ )
+ assert len(G) == 100
+ assert not isinstance(G, nx.DiGraph)
+ G = nx.gaussian_random_partition_graph(
+ 100, 10, 10, 0.3, 0.01, directed=True, seed=42
+ )
+ assert len(G) == 100
+ assert isinstance(G, nx.DiGraph)
+ pytest.raises(
+ nx.NetworkXError, nx.gaussian_random_partition_graph, 100, 101, 10, 1, 0
+ )
+ # Test when clusters are likely less than 1
+ G = nx.gaussian_random_partition_graph(10, 0.5, 0.5, 0.5, 0.5, seed=1)
+ assert len(G) == 10
+
+
+def test_ring_of_cliques():
+ for i in range(2, 20, 3):
+ for j in range(2, 20, 3):
+ G = nx.ring_of_cliques(i, j)
+ assert G.number_of_nodes() == i * j
+ if i != 2 or j != 1:
+ expected_num_edges = i * (((j * (j - 1)) // 2) + 1)
+ else:
+ # the edge that already exists cannot be duplicated
+ expected_num_edges = i * (((j * (j - 1)) // 2) + 1) - 1
+ assert G.number_of_edges() == expected_num_edges
+ with pytest.raises(
+ nx.NetworkXError, match="A ring of cliques must have at least two cliques"
+ ):
+ nx.ring_of_cliques(1, 5)
+ with pytest.raises(
+ nx.NetworkXError, match="The cliques must have at least two nodes"
+ ):
+ nx.ring_of_cliques(3, 0)
+
+
+def test_windmill_graph():
+ for n in range(2, 20, 3):
+ for k in range(2, 20, 3):
+ G = nx.windmill_graph(n, k)
+ assert G.number_of_nodes() == (k - 1) * n + 1
+ assert G.number_of_edges() == n * k * (k - 1) / 2
+ assert G.degree(0) == G.number_of_nodes() - 1
+ for i in range(1, G.number_of_nodes()):
+ assert G.degree(i) == k - 1
+ with pytest.raises(
+ nx.NetworkXError, match="A windmill graph must have at least two cliques"
+ ):
+ nx.windmill_graph(1, 3)
+ with pytest.raises(
+ nx.NetworkXError, match="The cliques must have at least two nodes"
+ ):
+ nx.windmill_graph(3, 0)
+
+
+def test_stochastic_block_model():
+ sizes = [75, 75, 300]
+ probs = [[0.25, 0.05, 0.02], [0.05, 0.35, 0.07], [0.02, 0.07, 0.40]]
+ G = nx.stochastic_block_model(sizes, probs, seed=0)
+ C = G.graph["partition"]
+ assert len(C) == 3
+ assert len(G) == 450
+ assert G.size() == 22160
+
+ GG = nx.stochastic_block_model(sizes, probs, range(450), seed=0)
+ assert G.nodes == GG.nodes
+
+ # Test Exceptions
+ sbm = nx.stochastic_block_model
+ badnodelist = list(range(400)) # not enough nodes to match sizes
+ badprobs1 = [[0.25, 0.05, 1.02], [0.05, 0.35, 0.07], [0.02, 0.07, 0.40]]
+ badprobs2 = [[0.25, 0.05, 0.02], [0.05, -0.35, 0.07], [0.02, 0.07, 0.40]]
+ probs_rect1 = [[0.25, 0.05, 0.02], [0.05, -0.35, 0.07]]
+ probs_rect2 = [[0.25, 0.05], [0.05, -0.35], [0.02, 0.07]]
+ asymprobs = [[0.25, 0.05, 0.01], [0.05, -0.35, 0.07], [0.02, 0.07, 0.40]]
+ pytest.raises(nx.NetworkXException, sbm, sizes, badprobs1)
+ pytest.raises(nx.NetworkXException, sbm, sizes, badprobs2)
+ pytest.raises(nx.NetworkXException, sbm, sizes, probs_rect1, directed=True)
+ pytest.raises(nx.NetworkXException, sbm, sizes, probs_rect2, directed=True)
+ pytest.raises(nx.NetworkXException, sbm, sizes, asymprobs, directed=False)
+ pytest.raises(nx.NetworkXException, sbm, sizes, probs, badnodelist)
+ nodelist = [0] + list(range(449)) # repeated node name in nodelist
+ pytest.raises(nx.NetworkXException, sbm, sizes, probs, nodelist)
+
+ # Extra keyword arguments test
+ GG = nx.stochastic_block_model(sizes, probs, seed=0, selfloops=True)
+ assert G.nodes == GG.nodes
+ GG = nx.stochastic_block_model(sizes, probs, selfloops=True, directed=True)
+ assert G.nodes == GG.nodes
+ GG = nx.stochastic_block_model(sizes, probs, seed=0, sparse=False)
+ assert G.nodes == GG.nodes
+
+
+def test_generator():
+ n = 250
+ tau1 = 3
+ tau2 = 1.5
+ mu = 0.1
+ G = nx.LFR_benchmark_graph(
+ n, tau1, tau2, mu, average_degree=5, min_community=20, seed=10
+ )
+ assert len(G) == 250
+ C = {frozenset(G.nodes[v]["community"]) for v in G}
+ assert nx.community.is_partition(G.nodes(), C)
+
+
+def test_invalid_tau1():
+ with pytest.raises(nx.NetworkXError, match="tau2 must be greater than one"):
+ n = 100
+ tau1 = 2
+ tau2 = 1
+ mu = 0.1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
+
+
+def test_invalid_tau2():
+ with pytest.raises(nx.NetworkXError, match="tau1 must be greater than one"):
+ n = 100
+ tau1 = 1
+ tau2 = 2
+ mu = 0.1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
+
+
+def test_mu_too_large():
+ with pytest.raises(nx.NetworkXError, match="mu must be in the interval \\[0, 1\\]"):
+ n = 100
+ tau1 = 2
+ tau2 = 2
+ mu = 1.1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
+
+
+def test_mu_too_small():
+ with pytest.raises(nx.NetworkXError, match="mu must be in the interval \\[0, 1\\]"):
+ n = 100
+ tau1 = 2
+ tau2 = 2
+ mu = -1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
+
+
+def test_both_degrees_none():
+ with pytest.raises(
+ nx.NetworkXError,
+ match="Must assign exactly one of min_degree and average_degree",
+ ):
+ n = 100
+ tau1 = 2
+ tau2 = 2
+ mu = 1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu)
+
+
+def test_neither_degrees_none():
+ with pytest.raises(
+ nx.NetworkXError,
+ match="Must assign exactly one of min_degree and average_degree",
+ ):
+ n = 100
+ tau1 = 2
+ tau2 = 2
+ mu = 1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, average_degree=5)
+
+
+def test_max_iters_exceeded():
+ with pytest.raises(
+ nx.ExceededMaxIterations,
+ match="Could not assign communities; try increasing min_community",
+ ):
+ n = 10
+ tau1 = 2
+ tau2 = 2
+ mu = 0.1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, max_iters=10, seed=1)
+
+
+def test_max_deg_out_of_range():
+ with pytest.raises(
+ nx.NetworkXError, match="max_degree must be in the interval \\(0, n\\]"
+ ):
+ n = 10
+ tau1 = 2
+ tau2 = 2
+ mu = 0.1
+ nx.LFR_benchmark_graph(
+ n, tau1, tau2, mu, max_degree=n + 1, max_iters=10, seed=1
+ )
+
+
+def test_max_community():
+ n = 250
+ tau1 = 3
+ tau2 = 1.5
+ mu = 0.1
+ G = nx.LFR_benchmark_graph(
+ n,
+ tau1,
+ tau2,
+ mu,
+ average_degree=5,
+ max_degree=100,
+ min_community=50,
+ max_community=200,
+ seed=10,
+ )
+ assert len(G) == 250
+ C = {frozenset(G.nodes[v]["community"]) for v in G}
+ assert nx.community.is_partition(G.nodes(), C)
+
+
+def test_powerlaw_iterations_exceeded():
+ with pytest.raises(
+ nx.ExceededMaxIterations, match="Could not create power law sequence"
+ ):
+ n = 100
+ tau1 = 2
+ tau2 = 2
+ mu = 1
+ nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, max_iters=0)
+
+
+def test_no_scipy_zeta():
+ zeta2 = 1.6449340668482264
+ assert abs(zeta2 - nx.generators.community._hurwitz_zeta(2, 1, 0.0001)) < 0.01
+
+
+def test_generate_min_degree_itr():
+ with pytest.raises(
+ nx.ExceededMaxIterations, match="Could not match average_degree"
+ ):
+ nx.generators.community._generate_min_degree(2, 2, 1, 0.01, 0)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_degree_seq.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_degree_seq.py
new file mode 100644
index 00000000..39ed59a5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_degree_seq.py
@@ -0,0 +1,230 @@
+import pytest
+
+import networkx as nx
+
+
+class TestConfigurationModel:
+ """Unit tests for the :func:`~networkx.configuration_model`
+ function.
+
+ """
+
+ def test_empty_degree_sequence(self):
+ """Tests that an empty degree sequence yields the null graph."""
+ G = nx.configuration_model([])
+ assert len(G) == 0
+
+ def test_degree_zero(self):
+ """Tests that a degree sequence of all zeros yields the empty
+ graph.
+
+ """
+ G = nx.configuration_model([0, 0, 0])
+ assert len(G) == 3
+ assert G.number_of_edges() == 0
+
+ def test_degree_sequence(self):
+ """Tests that the degree sequence of the generated graph matches
+ the input degree sequence.
+
+ """
+ deg_seq = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
+ G = nx.configuration_model(deg_seq, seed=12345678)
+ assert sorted((d for n, d in G.degree()), reverse=True) == [
+ 5,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 1,
+ 1,
+ 1,
+ ]
+ assert sorted((d for n, d in G.degree(range(len(deg_seq)))), reverse=True) == [
+ 5,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 1,
+ 1,
+ 1,
+ ]
+
+ def test_random_seed(self):
+ """Tests that each call with the same random seed generates the
+ same graph.
+
+ """
+ deg_seq = [3] * 12
+ G1 = nx.configuration_model(deg_seq, seed=1000)
+ G2 = nx.configuration_model(deg_seq, seed=1000)
+ assert nx.is_isomorphic(G1, G2)
+ G1 = nx.configuration_model(deg_seq, seed=10)
+ G2 = nx.configuration_model(deg_seq, seed=10)
+ assert nx.is_isomorphic(G1, G2)
+
+ def test_directed_disallowed(self):
+ """Tests that attempting to create a configuration model graph
+ using a directed graph yields an exception.
+
+ """
+ with pytest.raises(nx.NetworkXNotImplemented):
+ nx.configuration_model([], create_using=nx.DiGraph())
+
+ def test_odd_degree_sum(self):
+ """Tests that a degree sequence whose sum is odd yields an
+ exception.
+
+ """
+ with pytest.raises(nx.NetworkXError):
+ nx.configuration_model([1, 2])
+
+
+def test_directed_configuration_raise_unequal():
+ with pytest.raises(nx.NetworkXError):
+ zin = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1]
+ zout = [5, 3, 3, 3, 3, 2, 2, 2, 1, 2]
+ nx.directed_configuration_model(zin, zout)
+
+
+def test_directed_configuration_model():
+ G = nx.directed_configuration_model([], [], seed=0)
+ assert len(G) == 0
+
+
+def test_simple_directed_configuration_model():
+ G = nx.directed_configuration_model([1, 1], [1, 1], seed=0)
+ assert len(G) == 2
+
+
+def test_expected_degree_graph_empty():
+ # empty graph has empty degree sequence
+ deg_seq = []
+ G = nx.expected_degree_graph(deg_seq)
+ assert dict(G.degree()) == {}
+
+
+def test_expected_degree_graph():
+ # test that fixed seed delivers the same graph
+ deg_seq = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
+ G1 = nx.expected_degree_graph(deg_seq, seed=1000)
+ assert len(G1) == 12
+
+ G2 = nx.expected_degree_graph(deg_seq, seed=1000)
+ assert nx.is_isomorphic(G1, G2)
+
+ G1 = nx.expected_degree_graph(deg_seq, seed=10)
+ G2 = nx.expected_degree_graph(deg_seq, seed=10)
+ assert nx.is_isomorphic(G1, G2)
+
+
+def test_expected_degree_graph_selfloops():
+ deg_seq = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
+ G1 = nx.expected_degree_graph(deg_seq, seed=1000, selfloops=False)
+ G2 = nx.expected_degree_graph(deg_seq, seed=1000, selfloops=False)
+ assert nx.is_isomorphic(G1, G2)
+ assert len(G1) == 12
+
+
+def test_expected_degree_graph_skew():
+ deg_seq = [10, 2, 2, 2, 2]
+ G1 = nx.expected_degree_graph(deg_seq, seed=1000)
+ G2 = nx.expected_degree_graph(deg_seq, seed=1000)
+ assert nx.is_isomorphic(G1, G2)
+ assert len(G1) == 5
+
+
+def test_havel_hakimi_construction():
+ G = nx.havel_hakimi_graph([])
+ assert len(G) == 0
+
+ z = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
+ pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
+ z = ["A", 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
+ pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
+
+ z = [5, 4, 3, 3, 3, 2, 2, 2]
+ G = nx.havel_hakimi_graph(z)
+ G = nx.configuration_model(z)
+ z = [6, 5, 4, 4, 2, 1, 1, 1]
+ pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
+
+ z = [10, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2]
+
+ G = nx.havel_hakimi_graph(z)
+
+ pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z, create_using=nx.DiGraph())
+
+
+def test_directed_havel_hakimi():
+ # Test range of valid directed degree sequences
+ n, r = 100, 10
+ p = 1.0 / r
+ for i in range(r):
+ G1 = nx.erdos_renyi_graph(n, p * (i + 1), None, True)
+ din1 = [d for n, d in G1.in_degree()]
+ dout1 = [d for n, d in G1.out_degree()]
+ G2 = nx.directed_havel_hakimi_graph(din1, dout1)
+ din2 = [d for n, d in G2.in_degree()]
+ dout2 = [d for n, d in G2.out_degree()]
+ assert sorted(din1) == sorted(din2)
+ assert sorted(dout1) == sorted(dout2)
+
+ # Test non-graphical sequence
+ dout = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
+ din = [103, 102, 102, 102, 102, 102, 102, 102, 102, 102]
+ pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
+ # Test valid sequences
+ dout = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
+ din = [2, 2, 2, 2, 2, 2, 2, 2, 0, 2]
+ G2 = nx.directed_havel_hakimi_graph(din, dout)
+ dout2 = (d for n, d in G2.out_degree())
+ din2 = (d for n, d in G2.in_degree())
+ assert sorted(dout) == sorted(dout2)
+ assert sorted(din) == sorted(din2)
+ # Test unequal sums
+ din = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
+ pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
+ # Test for negative values
+ din = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -2]
+ pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
+
+
+def test_degree_sequence_tree():
+ z = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
+ G = nx.degree_sequence_tree(z)
+ assert len(G) == len(z)
+ assert len(list(G.edges())) == sum(z) / 2
+
+ pytest.raises(
+ nx.NetworkXError, nx.degree_sequence_tree, z, create_using=nx.DiGraph()
+ )
+
+ z = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
+ pytest.raises(nx.NetworkXError, nx.degree_sequence_tree, z)
+
+
+def test_random_degree_sequence_graph():
+ d = [1, 2, 2, 3]
+ G = nx.random_degree_sequence_graph(d, seed=42)
+ assert d == sorted(d for n, d in G.degree())
+
+
+def test_random_degree_sequence_graph_raise():
+ z = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
+ pytest.raises(nx.NetworkXUnfeasible, nx.random_degree_sequence_graph, z)
+
+
+def test_random_degree_sequence_large():
+ G1 = nx.fast_gnp_random_graph(100, 0.1, seed=42)
+ d1 = (d for n, d in G1.degree())
+ G2 = nx.random_degree_sequence_graph(d1, seed=42)
+ d2 = (d for n, d in G2.degree())
+ assert sorted(d1) == sorted(d2)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_directed.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_directed.py
new file mode 100644
index 00000000..8078d9f7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_directed.py
@@ -0,0 +1,163 @@
+"""Generators - Directed Graphs
+----------------------------
+"""
+
+import pytest
+
+import networkx as nx
+from networkx.classes import Graph, MultiDiGraph
+from networkx.generators.directed import (
+ gn_graph,
+ gnc_graph,
+ gnr_graph,
+ random_k_out_graph,
+ random_uniform_k_out_graph,
+ scale_free_graph,
+)
+
+
+class TestGeneratorsDirected:
+ def test_smoke_test_random_graphs(self):
+ gn_graph(100)
+ gnr_graph(100, 0.5)
+ gnc_graph(100)
+ scale_free_graph(100)
+
+ gn_graph(100, seed=42)
+ gnr_graph(100, 0.5, seed=42)
+ gnc_graph(100, seed=42)
+ scale_free_graph(100, seed=42)
+
+ def test_create_using_keyword_arguments(self):
+ pytest.raises(nx.NetworkXError, gn_graph, 100, create_using=Graph())
+ pytest.raises(nx.NetworkXError, gnr_graph, 100, 0.5, create_using=Graph())
+ pytest.raises(nx.NetworkXError, gnc_graph, 100, create_using=Graph())
+ G = gn_graph(100, seed=1)
+ MG = gn_graph(100, create_using=MultiDiGraph(), seed=1)
+ assert sorted(G.edges()) == sorted(MG.edges())
+ G = gnr_graph(100, 0.5, seed=1)
+ MG = gnr_graph(100, 0.5, create_using=MultiDiGraph(), seed=1)
+ assert sorted(G.edges()) == sorted(MG.edges())
+ G = gnc_graph(100, seed=1)
+ MG = gnc_graph(100, create_using=MultiDiGraph(), seed=1)
+ assert sorted(G.edges()) == sorted(MG.edges())
+
+ G = scale_free_graph(
+ 100,
+ alpha=0.3,
+ beta=0.4,
+ gamma=0.3,
+ delta_in=0.3,
+ delta_out=0.1,
+ initial_graph=nx.cycle_graph(4, create_using=MultiDiGraph),
+ seed=1,
+ )
+ pytest.raises(ValueError, scale_free_graph, 100, 0.5, 0.4, 0.3)
+ pytest.raises(ValueError, scale_free_graph, 100, alpha=-0.3)
+ pytest.raises(ValueError, scale_free_graph, 100, beta=-0.3)
+ pytest.raises(ValueError, scale_free_graph, 100, gamma=-0.3)
+
+ def test_parameters(self):
+ G = nx.DiGraph()
+ G.add_node(0)
+
+ def kernel(x):
+ return x
+
+ assert nx.is_isomorphic(gn_graph(1), G)
+ assert nx.is_isomorphic(gn_graph(1, kernel=kernel), G)
+ assert nx.is_isomorphic(gnc_graph(1), G)
+ assert nx.is_isomorphic(gnr_graph(1, 0.5), G)
+
+
+def test_scale_free_graph_negative_delta():
+ with pytest.raises(ValueError, match="delta_in must be >= 0."):
+ scale_free_graph(10, delta_in=-1)
+ with pytest.raises(ValueError, match="delta_out must be >= 0."):
+ scale_free_graph(10, delta_out=-1)
+
+
+def test_non_numeric_ordering():
+ G = MultiDiGraph([("a", "b"), ("b", "c"), ("c", "a")])
+ s = scale_free_graph(3, initial_graph=G)
+ assert len(s) == 3
+ assert len(s.edges) == 3
+
+
+@pytest.mark.parametrize("ig", (nx.Graph(), nx.DiGraph([(0, 1)])))
+def test_scale_free_graph_initial_graph_kwarg(ig):
+ with pytest.raises(nx.NetworkXError):
+ scale_free_graph(100, initial_graph=ig)
+
+
+class TestRandomKOutGraph:
+ """Unit tests for the
+ :func:`~networkx.generators.directed.random_k_out_graph` function.
+
+ """
+
+ def test_regularity(self):
+ """Tests that the generated graph is `k`-out-regular."""
+ n = 10
+ k = 3
+ alpha = 1
+ G = random_k_out_graph(n, k, alpha)
+ assert all(d == k for v, d in G.out_degree())
+ G = random_k_out_graph(n, k, alpha, seed=42)
+ assert all(d == k for v, d in G.out_degree())
+
+ def test_no_self_loops(self):
+ """Tests for forbidding self-loops."""
+ n = 10
+ k = 3
+ alpha = 1
+ G = random_k_out_graph(n, k, alpha, self_loops=False)
+ assert nx.number_of_selfloops(G) == 0
+
+ def test_negative_alpha(self):
+ with pytest.raises(ValueError, match="alpha must be positive"):
+ random_k_out_graph(10, 3, -1)
+
+
+class TestUniformRandomKOutGraph:
+ """Unit tests for the
+ :func:`~networkx.generators.directed.random_uniform_k_out_graph`
+ function.
+
+ """
+
+ def test_regularity(self):
+ """Tests that the generated graph is `k`-out-regular."""
+ n = 10
+ k = 3
+ G = random_uniform_k_out_graph(n, k)
+ assert all(d == k for v, d in G.out_degree())
+ G = random_uniform_k_out_graph(n, k, seed=42)
+ assert all(d == k for v, d in G.out_degree())
+
+ def test_no_self_loops(self):
+ """Tests for forbidding self-loops."""
+ n = 10
+ k = 3
+ G = random_uniform_k_out_graph(n, k, self_loops=False)
+ assert nx.number_of_selfloops(G) == 0
+ assert all(d == k for v, d in G.out_degree())
+
+ def test_with_replacement(self):
+ n = 10
+ k = 3
+ G = random_uniform_k_out_graph(n, k, with_replacement=True)
+ assert G.is_multigraph()
+ assert all(d == k for v, d in G.out_degree())
+ n = 10
+ k = 9
+ G = random_uniform_k_out_graph(n, k, with_replacement=False, self_loops=False)
+ assert nx.number_of_selfloops(G) == 0
+ assert all(d == k for v, d in G.out_degree())
+
+ def test_without_replacement(self):
+ n = 10
+ k = 3
+ G = random_uniform_k_out_graph(n, k, with_replacement=False)
+ assert not G.is_multigraph()
+ assert all(d == k for v, d in G.out_degree())
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_duplication.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_duplication.py
new file mode 100644
index 00000000..9b6100b7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_duplication.py
@@ -0,0 +1,103 @@
+"""Unit tests for the :mod:`networkx.generators.duplication` module."""
+
+import pytest
+
+import networkx as nx
+
+
+class TestDuplicationDivergenceGraph:
+ """Unit tests for the
+ :func:`networkx.generators.duplication.duplication_divergence_graph`
+ function.
+
+ """
+
+ def test_final_size(self):
+ G = nx.duplication_divergence_graph(3, p=1)
+ assert len(G) == 3
+ G = nx.duplication_divergence_graph(3, p=1, seed=42)
+ assert len(G) == 3
+
+ def test_probability_too_large(self):
+ with pytest.raises(nx.NetworkXError):
+ nx.duplication_divergence_graph(3, p=2)
+
+ def test_probability_too_small(self):
+ with pytest.raises(nx.NetworkXError):
+ nx.duplication_divergence_graph(3, p=-1)
+
+ def test_non_extreme_probability_value(self):
+ G = nx.duplication_divergence_graph(6, p=0.3, seed=42)
+ assert len(G) == 6
+ assert list(G.degree()) == [(0, 2), (1, 3), (2, 2), (3, 3), (4, 1), (5, 1)]
+
+ def test_minimum_desired_nodes(self):
+ with pytest.raises(
+ nx.NetworkXError, match=".*n must be greater than or equal to 2"
+ ):
+ nx.duplication_divergence_graph(1, p=1)
+
+ def test_create_using(self):
+ class DummyGraph(nx.Graph):
+ pass
+
+ class DummyDiGraph(nx.DiGraph):
+ pass
+
+ G = nx.duplication_divergence_graph(6, 0.3, seed=42, create_using=DummyGraph)
+ assert isinstance(G, DummyGraph)
+ with pytest.raises(nx.NetworkXError, match="create_using must not be directed"):
+ nx.duplication_divergence_graph(6, 0.3, seed=42, create_using=DummyDiGraph)
+
+
+class TestPartialDuplicationGraph:
+ """Unit tests for the
+ :func:`networkx.generators.duplication.partial_duplication_graph`
+ function.
+
+ """
+
+ def test_final_size(self):
+ N = 10
+ n = 5
+ p = 0.5
+ q = 0.5
+ G = nx.partial_duplication_graph(N, n, p, q)
+ assert len(G) == N
+ G = nx.partial_duplication_graph(N, n, p, q, seed=42)
+ assert len(G) == N
+
+ def test_initial_clique_size(self):
+ N = 10
+ n = 10
+ p = 0.5
+ q = 0.5
+ G = nx.partial_duplication_graph(N, n, p, q)
+ assert len(G) == n
+
+ def test_invalid_initial_size(self):
+ with pytest.raises(nx.NetworkXError):
+ N = 5
+ n = 10
+ p = 0.5
+ q = 0.5
+ G = nx.partial_duplication_graph(N, n, p, q)
+
+ def test_invalid_probabilities(self):
+ N = 1
+ n = 1
+ for p, q in [(0.5, 2), (0.5, -1), (2, 0.5), (-1, 0.5)]:
+ args = (N, n, p, q)
+ pytest.raises(nx.NetworkXError, nx.partial_duplication_graph, *args)
+
+ def test_create_using(self):
+ class DummyGraph(nx.Graph):
+ pass
+
+ class DummyDiGraph(nx.DiGraph):
+ pass
+
+ G = nx.partial_duplication_graph(10, 5, 0.5, 0.5, create_using=DummyGraph)
+ assert isinstance(G, DummyGraph)
+ with pytest.raises(nx.NetworkXError, match="create_using must not be directed"):
+ nx.partial_duplication_graph(10, 5, 0.5, 0.5, create_using=DummyDiGraph)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_ego.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_ego.py
new file mode 100644
index 00000000..f6fc7795
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_ego.py
@@ -0,0 +1,39 @@
+"""
+ego graph
+---------
+"""
+
+import networkx as nx
+from networkx.utils import edges_equal, nodes_equal
+
+
+class TestGeneratorEgo:
+ def test_ego(self):
+ G = nx.star_graph(3)
+ H = nx.ego_graph(G, 0)
+ assert nx.is_isomorphic(G, H)
+ G.add_edge(1, 11)
+ G.add_edge(2, 22)
+ G.add_edge(3, 33)
+ H = nx.ego_graph(G, 0)
+ assert nx.is_isomorphic(nx.star_graph(3), H)
+ G = nx.path_graph(3)
+ H = nx.ego_graph(G, 0)
+ assert edges_equal(H.edges(), [(0, 1)])
+ H = nx.ego_graph(G, 0, undirected=True)
+ assert edges_equal(H.edges(), [(0, 1)])
+ H = nx.ego_graph(G, 0, center=False)
+ assert edges_equal(H.edges(), [])
+
+ def test_ego_distance(self):
+ G = nx.Graph()
+ G.add_edge(0, 1, weight=2, distance=1)
+ G.add_edge(1, 2, weight=2, distance=2)
+ G.add_edge(2, 3, weight=2, distance=1)
+ assert nodes_equal(nx.ego_graph(G, 0, radius=3).nodes(), [0, 1, 2, 3])
+ eg = nx.ego_graph(G, 0, radius=3, distance="weight")
+ assert nodes_equal(eg.nodes(), [0, 1])
+ eg = nx.ego_graph(G, 0, radius=3, distance="weight", undirected=True)
+ assert nodes_equal(eg.nodes(), [0, 1])
+ eg = nx.ego_graph(G, 0, radius=3, distance="distance")
+ assert nodes_equal(eg.nodes(), [0, 1, 2])
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_expanders.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_expanders.py
new file mode 100644
index 00000000..7cebc588
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_expanders.py
@@ -0,0 +1,162 @@
+"""Unit tests for the :mod:`networkx.generators.expanders` module."""
+
+import pytest
+
+import networkx as nx
+
+
+@pytest.mark.parametrize("n", (2, 3, 5, 6, 10))
+def test_margulis_gabber_galil_graph_properties(n):
+ g = nx.margulis_gabber_galil_graph(n)
+ assert g.number_of_nodes() == n * n
+ for node in g:
+ assert g.degree(node) == 8
+ assert len(node) == 2
+ for i in node:
+ assert int(i) == i
+ assert 0 <= i < n
+
+
+@pytest.mark.parametrize("n", (2, 3, 5, 6, 10))
+def test_margulis_gabber_galil_graph_eigvals(n):
+ np = pytest.importorskip("numpy")
+ sp = pytest.importorskip("scipy")
+
+ g = nx.margulis_gabber_galil_graph(n)
+ # Eigenvalues are already sorted using the scipy eigvalsh,
+ # but the implementation in numpy does not guarantee order.
+ w = sorted(sp.linalg.eigvalsh(nx.adjacency_matrix(g).toarray()))
+ assert w[-2] < 5 * np.sqrt(2)
+
+
+@pytest.mark.parametrize("p", (3, 5, 7, 11)) # Primes
+def test_chordal_cycle_graph(p):
+ """Test for the :func:`networkx.chordal_cycle_graph` function."""
+ G = nx.chordal_cycle_graph(p)
+ assert len(G) == p
+ # TODO The second largest eigenvalue should be smaller than a constant,
+ # independent of the number of nodes in the graph:
+ #
+ # eigs = sorted(sp.linalg.eigvalsh(nx.adjacency_matrix(G).toarray()))
+ # assert_less(eigs[-2], ...)
+ #
+
+
+@pytest.mark.parametrize("p", (3, 5, 7, 11, 13)) # Primes
+def test_paley_graph(p):
+ """Test for the :func:`networkx.paley_graph` function."""
+ G = nx.paley_graph(p)
+ # G has p nodes
+ assert len(G) == p
+ # G is (p-1)/2-regular
+ in_degrees = {G.in_degree(node) for node in G.nodes}
+ out_degrees = {G.out_degree(node) for node in G.nodes}
+ assert len(in_degrees) == 1 and in_degrees.pop() == (p - 1) // 2
+ assert len(out_degrees) == 1 and out_degrees.pop() == (p - 1) // 2
+
+ # If p = 1 mod 4, -1 is a square mod 4 and therefore the
+ # edge in the Paley graph are symmetric.
+ if p % 4 == 1:
+ for u, v in G.edges:
+ assert (v, u) in G.edges
+
+
+@pytest.mark.parametrize("d, n", [(2, 7), (4, 10), (4, 16)])
+def test_maybe_regular_expander(d, n):
+ pytest.importorskip("numpy")
+ G = nx.maybe_regular_expander(n, d)
+
+ assert len(G) == n, "Should have n nodes"
+ assert len(G.edges) == n * d / 2, "Should have n*d/2 edges"
+ assert nx.is_k_regular(G, d), "Should be d-regular"
+
+
+@pytest.mark.parametrize("n", (3, 5, 6, 10))
+def test_is_regular_expander(n):
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+ G = nx.complete_graph(n)
+
+ assert nx.is_regular_expander(G) == True, "Should be a regular expander"
+
+
+@pytest.mark.parametrize("d, n", [(2, 7), (4, 10), (4, 16)])
+def test_random_regular_expander(d, n):
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+ G = nx.random_regular_expander_graph(n, d)
+
+ assert len(G) == n, "Should have n nodes"
+ assert len(G.edges) == n * d / 2, "Should have n*d/2 edges"
+ assert nx.is_k_regular(G, d), "Should be d-regular"
+ assert nx.is_regular_expander(G) == True, "Should be a regular expander"
+
+
+def test_random_regular_expander_explicit_construction():
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+ G = nx.random_regular_expander_graph(d=4, n=5)
+
+ assert len(G) == 5 and len(G.edges) == 10, "Should be a complete graph"
+
+
+@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
+def test_margulis_gabber_galil_graph_badinput(graph_type):
+ with pytest.raises(
+ nx.NetworkXError, match="`create_using` must be an undirected multigraph"
+ ):
+ nx.margulis_gabber_galil_graph(3, create_using=graph_type)
+
+
+@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
+def test_chordal_cycle_graph_badinput(graph_type):
+ with pytest.raises(
+ nx.NetworkXError, match="`create_using` must be an undirected multigraph"
+ ):
+ nx.chordal_cycle_graph(3, create_using=graph_type)
+
+
+def test_paley_graph_badinput():
+ with pytest.raises(
+ nx.NetworkXError, match="`create_using` cannot be a multigraph."
+ ):
+ nx.paley_graph(3, create_using=nx.MultiGraph)
+
+
+def test_maybe_regular_expander_badinput():
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+
+ with pytest.raises(nx.NetworkXError, match="n must be a positive integer"):
+ nx.maybe_regular_expander(n=-1, d=2)
+
+ with pytest.raises(nx.NetworkXError, match="d must be greater than or equal to 2"):
+ nx.maybe_regular_expander(n=10, d=0)
+
+ with pytest.raises(nx.NetworkXError, match="Need n-1>= d to have room"):
+ nx.maybe_regular_expander(n=5, d=6)
+
+
+def test_is_regular_expander_badinput():
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+
+ with pytest.raises(nx.NetworkXError, match="epsilon must be non negative"):
+ nx.is_regular_expander(nx.Graph(), epsilon=-1)
+
+
+def test_random_regular_expander_badinput():
+ pytest.importorskip("numpy")
+ pytest.importorskip("scipy")
+
+ with pytest.raises(nx.NetworkXError, match="n must be a positive integer"):
+ nx.random_regular_expander_graph(n=-1, d=2)
+
+ with pytest.raises(nx.NetworkXError, match="d must be greater than or equal to 2"):
+ nx.random_regular_expander_graph(n=10, d=0)
+
+ with pytest.raises(nx.NetworkXError, match="Need n-1>= d to have room"):
+ nx.random_regular_expander_graph(n=5, d=6)
+
+ with pytest.raises(nx.NetworkXError, match="epsilon must be non negative"):
+ nx.random_regular_expander_graph(n=4, d=2, epsilon=-1)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_geometric.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_geometric.py
new file mode 100644
index 00000000..f1c68bea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_geometric.py
@@ -0,0 +1,488 @@
+import math
+import random
+from itertools import combinations
+
+import pytest
+
+import networkx as nx
+
+
+def l1dist(x, y):
+ return sum(abs(a - b) for a, b in zip(x, y))
+
+
+class TestRandomGeometricGraph:
+ """Unit tests for :func:`~networkx.random_geometric_graph`"""
+
+ def test_number_of_nodes(self):
+ G = nx.random_geometric_graph(50, 0.25, seed=42)
+ assert len(G) == 50
+ G = nx.random_geometric_graph(range(50), 0.25, seed=42)
+ assert len(G) == 50
+
+ def test_distances(self):
+ """Tests that pairs of vertices adjacent if and only if they are
+ within the prescribed radius.
+ """
+ # Use the Euclidean metric, the default according to the
+ # documentation.
+ G = nx.random_geometric_graph(50, 0.25)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+ # Nonadjacent vertices must be at greater distance.
+ else:
+ assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_p(self):
+ """Tests for providing an alternate distance metric to the generator."""
+ # Use the L1 metric.
+ G = nx.random_geometric_graph(50, 0.25, p=1)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+ # Nonadjacent vertices must be at greater distance.
+ else:
+ assert not l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_node_names(self):
+ """Tests using values other than sequential numbers as node IDs."""
+ import string
+
+ nodes = list(string.ascii_lowercase)
+ G = nx.random_geometric_graph(nodes, 0.25)
+ assert len(G) == len(nodes)
+
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+ # Nonadjacent vertices must be at greater distance.
+ else:
+ assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_pos_name(self):
+ G = nx.random_geometric_graph(50, 0.25, seed=42, pos_name="coords")
+ assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
+
+
+class TestSoftRandomGeometricGraph:
+ """Unit tests for :func:`~networkx.soft_random_geometric_graph`"""
+
+ def test_number_of_nodes(self):
+ G = nx.soft_random_geometric_graph(50, 0.25, seed=42)
+ assert len(G) == 50
+ G = nx.soft_random_geometric_graph(range(50), 0.25, seed=42)
+ assert len(G) == 50
+
+ def test_distances(self):
+ """Tests that pairs of vertices adjacent if and only if they are
+ within the prescribed radius.
+ """
+ # Use the Euclidean metric, the default according to the
+ # documentation.
+ G = nx.soft_random_geometric_graph(50, 0.25)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_p(self):
+ """Tests for providing an alternate distance metric to the generator."""
+
+ # Use the L1 metric.
+ def dist(x, y):
+ return sum(abs(a - b) for a, b in zip(x, y))
+
+ G = nx.soft_random_geometric_graph(50, 0.25, p=1)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_node_names(self):
+ """Tests using values other than sequential numbers as node IDs."""
+ import string
+
+ nodes = list(string.ascii_lowercase)
+ G = nx.soft_random_geometric_graph(nodes, 0.25)
+ assert len(G) == len(nodes)
+
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_p_dist_default(self):
+ """Tests default p_dict = 0.5 returns graph with edge count <= RGG with
+ same n, radius, dim and positions
+ """
+ nodes = 50
+ dim = 2
+ pos = {v: [random.random() for i in range(dim)] for v in range(nodes)}
+ RGG = nx.random_geometric_graph(50, 0.25, pos=pos)
+ SRGG = nx.soft_random_geometric_graph(50, 0.25, pos=pos)
+ assert len(SRGG.edges()) <= len(RGG.edges())
+
+ def test_p_dist_zero(self):
+ """Tests if p_dict = 0 returns disconnected graph with 0 edges"""
+
+ def p_dist(dist):
+ return 0
+
+ G = nx.soft_random_geometric_graph(50, 0.25, p_dist=p_dist)
+ assert len(G.edges) == 0
+
+ def test_pos_name(self):
+ G = nx.soft_random_geometric_graph(50, 0.25, seed=42, pos_name="coords")
+ assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
+
+
+def join(G, u, v, theta, alpha, metric):
+ """Returns ``True`` if and only if the nodes whose attributes are
+ ``du`` and ``dv`` should be joined, according to the threshold
+ condition for geographical threshold graphs.
+
+ ``G`` is an undirected NetworkX graph, and ``u`` and ``v`` are nodes
+ in that graph. The nodes must have node attributes ``'pos'`` and
+ ``'weight'``.
+
+ ``metric`` is a distance metric.
+ """
+ du, dv = G.nodes[u], G.nodes[v]
+ u_pos, v_pos = du["pos"], dv["pos"]
+ u_weight, v_weight = du["weight"], dv["weight"]
+ return (u_weight + v_weight) * metric(u_pos, v_pos) ** alpha >= theta
+
+
+class TestGeographicalThresholdGraph:
+ """Unit tests for :func:`~networkx.geographical_threshold_graph`"""
+
+ def test_number_of_nodes(self):
+ G = nx.geographical_threshold_graph(50, 100, seed=42)
+ assert len(G) == 50
+ G = nx.geographical_threshold_graph(range(50), 100, seed=42)
+ assert len(G) == 50
+
+ def test_distances(self):
+ """Tests that pairs of vertices adjacent if and only if their
+ distances meet the given threshold.
+ """
+ # Use the Euclidean metric and alpha = -2
+ # the default according to the documentation.
+ G = nx.geographical_threshold_graph(50, 10)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must exceed the threshold.
+ if v in G[u]:
+ assert join(G, u, v, 10, -2, math.dist)
+ # Nonadjacent vertices must not exceed the threshold.
+ else:
+ assert not join(G, u, v, 10, -2, math.dist)
+
+ def test_metric(self):
+ """Tests for providing an alternate distance metric to the generator."""
+ # Use the L1 metric.
+ G = nx.geographical_threshold_graph(50, 10, metric=l1dist)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must exceed the threshold.
+ if v in G[u]:
+ assert join(G, u, v, 10, -2, l1dist)
+ # Nonadjacent vertices must not exceed the threshold.
+ else:
+ assert not join(G, u, v, 10, -2, l1dist)
+
+ def test_p_dist_zero(self):
+ """Tests if p_dict = 0 returns disconnected graph with 0 edges"""
+
+ def p_dist(dist):
+ return 0
+
+ G = nx.geographical_threshold_graph(50, 1, p_dist=p_dist)
+ assert len(G.edges) == 0
+
+ def test_pos_weight_name(self):
+ gtg = nx.geographical_threshold_graph
+ G = gtg(50, 100, seed=42, pos_name="coords", weight_name="wt")
+ assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
+ assert all(d["wt"] > 0 for n, d in G.nodes.items())
+
+
+class TestWaxmanGraph:
+ """Unit tests for the :func:`~networkx.waxman_graph` function."""
+
+ def test_number_of_nodes_1(self):
+ G = nx.waxman_graph(50, 0.5, 0.1, seed=42)
+ assert len(G) == 50
+ G = nx.waxman_graph(range(50), 0.5, 0.1, seed=42)
+ assert len(G) == 50
+
+ def test_number_of_nodes_2(self):
+ G = nx.waxman_graph(50, 0.5, 0.1, L=1)
+ assert len(G) == 50
+ G = nx.waxman_graph(range(50), 0.5, 0.1, L=1)
+ assert len(G) == 50
+
+ def test_metric(self):
+ """Tests for providing an alternate distance metric to the generator."""
+ # Use the L1 metric.
+ G = nx.waxman_graph(50, 0.5, 0.1, metric=l1dist)
+ assert len(G) == 50
+
+ def test_pos_name(self):
+ G = nx.waxman_graph(50, 0.5, 0.1, seed=42, pos_name="coords")
+ assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
+
+
+class TestNavigableSmallWorldGraph:
+ def test_navigable_small_world(self):
+ G = nx.navigable_small_world_graph(5, p=1, q=0, seed=42)
+ gg = nx.grid_2d_graph(5, 5).to_directed()
+ assert nx.is_isomorphic(G, gg)
+
+ G = nx.navigable_small_world_graph(5, p=1, q=0, dim=3)
+ gg = nx.grid_graph([5, 5, 5]).to_directed()
+ assert nx.is_isomorphic(G, gg)
+
+ G = nx.navigable_small_world_graph(5, p=1, q=0, dim=1)
+ gg = nx.grid_graph([5]).to_directed()
+ assert nx.is_isomorphic(G, gg)
+
+ def test_invalid_diameter_value(self):
+ with pytest.raises(nx.NetworkXException, match=".*p must be >= 1"):
+ nx.navigable_small_world_graph(5, p=0, q=0, dim=1)
+
+ def test_invalid_long_range_connections_value(self):
+ with pytest.raises(nx.NetworkXException, match=".*q must be >= 0"):
+ nx.navigable_small_world_graph(5, p=1, q=-1, dim=1)
+
+ def test_invalid_exponent_for_decaying_probability_value(self):
+ with pytest.raises(nx.NetworkXException, match=".*r must be >= 0"):
+ nx.navigable_small_world_graph(5, p=1, q=0, r=-1, dim=1)
+
+ def test_r_between_0_and_1(self):
+ """Smoke test for radius in range [0, 1]"""
+ # q=0 means no long-range connections
+ G = nx.navigable_small_world_graph(3, p=1, q=0, r=0.5, dim=2, seed=42)
+ expected = nx.grid_2d_graph(3, 3, create_using=nx.DiGraph)
+ assert nx.utils.graphs_equal(G, expected)
+
+ @pytest.mark.parametrize("seed", range(2478, 2578, 10))
+ def test_r_general_scaling(self, seed):
+ """The probability of adding a long-range edge scales with `1 / dist**r`,
+ so a navigable_small_world graph created with r < 1 should generally
+ result in more edges than a navigable_small_world graph with r >= 1
+ (for 0 < q << n).
+
+ N.B. this is probabilistic, so this test may not hold for all seeds."""
+ G1 = nx.navigable_small_world_graph(7, q=3, r=0.5, seed=seed)
+ G2 = nx.navigable_small_world_graph(7, q=3, r=1, seed=seed)
+ G3 = nx.navigable_small_world_graph(7, q=3, r=2, seed=seed)
+ assert G1.number_of_edges() > G2.number_of_edges()
+ assert G2.number_of_edges() > G3.number_of_edges()
+
+
+class TestThresholdedRandomGeometricGraph:
+ """Unit tests for :func:`~networkx.thresholded_random_geometric_graph`"""
+
+ def test_number_of_nodes(self):
+ G = nx.thresholded_random_geometric_graph(50, 0.2, 0.1, seed=42)
+ assert len(G) == 50
+ G = nx.thresholded_random_geometric_graph(range(50), 0.2, 0.1, seed=42)
+ assert len(G) == 50
+
+ def test_distances(self):
+ """Tests that pairs of vertices adjacent if and only if they are
+ within the prescribed radius.
+ """
+ # Use the Euclidean metric, the default according to the
+ # documentation.
+ G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, seed=42)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_p(self):
+ """Tests for providing an alternate distance metric to the generator."""
+
+ # Use the L1 metric.
+ def dist(x, y):
+ return sum(abs(a - b) for a, b in zip(x, y))
+
+ G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, p=1, seed=42)
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_node_names(self):
+ """Tests using values other than sequential numbers as node IDs."""
+ import string
+
+ nodes = list(string.ascii_lowercase)
+ G = nx.thresholded_random_geometric_graph(nodes, 0.25, 0.1, seed=42)
+ assert len(G) == len(nodes)
+
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
+
+ def test_theta(self):
+ """Tests that pairs of vertices adjacent if and only if their sum
+ weights exceeds the threshold parameter theta.
+ """
+ G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, seed=42)
+
+ for u, v in combinations(G, 2):
+ # Adjacent vertices must be within the given distance.
+ if v in G[u]:
+ assert (G.nodes[u]["weight"] + G.nodes[v]["weight"]) >= 0.1
+
+ def test_pos_name(self):
+ trgg = nx.thresholded_random_geometric_graph
+ G = trgg(50, 0.25, 0.1, seed=42, pos_name="p", weight_name="wt")
+ assert all(len(d["p"]) == 2 for n, d in G.nodes.items())
+ assert all(d["wt"] > 0 for n, d in G.nodes.items())
+
+
+def test_geometric_edges_pos_attribute():
+ G = nx.Graph()
+ G.add_nodes_from(
+ [
+ (0, {"position": (0, 0)}),
+ (1, {"position": (0, 1)}),
+ (2, {"position": (1, 0)}),
+ ]
+ )
+ expected_edges = [(0, 1), (0, 2)]
+ assert expected_edges == nx.geometric_edges(G, radius=1, pos_name="position")
+
+
+def test_geometric_edges_raises_no_pos():
+ G = nx.path_graph(3)
+ msg = "all nodes. must have a '"
+ with pytest.raises(nx.NetworkXError, match=msg):
+ nx.geometric_edges(G, radius=1)
+
+
+def test_number_of_nodes_S1():
+ G = nx.geometric_soft_configuration_graph(
+ beta=1.5, n=100, gamma=2.7, mean_degree=10, seed=42
+ )
+ assert len(G) == 100
+
+
+def test_set_attributes_S1():
+ G = nx.geometric_soft_configuration_graph(
+ beta=1.5, n=100, gamma=2.7, mean_degree=10, seed=42
+ )
+ kappas = nx.get_node_attributes(G, "kappa")
+ assert len(kappas) == 100
+ thetas = nx.get_node_attributes(G, "theta")
+ assert len(thetas) == 100
+ radii = nx.get_node_attributes(G, "radius")
+ assert len(radii) == 100
+
+
+def test_mean_kappas_mean_degree_S1():
+ G = nx.geometric_soft_configuration_graph(
+ beta=2.5, n=50, gamma=2.7, mean_degree=10, seed=8023
+ )
+
+ kappas = nx.get_node_attributes(G, "kappa")
+ mean_kappas = sum(kappas.values()) / len(kappas)
+ assert math.fabs(mean_kappas - 10) < 0.5
+
+ degrees = dict(G.degree())
+ mean_degree = sum(degrees.values()) / len(degrees)
+ assert math.fabs(mean_degree - 10) < 1
+
+
+def test_dict_kappas_S1():
+ kappas = {i: 10 for i in range(1000)}
+ G = nx.geometric_soft_configuration_graph(beta=1, kappas=kappas)
+ assert len(G) == 1000
+ kappas = nx.get_node_attributes(G, "kappa")
+ assert all(kappa == 10 for kappa in kappas.values())
+
+
+def test_beta_clustering_S1():
+ G1 = nx.geometric_soft_configuration_graph(
+ beta=1.5, n=100, gamma=3.5, mean_degree=10, seed=42
+ )
+ G2 = nx.geometric_soft_configuration_graph(
+ beta=3.0, n=100, gamma=3.5, mean_degree=10, seed=42
+ )
+ assert nx.average_clustering(G1) < nx.average_clustering(G2)
+
+
+def test_wrong_parameters_S1():
+ with pytest.raises(
+ nx.NetworkXError,
+ match="Please provide either kappas, or all 3 of: n, gamma and mean_degree.",
+ ):
+ G = nx.geometric_soft_configuration_graph(
+ beta=1.5, gamma=3.5, mean_degree=10, seed=42
+ )
+
+ with pytest.raises(
+ nx.NetworkXError,
+ match="When kappas is input, n, gamma and mean_degree must not be.",
+ ):
+ kappas = {i: 10 for i in range(1000)}
+ G = nx.geometric_soft_configuration_graph(
+ beta=1.5, kappas=kappas, gamma=2.3, seed=42
+ )
+
+ with pytest.raises(
+ nx.NetworkXError,
+ match="Please provide either kappas, or all 3 of: n, gamma and mean_degree.",
+ ):
+ G = nx.geometric_soft_configuration_graph(beta=1.5, seed=42)
+
+
+def test_negative_beta_S1():
+ with pytest.raises(
+ nx.NetworkXError, match="The parameter beta cannot be smaller or equal to 0."
+ ):
+ G = nx.geometric_soft_configuration_graph(
+ beta=-1, n=100, gamma=2.3, mean_degree=10, seed=42
+ )
+
+
+def test_non_zero_clustering_beta_lower_one_S1():
+ G = nx.geometric_soft_configuration_graph(
+ beta=0.5, n=100, gamma=3.5, mean_degree=10, seed=42
+ )
+ assert nx.average_clustering(G) > 0
+
+
+def test_mean_degree_influence_on_connectivity_S1():
+ low_mean_degree = 2
+ high_mean_degree = 20
+ G_low = nx.geometric_soft_configuration_graph(
+ beta=1.2, n=100, gamma=2.7, mean_degree=low_mean_degree, seed=42
+ )
+ G_high = nx.geometric_soft_configuration_graph(
+ beta=1.2, n=100, gamma=2.7, mean_degree=high_mean_degree, seed=42
+ )
+ assert nx.number_connected_components(G_low) > nx.number_connected_components(
+ G_high
+ )
+
+
+def test_compare_mean_kappas_different_gammas_S1():
+ G1 = nx.geometric_soft_configuration_graph(
+ beta=1.5, n=20, gamma=2.7, mean_degree=5, seed=42
+ )
+ G2 = nx.geometric_soft_configuration_graph(
+ beta=1.5, n=20, gamma=3.5, mean_degree=5, seed=42
+ )
+ kappas1 = nx.get_node_attributes(G1, "kappa")
+ mean_kappas1 = sum(kappas1.values()) / len(kappas1)
+ kappas2 = nx.get_node_attributes(G2, "kappa")
+ mean_kappas2 = sum(kappas2.values()) / len(kappas2)
+ assert math.fabs(mean_kappas1 - mean_kappas2) < 1
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_harary_graph.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_harary_graph.py
new file mode 100644
index 00000000..8a0142df
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_harary_graph.py
@@ -0,0 +1,133 @@
+"""Unit tests for the :mod:`networkx.generators.harary_graph` module."""
+
+import pytest
+
+import networkx as nx
+from networkx.algorithms.isomorphism.isomorph import is_isomorphic
+from networkx.generators.harary_graph import hkn_harary_graph, hnm_harary_graph
+
+
+class TestHararyGraph:
+ """
+ Suppose n nodes, m >= n-1 edges, d = 2m // n, r = 2m % n
+ """
+
+ def test_hnm_harary_graph(self):
+ # When d is even and r = 0, the hnm_harary_graph(n,m) is
+ # the circulant_graph(n, list(range(1,d/2+1)))
+ for n, m in [(5, 5), (6, 12), (7, 14)]:
+ G1 = hnm_harary_graph(n, m)
+ d = 2 * m // n
+ G2 = nx.circulant_graph(n, list(range(1, d // 2 + 1)))
+ assert is_isomorphic(G1, G2)
+
+ # When d is even and r > 0, the hnm_harary_graph(n,m) is
+ # the circulant_graph(n, list(range(1,d/2+1)))
+ # with r edges added arbitrarily
+ for n, m in [(5, 7), (6, 13), (7, 16)]:
+ G1 = hnm_harary_graph(n, m)
+ d = 2 * m // n
+ G2 = nx.circulant_graph(n, list(range(1, d // 2 + 1)))
+ assert set(G2.edges) < set(G1.edges)
+ assert G1.number_of_edges() == m
+
+ # When d is odd and n is even and r = 0, the hnm_harary_graph(n,m)
+ # is the circulant_graph(n, list(range(1,(d+1)/2) plus [n//2])
+ for n, m in [(6, 9), (8, 12), (10, 15)]:
+ G1 = hnm_harary_graph(n, m)
+ d = 2 * m // n
+ L = list(range(1, (d + 1) // 2))
+ L.append(n // 2)
+ G2 = nx.circulant_graph(n, L)
+ assert is_isomorphic(G1, G2)
+
+ # When d is odd and n is even and r > 0, the hnm_harary_graph(n,m)
+ # is the circulant_graph(n, list(range(1,(d+1)/2) plus [n//2])
+ # with r edges added arbitrarily
+ for n, m in [(6, 10), (8, 13), (10, 17)]:
+ G1 = hnm_harary_graph(n, m)
+ d = 2 * m // n
+ L = list(range(1, (d + 1) // 2))
+ L.append(n // 2)
+ G2 = nx.circulant_graph(n, L)
+ assert set(G2.edges) < set(G1.edges)
+ assert G1.number_of_edges() == m
+
+ # When d is odd and n is odd, the hnm_harary_graph(n,m) is
+ # the circulant_graph(n, list(range(1,(d+1)/2))
+ # with m - n*(d-1)/2 edges added arbitrarily
+ for n, m in [(5, 4), (7, 12), (9, 14)]:
+ G1 = hnm_harary_graph(n, m)
+ d = 2 * m // n
+ L = list(range(1, (d + 1) // 2))
+ G2 = nx.circulant_graph(n, L)
+ assert set(G2.edges) < set(G1.edges)
+ assert G1.number_of_edges() == m
+
+ # Raise NetworkXError if n<1
+ n = 0
+ m = 0
+ pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
+
+ # Raise NetworkXError if m < n-1
+ n = 6
+ m = 4
+ pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
+
+ # Raise NetworkXError if m > n(n-1)/2
+ n = 6
+ m = 16
+ pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
+
+ """
+ Suppose connectivity k, number of nodes n
+ """
+
+ def test_hkn_harary_graph(self):
+ # When k == 1, the hkn_harary_graph(k,n) is
+ # the path_graph(n)
+ for k, n in [(1, 6), (1, 7)]:
+ G1 = hkn_harary_graph(k, n)
+ G2 = nx.path_graph(n)
+ assert is_isomorphic(G1, G2)
+
+ # When k is even, the hkn_harary_graph(k,n) is
+ # the circulant_graph(n, list(range(1,k/2+1)))
+ for k, n in [(2, 6), (2, 7), (4, 6), (4, 7)]:
+ G1 = hkn_harary_graph(k, n)
+ G2 = nx.circulant_graph(n, list(range(1, k // 2 + 1)))
+ assert is_isomorphic(G1, G2)
+
+ # When k is odd and n is even, the hkn_harary_graph(k,n) is
+ # the circulant_graph(n, list(range(1,(k+1)/2)) plus [n/2])
+ for k, n in [(3, 6), (5, 8), (7, 10)]:
+ G1 = hkn_harary_graph(k, n)
+ L = list(range(1, (k + 1) // 2))
+ L.append(n // 2)
+ G2 = nx.circulant_graph(n, L)
+ assert is_isomorphic(G1, G2)
+
+ # When k is odd and n is odd, the hkn_harary_graph(k,n) is
+ # the circulant_graph(n, list(range(1,(k+1)/2))) with
+ # n//2+1 edges added between node i and node i+n//2+1
+ for k, n in [(3, 5), (5, 9), (7, 11)]:
+ G1 = hkn_harary_graph(k, n)
+ G2 = nx.circulant_graph(n, list(range(1, (k + 1) // 2)))
+ eSet1 = set(G1.edges)
+ eSet2 = set(G2.edges)
+ eSet3 = set()
+ half = n // 2
+ for i in range(half + 1):
+ # add half+1 edges between i and i+half
+ eSet3.add((i, (i + half) % n))
+ assert eSet1 == eSet2 | eSet3
+
+ # Raise NetworkXError if k<1
+ k = 0
+ n = 0
+ pytest.raises(nx.NetworkXError, hkn_harary_graph, k, n)
+
+ # Raise NetworkXError if n<k+1
+ k = 6
+ n = 6
+ pytest.raises(nx.NetworkXError, hkn_harary_graph, k, n)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_internet_as_graphs.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_internet_as_graphs.py
new file mode 100644
index 00000000..0d578b4b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_internet_as_graphs.py
@@ -0,0 +1,176 @@
+from pytest import approx
+
+from networkx import is_connected, neighbors
+from networkx.generators.internet_as_graphs import random_internet_as_graph
+
+
+class TestInternetASTopology:
+ @classmethod
+ def setup_class(cls):
+ cls.n = 1000
+ cls.seed = 42
+ cls.G = random_internet_as_graph(cls.n, cls.seed)
+ cls.T = []
+ cls.M = []
+ cls.C = []
+ cls.CP = []
+ cls.customers = {}
+ cls.providers = {}
+
+ for i in cls.G.nodes():
+ if cls.G.nodes[i]["type"] == "T":
+ cls.T.append(i)
+ elif cls.G.nodes[i]["type"] == "M":
+ cls.M.append(i)
+ elif cls.G.nodes[i]["type"] == "C":
+ cls.C.append(i)
+ elif cls.G.nodes[i]["type"] == "CP":
+ cls.CP.append(i)
+ else:
+ raise ValueError("Inconsistent data in the graph node attributes")
+ cls.set_customers(i)
+ cls.set_providers(i)
+
+ @classmethod
+ def set_customers(cls, i):
+ if i not in cls.customers:
+ cls.customers[i] = set()
+ for j in neighbors(cls.G, i):
+ e = cls.G.edges[(i, j)]
+ if e["type"] == "transit":
+ customer = int(e["customer"])
+ if j == customer:
+ cls.set_customers(j)
+ cls.customers[i] = cls.customers[i].union(cls.customers[j])
+ cls.customers[i].add(j)
+ elif i != customer:
+ raise ValueError(
+ "Inconsistent data in the graph edge attributes"
+ )
+
+ @classmethod
+ def set_providers(cls, i):
+ if i not in cls.providers:
+ cls.providers[i] = set()
+ for j in neighbors(cls.G, i):
+ e = cls.G.edges[(i, j)]
+ if e["type"] == "transit":
+ customer = int(e["customer"])
+ if i == customer:
+ cls.set_providers(j)
+ cls.providers[i] = cls.providers[i].union(cls.providers[j])
+ cls.providers[i].add(j)
+ elif j != customer:
+ raise ValueError(
+ "Inconsistent data in the graph edge attributes"
+ )
+
+ def test_wrong_input(self):
+ G = random_internet_as_graph(0)
+ assert len(G.nodes()) == 0
+
+ G = random_internet_as_graph(-1)
+ assert len(G.nodes()) == 0
+
+ G = random_internet_as_graph(1)
+ assert len(G.nodes()) == 1
+
+ def test_node_numbers(self):
+ assert len(self.G.nodes()) == self.n
+ assert len(self.T) < 7
+ assert len(self.M) == round(self.n * 0.15)
+ assert len(self.CP) == round(self.n * 0.05)
+ numb = self.n - len(self.T) - len(self.M) - len(self.CP)
+ assert len(self.C) == numb
+
+ def test_connectivity(self):
+ assert is_connected(self.G)
+
+ def test_relationships(self):
+ # T nodes are not customers of anyone
+ for i in self.T:
+ assert len(self.providers[i]) == 0
+
+ # C nodes are not providers of anyone
+ for i in self.C:
+ assert len(self.customers[i]) == 0
+
+ # CP nodes are not providers of anyone
+ for i in self.CP:
+ assert len(self.customers[i]) == 0
+
+ # test whether there is a customer-provider loop
+ for i in self.G.nodes():
+ assert len(self.customers[i].intersection(self.providers[i])) == 0
+
+ # test whether there is a peering with a customer or provider
+ for i, j in self.G.edges():
+ if self.G.edges[(i, j)]["type"] == "peer":
+ assert j not in self.customers[i]
+ assert i not in self.customers[j]
+ assert j not in self.providers[i]
+ assert i not in self.providers[j]
+
+ def test_degree_values(self):
+ d_m = 0 # multihoming degree for M nodes
+ d_cp = 0 # multihoming degree for CP nodes
+ d_c = 0 # multihoming degree for C nodes
+ p_m_m = 0 # avg number of peering edges between M and M
+ p_cp_m = 0 # avg number of peering edges between CP and M
+ p_cp_cp = 0 # avg number of peering edges between CP and CP
+ t_m = 0 # probability M's provider is T
+ t_cp = 0 # probability CP's provider is T
+ t_c = 0 # probability C's provider is T
+
+ for i, j in self.G.edges():
+ e = self.G.edges[(i, j)]
+ if e["type"] == "transit":
+ cust = int(e["customer"])
+ if i == cust:
+ prov = j
+ elif j == cust:
+ prov = i
+ else:
+ raise ValueError("Inconsistent data in the graph edge attributes")
+ if cust in self.M:
+ d_m += 1
+ if self.G.nodes[prov]["type"] == "T":
+ t_m += 1
+ elif cust in self.C:
+ d_c += 1
+ if self.G.nodes[prov]["type"] == "T":
+ t_c += 1
+ elif cust in self.CP:
+ d_cp += 1
+ if self.G.nodes[prov]["type"] == "T":
+ t_cp += 1
+ else:
+ raise ValueError("Inconsistent data in the graph edge attributes")
+ elif e["type"] == "peer":
+ if self.G.nodes[i]["type"] == "M" and self.G.nodes[j]["type"] == "M":
+ p_m_m += 1
+ if self.G.nodes[i]["type"] == "CP" and self.G.nodes[j]["type"] == "CP":
+ p_cp_cp += 1
+ if (
+ self.G.nodes[i]["type"] == "M"
+ and self.G.nodes[j]["type"] == "CP"
+ or self.G.nodes[i]["type"] == "CP"
+ and self.G.nodes[j]["type"] == "M"
+ ):
+ p_cp_m += 1
+ else:
+ raise ValueError("Unexpected data in the graph edge attributes")
+
+ assert d_m / len(self.M) == approx((2 + (2.5 * self.n) / 10000), abs=1e-0)
+ assert d_cp / len(self.CP) == approx((2 + (1.5 * self.n) / 10000), abs=1e-0)
+ assert d_c / len(self.C) == approx((1 + (5 * self.n) / 100000), abs=1e-0)
+
+ assert p_m_m / len(self.M) == approx((1 + (2 * self.n) / 10000), abs=1e-0)
+ assert p_cp_m / len(self.CP) == approx((0.2 + (2 * self.n) / 10000), abs=1e-0)
+ assert p_cp_cp / len(self.CP) == approx(
+ (0.05 + (2 * self.n) / 100000), abs=1e-0
+ )
+
+ assert t_m / d_m == approx(0.375, abs=1e-1)
+ assert t_cp / d_cp == approx(0.375, abs=1e-1)
+ assert t_c / d_c == approx(0.125, abs=1e-1)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_intersection.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_intersection.py
new file mode 100644
index 00000000..f52551b4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_intersection.py
@@ -0,0 +1,28 @@
+import pytest
+
+import networkx as nx
+
+
+class TestIntersectionGraph:
+ def test_random_intersection_graph(self):
+ G = nx.uniform_random_intersection_graph(10, 5, 0.5)
+ assert len(G) == 10
+
+ def test_k_random_intersection_graph(self):
+ G = nx.k_random_intersection_graph(10, 5, 2)
+ assert len(G) == 10
+
+ def test_k_random_intersection_graph_seeded(self):
+ G = nx.k_random_intersection_graph(10, 5, 2, seed=1234)
+ assert len(G) == 10
+
+ def test_general_random_intersection_graph(self):
+ G = nx.general_random_intersection_graph(10, 5, [0.1, 0.2, 0.2, 0.1, 0.1])
+ assert len(G) == 10
+ pytest.raises(
+ ValueError,
+ nx.general_random_intersection_graph,
+ 10,
+ 5,
+ [0.1, 0.2, 0.2, 0.1],
+ )
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_interval_graph.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_interval_graph.py
new file mode 100644
index 00000000..57cf7106
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_interval_graph.py
@@ -0,0 +1,144 @@
+"""Unit tests for the :mod:`networkx.generators.interval_graph` module."""
+
+import math
+
+import pytest
+
+import networkx as nx
+from networkx.generators.interval_graph import interval_graph
+from networkx.utils import edges_equal
+
+
+class TestIntervalGraph:
+ """Unit tests for :func:`networkx.generators.interval_graph.interval_graph`"""
+
+ def test_empty(self):
+ """Tests for trivial case of empty input"""
+ assert len(interval_graph([])) == 0
+
+ def test_interval_graph_check_invalid(self):
+ """Tests for conditions that raise Exceptions"""
+
+ invalids_having_none = [None, (1, 2)]
+ with pytest.raises(TypeError):
+ interval_graph(invalids_having_none)
+
+ invalids_having_set = [{1, 2}]
+ with pytest.raises(TypeError):
+ interval_graph(invalids_having_set)
+
+ invalids_having_seq_but_not_length2 = [(1, 2, 3)]
+ with pytest.raises(TypeError):
+ interval_graph(invalids_having_seq_but_not_length2)
+
+ invalids_interval = [[3, 2]]
+ with pytest.raises(ValueError):
+ interval_graph(invalids_interval)
+
+ def test_interval_graph_0(self):
+ intervals = [(1, 2), (1, 3)]
+
+ expected_graph = nx.Graph()
+ expected_graph.add_edge(*intervals)
+
+ actual_g = interval_graph(intervals)
+
+ assert set(actual_g.nodes) == set(expected_graph.nodes)
+ assert edges_equal(expected_graph, actual_g)
+
+ def test_interval_graph_1(self):
+ intervals = [(1, 2), (2, 3), (3, 4), (1, 4)]
+
+ expected_graph = nx.Graph()
+ expected_graph.add_nodes_from(intervals)
+ e1 = ((1, 4), (1, 2))
+ e2 = ((1, 4), (2, 3))
+ e3 = ((1, 4), (3, 4))
+ e4 = ((3, 4), (2, 3))
+ e5 = ((1, 2), (2, 3))
+
+ expected_graph.add_edges_from([e1, e2, e3, e4, e5])
+
+ actual_g = interval_graph(intervals)
+
+ assert set(actual_g.nodes) == set(expected_graph.nodes)
+ assert edges_equal(expected_graph, actual_g)
+
+ def test_interval_graph_2(self):
+ intervals = [(1, 2), [3, 5], [6, 8], (9, 10)]
+
+ expected_graph = nx.Graph()
+ expected_graph.add_nodes_from([(1, 2), (3, 5), (6, 8), (9, 10)])
+
+ actual_g = interval_graph(intervals)
+
+ assert set(actual_g.nodes) == set(expected_graph.nodes)
+ assert edges_equal(expected_graph, actual_g)
+
+ def test_interval_graph_3(self):
+ intervals = [(1, 4), [3, 5], [2.5, 4]]
+
+ expected_graph = nx.Graph()
+ expected_graph.add_nodes_from([(1, 4), (3, 5), (2.5, 4)])
+ e1 = ((1, 4), (3, 5))
+ e2 = ((1, 4), (2.5, 4))
+ e3 = ((3, 5), (2.5, 4))
+
+ expected_graph.add_edges_from([e1, e2, e3])
+
+ actual_g = interval_graph(intervals)
+
+ assert set(actual_g.nodes) == set(expected_graph.nodes)
+ assert edges_equal(expected_graph, actual_g)
+
+ def test_interval_graph_4(self):
+ """test all possible overlaps"""
+ intervals = [
+ (0, 2),
+ (-2, -1),
+ (-2, 0),
+ (-2, 1),
+ (-2, 2),
+ (-2, 3),
+ (0, 1),
+ (0, 2),
+ (0, 3),
+ (1, 2),
+ (1, 3),
+ (2, 3),
+ (3, 4),
+ ]
+
+ expected_graph = nx.Graph()
+ expected_graph.add_nodes_from(intervals)
+ expected_nbrs = {
+ (-2, 0),
+ (-2, 1),
+ (-2, 2),
+ (-2, 3),
+ (0, 1),
+ (0, 2),
+ (0, 3),
+ (1, 2),
+ (1, 3),
+ (2, 3),
+ }
+ actual_g = nx.interval_graph(intervals)
+ actual_nbrs = nx.neighbors(actual_g, (0, 2))
+
+ assert set(actual_nbrs) == expected_nbrs
+
+ def test_interval_graph_5(self):
+ """this test is to see that an interval supports infinite number"""
+ intervals = {(-math.inf, 0), (-1, -1), (0.5, 0.5), (1, 1), (1, math.inf)}
+
+ expected_graph = nx.Graph()
+ expected_graph.add_nodes_from(intervals)
+ e1 = ((-math.inf, 0), (-1, -1))
+ e2 = ((1, 1), (1, math.inf))
+
+ expected_graph.add_edges_from([e1, e2])
+ actual_g = interval_graph(intervals)
+
+ assert set(actual_g.nodes) == set(expected_graph.nodes)
+ assert edges_equal(expected_graph, actual_g)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_joint_degree_seq.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_joint_degree_seq.py
new file mode 100644
index 00000000..1bc0df5c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_joint_degree_seq.py
@@ -0,0 +1,125 @@
+import time
+
+from networkx.algorithms.assortativity import degree_mixing_dict
+from networkx.generators import gnm_random_graph, powerlaw_cluster_graph
+from networkx.generators.joint_degree_seq import (
+ directed_joint_degree_graph,
+ is_valid_directed_joint_degree,
+ is_valid_joint_degree,
+ joint_degree_graph,
+)
+
+
+def test_is_valid_joint_degree():
+ """Tests for conditions that invalidate a joint degree dict"""
+
+ # valid joint degree that satisfies all five conditions
+ joint_degrees = {
+ 1: {4: 1},
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 1, 2: 2, 3: 1},
+ }
+ assert is_valid_joint_degree(joint_degrees)
+
+ # test condition 1
+ # joint_degrees_1[1][4] not integer
+ joint_degrees_1 = {
+ 1: {4: 1.5},
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 1.5, 2: 2, 3: 1},
+ }
+ assert not is_valid_joint_degree(joint_degrees_1)
+
+ # test condition 2
+ # degree_count[2] = sum(joint_degrees_2[2][j)/2, is not an int
+ # degree_count[4] = sum(joint_degrees_2[4][j)/4, is not an int
+ joint_degrees_2 = {
+ 1: {4: 1},
+ 2: {2: 2, 3: 2, 4: 3},
+ 3: {2: 2, 4: 1},
+ 4: {1: 1, 2: 3, 3: 1},
+ }
+ assert not is_valid_joint_degree(joint_degrees_2)
+
+ # test conditions 3 and 4
+ # joint_degrees_3[1][4]>degree_count[1]*degree_count[4]
+ joint_degrees_3 = {
+ 1: {4: 2},
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 2, 2: 2, 3: 1},
+ }
+ assert not is_valid_joint_degree(joint_degrees_3)
+
+ # test condition 5
+ # joint_degrees_5[1][1] not even
+ joint_degrees_5 = {1: {1: 9}}
+ assert not is_valid_joint_degree(joint_degrees_5)
+
+
+def test_joint_degree_graph(ntimes=10):
+ for _ in range(ntimes):
+ seed = int(time.time())
+
+ n, m, p = 20, 10, 1
+ # generate random graph with model powerlaw_cluster and calculate
+ # its joint degree
+ g = powerlaw_cluster_graph(n, m, p, seed=seed)
+ joint_degrees_g = degree_mixing_dict(g, normalized=False)
+
+ # generate simple undirected graph with given joint degree
+ # joint_degrees_g
+ G = joint_degree_graph(joint_degrees_g)
+ joint_degrees_G = degree_mixing_dict(G, normalized=False)
+
+ # assert that the given joint degree is equal to the generated
+ # graph's joint degree
+ assert joint_degrees_g == joint_degrees_G
+
+
+def test_is_valid_directed_joint_degree():
+ in_degrees = [0, 1, 1, 2]
+ out_degrees = [1, 1, 1, 1]
+ nkk = {1: {1: 2, 2: 2}}
+ assert is_valid_directed_joint_degree(in_degrees, out_degrees, nkk)
+
+ # not realizable, values are not integers.
+ nkk = {1: {1: 1.5, 2: 2.5}}
+ assert not is_valid_directed_joint_degree(in_degrees, out_degrees, nkk)
+
+ # not realizable, number of edges between 1-2 are insufficient.
+ nkk = {1: {1: 2, 2: 1}}
+ assert not is_valid_directed_joint_degree(in_degrees, out_degrees, nkk)
+
+ # not realizable, in/out degree sequences have different number of nodes.
+ out_degrees = [1, 1, 1]
+ nkk = {1: {1: 2, 2: 2}}
+ assert not is_valid_directed_joint_degree(in_degrees, out_degrees, nkk)
+
+ # not realizable, degree sequences have fewer than required nodes.
+ in_degrees = [0, 1, 2]
+ assert not is_valid_directed_joint_degree(in_degrees, out_degrees, nkk)
+
+
+def test_directed_joint_degree_graph(n=15, m=100, ntimes=1000):
+ for _ in range(ntimes):
+ # generate gnm random graph and calculate its joint degree.
+ g = gnm_random_graph(n, m, None, directed=True)
+
+ # in-degree sequence of g as a list of integers.
+ in_degrees = list(dict(g.in_degree()).values())
+ # out-degree sequence of g as a list of integers.
+ out_degrees = list(dict(g.out_degree()).values())
+ nkk = degree_mixing_dict(g)
+
+ # generate simple directed graph with given degree sequence and joint
+ # degree matrix.
+ G = directed_joint_degree_graph(in_degrees, out_degrees, nkk)
+
+ # assert degree sequence correctness.
+ assert in_degrees == list(dict(G.in_degree()).values())
+ assert out_degrees == list(dict(G.out_degree()).values())
+ # assert joint degree matrix correctness.
+ assert nkk == degree_mixing_dict(G)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_lattice.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_lattice.py
new file mode 100644
index 00000000..5012324a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_lattice.py
@@ -0,0 +1,246 @@
+"""Unit tests for the :mod:`networkx.generators.lattice` module."""
+
+from itertools import product
+
+import pytest
+
+import networkx as nx
+from networkx.utils import edges_equal
+
+
+class TestGrid2DGraph:
+ """Unit tests for :func:`networkx.generators.lattice.grid_2d_graph`"""
+
+ def test_number_of_vertices(self):
+ m, n = 5, 6
+ G = nx.grid_2d_graph(m, n)
+ assert len(G) == m * n
+
+ def test_degree_distribution(self):
+ m, n = 5, 6
+ G = nx.grid_2d_graph(m, n)
+ expected_histogram = [0, 0, 4, 2 * (m + n) - 8, (m - 2) * (n - 2)]
+ assert nx.degree_histogram(G) == expected_histogram
+
+ def test_directed(self):
+ m, n = 5, 6
+ G = nx.grid_2d_graph(m, n)
+ H = nx.grid_2d_graph(m, n, create_using=nx.DiGraph())
+ assert H.succ == G.adj
+ assert H.pred == G.adj
+
+ def test_multigraph(self):
+ m, n = 5, 6
+ G = nx.grid_2d_graph(m, n)
+ H = nx.grid_2d_graph(m, n, create_using=nx.MultiGraph())
+ assert list(H.edges()) == list(G.edges())
+
+ def test_periodic(self):
+ G = nx.grid_2d_graph(0, 0, periodic=True)
+ assert dict(G.degree()) == {}
+
+ for m, n, H in [
+ (2, 2, nx.cycle_graph(4)),
+ (1, 7, nx.cycle_graph(7)),
+ (7, 1, nx.cycle_graph(7)),
+ (2, 5, nx.circular_ladder_graph(5)),
+ (5, 2, nx.circular_ladder_graph(5)),
+ (2, 4, nx.cubical_graph()),
+ (4, 2, nx.cubical_graph()),
+ ]:
+ G = nx.grid_2d_graph(m, n, periodic=True)
+ assert nx.could_be_isomorphic(G, H)
+
+ def test_periodic_iterable(self):
+ m, n = 3, 7
+ for a, b in product([0, 1], [0, 1]):
+ G = nx.grid_2d_graph(m, n, periodic=(a, b))
+ assert G.number_of_nodes() == m * n
+ assert G.number_of_edges() == (m + a - 1) * n + (n + b - 1) * m
+
+ def test_periodic_directed(self):
+ G = nx.grid_2d_graph(4, 2, periodic=True)
+ H = nx.grid_2d_graph(4, 2, periodic=True, create_using=nx.DiGraph())
+ assert H.succ == G.adj
+ assert H.pred == G.adj
+
+ def test_periodic_multigraph(self):
+ G = nx.grid_2d_graph(4, 2, periodic=True)
+ H = nx.grid_2d_graph(4, 2, periodic=True, create_using=nx.MultiGraph())
+ assert list(G.edges()) == list(H.edges())
+
+ def test_exceptions(self):
+ pytest.raises(nx.NetworkXError, nx.grid_2d_graph, -3, 2)
+ pytest.raises(nx.NetworkXError, nx.grid_2d_graph, 3, -2)
+ pytest.raises(TypeError, nx.grid_2d_graph, 3.3, 2)
+ pytest.raises(TypeError, nx.grid_2d_graph, 3, 2.2)
+
+ def test_node_input(self):
+ G = nx.grid_2d_graph(4, 2, periodic=True)
+ H = nx.grid_2d_graph(range(4), range(2), periodic=True)
+ assert nx.is_isomorphic(H, G)
+ H = nx.grid_2d_graph("abcd", "ef", periodic=True)
+ assert nx.is_isomorphic(H, G)
+ G = nx.grid_2d_graph(5, 6)
+ H = nx.grid_2d_graph(range(5), range(6))
+ assert edges_equal(H, G)
+
+
+class TestGridGraph:
+ """Unit tests for :func:`networkx.generators.lattice.grid_graph`"""
+
+ def test_grid_graph(self):
+ """grid_graph([n,m]) is a connected simple graph with the
+ following properties:
+ number_of_nodes = n*m
+ degree_histogram = [0,0,4,2*(n+m)-8,(n-2)*(m-2)]
+ """
+ for n, m in [(3, 5), (5, 3), (4, 5), (5, 4)]:
+ dim = [n, m]
+ g = nx.grid_graph(dim)
+ assert len(g) == n * m
+ assert nx.degree_histogram(g) == [
+ 0,
+ 0,
+ 4,
+ 2 * (n + m) - 8,
+ (n - 2) * (m - 2),
+ ]
+
+ for n, m in [(1, 5), (5, 1)]:
+ dim = [n, m]
+ g = nx.grid_graph(dim)
+ assert len(g) == n * m
+ assert nx.is_isomorphic(g, nx.path_graph(5))
+
+ # mg = nx.grid_graph([n,m], create_using=MultiGraph())
+ # assert_equal(mg.edges(), g.edges())
+
+ def test_node_input(self):
+ G = nx.grid_graph([range(7, 9), range(3, 6)])
+ assert len(G) == 2 * 3
+ assert nx.is_isomorphic(G, nx.grid_graph([2, 3]))
+
+ def test_periodic_iterable(self):
+ m, n, k = 3, 7, 5
+ for a, b, c in product([0, 1], [0, 1], [0, 1]):
+ G = nx.grid_graph([m, n, k], periodic=(a, b, c))
+ num_e = (m + a - 1) * n * k + (n + b - 1) * m * k + (k + c - 1) * m * n
+ assert G.number_of_nodes() == m * n * k
+ assert G.number_of_edges() == num_e
+
+
+class TestHypercubeGraph:
+ """Unit tests for :func:`networkx.generators.lattice.hypercube_graph`"""
+
+ def test_special_cases(self):
+ for n, H in [
+ (0, nx.null_graph()),
+ (1, nx.path_graph(2)),
+ (2, nx.cycle_graph(4)),
+ (3, nx.cubical_graph()),
+ ]:
+ G = nx.hypercube_graph(n)
+ assert nx.could_be_isomorphic(G, H)
+
+ def test_degree_distribution(self):
+ for n in range(1, 10):
+ G = nx.hypercube_graph(n)
+ expected_histogram = [0] * n + [2**n]
+ assert nx.degree_histogram(G) == expected_histogram
+
+
+class TestTriangularLatticeGraph:
+ "Tests for :func:`networkx.generators.lattice.triangular_lattice_graph`"
+
+ def test_lattice_points(self):
+ """Tests that the graph is really a triangular lattice."""
+ for m, n in [(2, 3), (2, 2), (2, 1), (3, 3), (3, 2), (3, 4)]:
+ G = nx.triangular_lattice_graph(m, n)
+ N = (n + 1) // 2
+ assert len(G) == (m + 1) * (1 + N) - (n % 2) * ((m + 1) // 2)
+ for i, j in G.nodes():
+ nbrs = G[(i, j)]
+ if i < N:
+ assert (i + 1, j) in nbrs
+ if j < m:
+ assert (i, j + 1) in nbrs
+ if j < m and (i > 0 or j % 2) and (i < N or (j + 1) % 2):
+ assert (i + 1, j + 1) in nbrs or (i - 1, j + 1) in nbrs
+
+ def test_directed(self):
+ """Tests for creating a directed triangular lattice."""
+ G = nx.triangular_lattice_graph(3, 4, create_using=nx.Graph())
+ H = nx.triangular_lattice_graph(3, 4, create_using=nx.DiGraph())
+ assert H.is_directed()
+ for u, v in H.edges():
+ assert v[1] >= u[1]
+ if v[1] == u[1]:
+ assert v[0] > u[0]
+
+ def test_multigraph(self):
+ """Tests for creating a triangular lattice multigraph."""
+ G = nx.triangular_lattice_graph(3, 4, create_using=nx.Graph())
+ H = nx.triangular_lattice_graph(3, 4, create_using=nx.MultiGraph())
+ assert list(H.edges()) == list(G.edges())
+
+ def test_periodic(self):
+ G = nx.triangular_lattice_graph(4, 6, periodic=True)
+ assert len(G) == 12
+ assert G.size() == 36
+ # all degrees are 6
+ assert len([n for n, d in G.degree() if d != 6]) == 0
+ G = nx.triangular_lattice_graph(5, 7, periodic=True)
+ TLG = nx.triangular_lattice_graph
+ pytest.raises(nx.NetworkXError, TLG, 2, 4, periodic=True)
+ pytest.raises(nx.NetworkXError, TLG, 4, 4, periodic=True)
+ pytest.raises(nx.NetworkXError, TLG, 2, 6, periodic=True)
+
+
+class TestHexagonalLatticeGraph:
+ "Tests for :func:`networkx.generators.lattice.hexagonal_lattice_graph`"
+
+ def test_lattice_points(self):
+ """Tests that the graph is really a hexagonal lattice."""
+ for m, n in [(4, 5), (4, 4), (4, 3), (3, 2), (3, 3), (3, 5)]:
+ G = nx.hexagonal_lattice_graph(m, n)
+ assert len(G) == 2 * (m + 1) * (n + 1) - 2
+ C_6 = nx.cycle_graph(6)
+ hexagons = [
+ [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)],
+ [(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4)],
+ [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3)],
+ [(2, 0), (2, 1), (2, 2), (3, 0), (3, 1), (3, 2)],
+ [(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4)],
+ ]
+ for hexagon in hexagons:
+ assert nx.is_isomorphic(G.subgraph(hexagon), C_6)
+
+ def test_directed(self):
+ """Tests for creating a directed hexagonal lattice."""
+ G = nx.hexagonal_lattice_graph(3, 5, create_using=nx.Graph())
+ H = nx.hexagonal_lattice_graph(3, 5, create_using=nx.DiGraph())
+ assert H.is_directed()
+ pos = nx.get_node_attributes(H, "pos")
+ for u, v in H.edges():
+ assert pos[v][1] >= pos[u][1]
+ if pos[v][1] == pos[u][1]:
+ assert pos[v][0] > pos[u][0]
+
+ def test_multigraph(self):
+ """Tests for creating a hexagonal lattice multigraph."""
+ G = nx.hexagonal_lattice_graph(3, 5, create_using=nx.Graph())
+ H = nx.hexagonal_lattice_graph(3, 5, create_using=nx.MultiGraph())
+ assert list(H.edges()) == list(G.edges())
+
+ def test_periodic(self):
+ G = nx.hexagonal_lattice_graph(4, 6, periodic=True)
+ assert len(G) == 48
+ assert G.size() == 72
+ # all degrees are 3
+ assert len([n for n, d in G.degree() if d != 3]) == 0
+ G = nx.hexagonal_lattice_graph(5, 8, periodic=True)
+ HLG = nx.hexagonal_lattice_graph
+ pytest.raises(nx.NetworkXError, HLG, 2, 7, periodic=True)
+ pytest.raises(nx.NetworkXError, HLG, 1, 4, periodic=True)
+ pytest.raises(nx.NetworkXError, HLG, 2, 1, periodic=True)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_line.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_line.py
new file mode 100644
index 00000000..7f5454eb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_line.py
@@ -0,0 +1,309 @@
+import pytest
+
+import networkx as nx
+from networkx.generators import line
+from networkx.utils import edges_equal
+
+
+class TestGeneratorLine:
+ def test_star(self):
+ G = nx.star_graph(5)
+ L = nx.line_graph(G)
+ assert nx.is_isomorphic(L, nx.complete_graph(5))
+
+ def test_path(self):
+ G = nx.path_graph(5)
+ L = nx.line_graph(G)
+ assert nx.is_isomorphic(L, nx.path_graph(4))
+
+ def test_cycle(self):
+ G = nx.cycle_graph(5)
+ L = nx.line_graph(G)
+ assert nx.is_isomorphic(L, G)
+
+ def test_digraph1(self):
+ G = nx.DiGraph([(0, 1), (0, 2), (0, 3)])
+ L = nx.line_graph(G)
+ # no edge graph, but with nodes
+ assert L.adj == {(0, 1): {}, (0, 2): {}, (0, 3): {}}
+
+ def test_multigraph1(self):
+ G = nx.MultiGraph([(0, 1), (0, 1), (1, 0), (0, 2), (2, 0), (0, 3)])
+ L = nx.line_graph(G)
+ # no edge graph, but with nodes
+ assert edges_equal(
+ L.edges(),
+ [
+ ((0, 3, 0), (0, 1, 0)),
+ ((0, 3, 0), (0, 2, 0)),
+ ((0, 3, 0), (0, 2, 1)),
+ ((0, 3, 0), (0, 1, 1)),
+ ((0, 3, 0), (0, 1, 2)),
+ ((0, 1, 0), (0, 1, 1)),
+ ((0, 1, 0), (0, 2, 0)),
+ ((0, 1, 0), (0, 1, 2)),
+ ((0, 1, 0), (0, 2, 1)),
+ ((0, 1, 1), (0, 1, 2)),
+ ((0, 1, 1), (0, 2, 0)),
+ ((0, 1, 1), (0, 2, 1)),
+ ((0, 1, 2), (0, 2, 0)),
+ ((0, 1, 2), (0, 2, 1)),
+ ((0, 2, 0), (0, 2, 1)),
+ ],
+ )
+
+ def test_multigraph2(self):
+ G = nx.MultiGraph([(1, 2), (2, 1)])
+ L = nx.line_graph(G)
+ assert edges_equal(L.edges(), [((1, 2, 0), (1, 2, 1))])
+
+ def test_multidigraph1(self):
+ G = nx.MultiDiGraph([(1, 2), (2, 1)])
+ L = nx.line_graph(G)
+ assert edges_equal(L.edges(), [((1, 2, 0), (2, 1, 0)), ((2, 1, 0), (1, 2, 0))])
+
+ def test_multidigraph2(self):
+ G = nx.MultiDiGraph([(0, 1), (0, 1), (0, 1), (1, 2)])
+ L = nx.line_graph(G)
+ assert edges_equal(
+ L.edges(),
+ [((0, 1, 0), (1, 2, 0)), ((0, 1, 1), (1, 2, 0)), ((0, 1, 2), (1, 2, 0))],
+ )
+
+ def test_digraph2(self):
+ G = nx.DiGraph([(0, 1), (1, 2), (2, 3)])
+ L = nx.line_graph(G)
+ assert edges_equal(L.edges(), [((0, 1), (1, 2)), ((1, 2), (2, 3))])
+
+ def test_create1(self):
+ G = nx.DiGraph([(0, 1), (1, 2), (2, 3)])
+ L = nx.line_graph(G, create_using=nx.Graph())
+ assert edges_equal(L.edges(), [((0, 1), (1, 2)), ((1, 2), (2, 3))])
+
+ def test_create2(self):
+ G = nx.Graph([(0, 1), (1, 2), (2, 3)])
+ L = nx.line_graph(G, create_using=nx.DiGraph())
+ assert edges_equal(L.edges(), [((0, 1), (1, 2)), ((1, 2), (2, 3))])
+
+
+class TestGeneratorInverseLine:
+ def test_example(self):
+ G = nx.Graph()
+ G_edges = [
+ [1, 2],
+ [1, 3],
+ [1, 4],
+ [1, 5],
+ [2, 3],
+ [2, 5],
+ [2, 6],
+ [2, 7],
+ [3, 4],
+ [3, 5],
+ [6, 7],
+ [6, 8],
+ [7, 8],
+ ]
+ G.add_edges_from(G_edges)
+ H = nx.inverse_line_graph(G)
+ solution = nx.Graph()
+ solution_edges = [
+ ("a", "b"),
+ ("a", "c"),
+ ("a", "d"),
+ ("a", "e"),
+ ("c", "d"),
+ ("e", "f"),
+ ("e", "g"),
+ ("f", "g"),
+ ]
+ solution.add_edges_from(solution_edges)
+ assert nx.is_isomorphic(H, solution)
+
+ def test_example_2(self):
+ G = nx.Graph()
+ G_edges = [[1, 2], [1, 3], [2, 3], [3, 4], [3, 5], [4, 5]]
+ G.add_edges_from(G_edges)
+ H = nx.inverse_line_graph(G)
+ solution = nx.Graph()
+ solution_edges = [("a", "c"), ("b", "c"), ("c", "d"), ("d", "e"), ("d", "f")]
+ solution.add_edges_from(solution_edges)
+ assert nx.is_isomorphic(H, solution)
+
+ def test_pair(self):
+ G = nx.path_graph(2)
+ H = nx.inverse_line_graph(G)
+ solution = nx.path_graph(3)
+ assert nx.is_isomorphic(H, solution)
+
+ def test_line(self):
+ G = nx.path_graph(5)
+ solution = nx.path_graph(6)
+ H = nx.inverse_line_graph(G)
+ assert nx.is_isomorphic(H, solution)
+
+ def test_triangle_graph(self):
+ G = nx.complete_graph(3)
+ H = nx.inverse_line_graph(G)
+ alternative_solution = nx.Graph()
+ alternative_solution.add_edges_from([[0, 1], [0, 2], [0, 3]])
+ # there are two alternative inverse line graphs for this case
+ # so long as we get one of them the test should pass
+ assert nx.is_isomorphic(H, G) or nx.is_isomorphic(H, alternative_solution)
+
+ def test_cycle(self):
+ G = nx.cycle_graph(5)
+ H = nx.inverse_line_graph(G)
+ assert nx.is_isomorphic(H, G)
+
+ def test_empty(self):
+ G = nx.Graph()
+ H = nx.inverse_line_graph(G)
+ assert nx.is_isomorphic(H, nx.complete_graph(1))
+
+ def test_K1(self):
+ G = nx.complete_graph(1)
+ H = nx.inverse_line_graph(G)
+ solution = nx.path_graph(2)
+ assert nx.is_isomorphic(H, solution)
+
+ def test_edgeless_graph(self):
+ G = nx.empty_graph(5)
+ with pytest.raises(nx.NetworkXError, match="edgeless graph"):
+ nx.inverse_line_graph(G)
+
+ def test_selfloops_error(self):
+ G = nx.cycle_graph(4)
+ G.add_edge(0, 0)
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ def test_non_line_graphs(self):
+ # Tests several known non-line graphs for impossibility
+ # Adapted from L.W.Beineke, "Characterizations of derived graphs"
+
+ # claw graph
+ claw = nx.star_graph(3)
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, claw)
+
+ # wheel graph with 6 nodes
+ wheel = nx.wheel_graph(6)
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, wheel)
+
+ # K5 with one edge remove
+ K5m = nx.complete_graph(5)
+ K5m.remove_edge(0, 1)
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, K5m)
+
+ # graph without any odd triangles (contains claw as induced subgraph)
+ G = nx.compose(nx.path_graph(2), nx.complete_bipartite_graph(2, 3))
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ ## Variations on a diamond graph
+
+ # Diamond + 2 edges (+ "roof")
+ G = nx.diamond_graph()
+ G.add_edges_from([(4, 0), (5, 3)])
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+ G.add_edge(4, 5)
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ # Diamond + 2 connected edges
+ G = nx.diamond_graph()
+ G.add_edges_from([(4, 0), (4, 3)])
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ # Diamond + K3 + one edge (+ 2*K3)
+ G = nx.diamond_graph()
+ G.add_edges_from([(4, 0), (4, 1), (4, 2), (5, 3)])
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+ G.add_edges_from([(5, 1), (5, 2)])
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ # 4 triangles
+ G = nx.diamond_graph()
+ G.add_edges_from([(4, 0), (4, 1), (5, 2), (5, 3)])
+ pytest.raises(nx.NetworkXError, nx.inverse_line_graph, G)
+
+ def test_wrong_graph_type(self):
+ G = nx.DiGraph()
+ G_edges = [[0, 1], [0, 2], [0, 3]]
+ G.add_edges_from(G_edges)
+ pytest.raises(nx.NetworkXNotImplemented, nx.inverse_line_graph, G)
+
+ G = nx.MultiGraph()
+ G_edges = [[0, 1], [0, 2], [0, 3]]
+ G.add_edges_from(G_edges)
+ pytest.raises(nx.NetworkXNotImplemented, nx.inverse_line_graph, G)
+
+ def test_line_inverse_line_complete(self):
+ G = nx.complete_graph(10)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_path(self):
+ G = nx.path_graph(10)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_hypercube(self):
+ G = nx.hypercube_graph(5)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_cycle(self):
+ G = nx.cycle_graph(10)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_star(self):
+ G = nx.star_graph(20)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_multipartite(self):
+ G = nx.complete_multipartite_graph(3, 4, 5)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_inverse_line_dgm(self):
+ G = nx.dorogovtsev_goltsev_mendes_graph(4)
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+ def test_line_different_node_types(self):
+ G = nx.path_graph([1, 2, 3, "a", "b", "c"])
+ H = nx.line_graph(G)
+ J = nx.inverse_line_graph(H)
+ assert nx.is_isomorphic(G, J)
+
+
+class TestGeneratorPrivateFunctions:
+ def test_triangles_error(self):
+ G = nx.diamond_graph()
+ pytest.raises(nx.NetworkXError, line._triangles, G, (4, 0))
+ pytest.raises(nx.NetworkXError, line._triangles, G, (0, 3))
+
+ def test_odd_triangles_error(self):
+ G = nx.diamond_graph()
+ pytest.raises(nx.NetworkXError, line._odd_triangle, G, (0, 1, 4))
+ pytest.raises(nx.NetworkXError, line._odd_triangle, G, (0, 1, 3))
+
+ def test_select_starting_cell_error(self):
+ G = nx.diamond_graph()
+ pytest.raises(nx.NetworkXError, line._select_starting_cell, G, (4, 0))
+ pytest.raises(nx.NetworkXError, line._select_starting_cell, G, (0, 3))
+
+ def test_diamond_graph(self):
+ G = nx.diamond_graph()
+ for edge in G.edges:
+ cell = line._select_starting_cell(G, starting_edge=edge)
+ # Starting cell should always be one of the two triangles
+ assert len(cell) == 3
+ assert all(v in G[u] for u in cell for v in cell if u != v)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_mycielski.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_mycielski.py
new file mode 100644
index 00000000..eb12b141
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_mycielski.py
@@ -0,0 +1,30 @@
+"""Unit tests for the :mod:`networkx.generators.mycielski` module."""
+
+import pytest
+
+import networkx as nx
+
+
+class TestMycielski:
+ def test_construction(self):
+ G = nx.path_graph(2)
+ M = nx.mycielskian(G)
+ assert nx.is_isomorphic(M, nx.cycle_graph(5))
+
+ def test_size(self):
+ G = nx.path_graph(2)
+ M = nx.mycielskian(G, 2)
+ assert len(M) == 11
+ assert M.size() == 20
+
+ def test_mycielski_graph_generator(self):
+ G = nx.mycielski_graph(1)
+ assert nx.is_isomorphic(G, nx.empty_graph(1))
+ G = nx.mycielski_graph(2)
+ assert nx.is_isomorphic(G, nx.path_graph(2))
+ G = nx.mycielski_graph(3)
+ assert nx.is_isomorphic(G, nx.cycle_graph(5))
+ G = nx.mycielski_graph(4)
+ assert nx.is_isomorphic(G, nx.mycielskian(nx.cycle_graph(5)))
+ with pytest.raises(nx.NetworkXError, match="must satisfy n >= 1"):
+ nx.mycielski_graph(0)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_nonisomorphic_trees.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_nonisomorphic_trees.py
new file mode 100644
index 00000000..c73d44ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_nonisomorphic_trees.py
@@ -0,0 +1,68 @@
+"""
+Unit tests for WROM algorithm generator in generators/nonisomorphic_trees.py
+"""
+
+import pytest
+
+import networkx as nx
+from networkx.utils import edges_equal
+
+
+class TestGeneratorNonIsomorphicTrees:
+ def test_tree_structure(self):
+ # test for tree structure for nx.nonisomorphic_trees()
+ def f(x):
+ return list(nx.nonisomorphic_trees(x))
+
+ for i in f(6):
+ assert nx.is_tree(i)
+ for i in f(8):
+ assert nx.is_tree(i)
+
+ def test_nonisomorphism(self):
+ # test for nonisomorphism of trees for nx.nonisomorphic_trees()
+ def f(x):
+ return list(nx.nonisomorphic_trees(x))
+
+ trees = f(6)
+ for i in range(len(trees)):
+ for j in range(i + 1, len(trees)):
+ assert not nx.is_isomorphic(trees[i], trees[j])
+ trees = f(8)
+ for i in range(len(trees)):
+ for j in range(i + 1, len(trees)):
+ assert not nx.is_isomorphic(trees[i], trees[j])
+
+ def test_number_of_nonisomorphic_trees(self):
+ # http://oeis.org/A000055
+ assert nx.number_of_nonisomorphic_trees(2) == 1
+ assert nx.number_of_nonisomorphic_trees(3) == 1
+ assert nx.number_of_nonisomorphic_trees(4) == 2
+ assert nx.number_of_nonisomorphic_trees(5) == 3
+ assert nx.number_of_nonisomorphic_trees(6) == 6
+ assert nx.number_of_nonisomorphic_trees(7) == 11
+ assert nx.number_of_nonisomorphic_trees(8) == 23
+
+ def test_nonisomorphic_trees(self):
+ def f(x):
+ return list(nx.nonisomorphic_trees(x))
+
+ assert edges_equal(f(3)[0].edges(), [(0, 1), (0, 2)])
+ assert edges_equal(f(4)[0].edges(), [(0, 1), (0, 3), (1, 2)])
+ assert edges_equal(f(4)[1].edges(), [(0, 1), (0, 2), (0, 3)])
+
+ def test_nonisomorphic_trees_matrix(self):
+ trees_2 = [[[0, 1], [1, 0]]]
+ with pytest.deprecated_call():
+ assert list(nx.nonisomorphic_trees(2, create="matrix")) == trees_2
+
+ trees_3 = [[[0, 1, 1], [1, 0, 0], [1, 0, 0]]]
+ with pytest.deprecated_call():
+ assert list(nx.nonisomorphic_trees(3, create="matrix")) == trees_3
+
+ trees_4 = [
+ [[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]],
+ [[0, 1, 1, 1], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]],
+ ]
+ with pytest.deprecated_call():
+ assert list(nx.nonisomorphic_trees(4, create="matrix")) == trees_4
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_clustered.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_clustered.py
new file mode 100644
index 00000000..85066520
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_clustered.py
@@ -0,0 +1,33 @@
+import pytest
+
+import networkx as nx
+
+
+class TestRandomClusteredGraph:
+ def test_custom_joint_degree_sequence(self):
+ node = [1, 1, 1, 2, 1, 2, 0, 0]
+ tri = [0, 0, 0, 0, 0, 1, 1, 1]
+ joint_degree_sequence = zip(node, tri)
+ G = nx.random_clustered_graph(joint_degree_sequence)
+ assert G.number_of_nodes() == 8
+ assert G.number_of_edges() == 7
+
+ def test_tuple_joint_degree_sequence(self):
+ G = nx.random_clustered_graph([(1, 2), (2, 1), (1, 1), (1, 1), (1, 1), (2, 0)])
+ assert G.number_of_nodes() == 6
+ assert G.number_of_edges() == 10
+
+ def test_invalid_joint_degree_sequence_type(self):
+ with pytest.raises(nx.NetworkXError, match="Invalid degree sequence"):
+ nx.random_clustered_graph([[1, 1], [2, 1], [0, 1]])
+
+ def test_invalid_joint_degree_sequence_value(self):
+ with pytest.raises(nx.NetworkXError, match="Invalid degree sequence"):
+ nx.random_clustered_graph([[1, 1], [1, 2], [0, 1]])
+
+ def test_directed_graph_raises_error(self):
+ with pytest.raises(nx.NetworkXError, match="Directed Graph not supported"):
+ nx.random_clustered_graph(
+ [(1, 2), (2, 1), (1, 1), (1, 1), (1, 1), (2, 0)],
+ create_using=nx.DiGraph,
+ )
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_graphs.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_graphs.py
new file mode 100644
index 00000000..3262e542
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_random_graphs.py
@@ -0,0 +1,478 @@
+"""Unit tests for the :mod:`networkx.generators.random_graphs` module."""
+
+import pytest
+
+import networkx as nx
+
+_gnp_generators = [
+ nx.gnp_random_graph,
+ nx.fast_gnp_random_graph,
+ nx.binomial_graph,
+ nx.erdos_renyi_graph,
+]
+
+
+@pytest.mark.parametrize("generator", _gnp_generators)
+@pytest.mark.parametrize("directed", (True, False))
+def test_gnp_generators_negative_edge_probability(generator, directed):
+ """If the edge probability `p` is <=0, the resulting graph should have no edges."""
+ G = generator(10, -1.1, directed=directed)
+ assert len(G) == 10
+ assert G.number_of_edges() == 0
+ assert G.is_directed() == directed
+
+
+@pytest.mark.parametrize("generator", _gnp_generators)
+@pytest.mark.parametrize(
+ ("directed", "expected_num_edges"),
+ [(False, 45), (True, 90)],
+)
+def test_gnp_generators_greater_than_1_edge_probability(
+ generator, directed, expected_num_edges
+):
+ """If the edge probability `p` is >=1, the resulting graph should be complete."""
+ G = generator(10, 1.1, directed=directed)
+ assert len(G) == 10
+ assert G.number_of_edges() == expected_num_edges
+ assert G.is_directed() == directed
+
+
+@pytest.mark.parametrize("generator", _gnp_generators)
+@pytest.mark.parametrize("directed", (True, False))
+def test_gnp_generators_basic(generator, directed):
+ """If the edge probability `p` is >0 and <1, test only the basic properties."""
+ G = generator(10, 0.1, directed=directed)
+ assert len(G) == 10
+ assert G.is_directed() == directed
+
+
+@pytest.mark.parametrize("generator", _gnp_generators)
+def test_gnp_generators_for_p_close_to_1(generator):
+ """If the edge probability `p` is close to 1, the resulting graph should have all edges."""
+ runs = 100
+ edges = sum(
+ generator(10, 0.99999, directed=True).number_of_edges() for _ in range(runs)
+ )
+ assert abs(edges / float(runs) - 90) <= runs * 2.0 / 100
+
+
+@pytest.mark.parametrize("generator", _gnp_generators)
+@pytest.mark.parametrize("p", (0.2, 0.8))
+@pytest.mark.parametrize("directed", (True, False))
+def test_gnp_generators_edge_probability(generator, p, directed):
+ """Test that gnp generators generate edges according to the their probability `p`."""
+ runs = 5000
+ n = 5
+ edge_counts = [[0] * n for _ in range(n)]
+ for i in range(runs):
+ G = generator(n, p, directed=directed)
+ for v, w in G.edges:
+ edge_counts[v][w] += 1
+ if not directed:
+ edge_counts[w][v] += 1
+ for v in range(n):
+ for w in range(n):
+ if v == w:
+ # There should be no loops
+ assert edge_counts[v][w] == 0
+ else:
+ # Each edge should have been generated with probability close to p
+ assert abs(edge_counts[v][w] / float(runs) - p) <= 0.03
+
+
+@pytest.mark.parametrize(
+ "generator", [nx.gnp_random_graph, nx.binomial_graph, nx.erdos_renyi_graph]
+)
+@pytest.mark.parametrize(
+ ("seed", "directed", "expected_num_edges"),
+ [(42, False, 1219), (42, True, 2454), (314, False, 1247), (314, True, 2476)],
+)
+def test_gnp_random_graph_aliases(generator, seed, directed, expected_num_edges):
+ """Test that aliases give the same result with the same seed."""
+ G = generator(100, 0.25, seed=seed, directed=directed)
+ assert len(G) == 100
+ assert G.number_of_edges() == expected_num_edges
+ assert G.is_directed() == directed
+
+
+class TestGeneratorsRandom:
+ def test_random_graph(self):
+ seed = 42
+ G = nx.gnm_random_graph(100, 20, seed)
+ G = nx.gnm_random_graph(100, 20, seed, directed=True)
+ G = nx.dense_gnm_random_graph(100, 20, seed)
+
+ G = nx.barabasi_albert_graph(100, 1, seed)
+ G = nx.barabasi_albert_graph(100, 3, seed)
+ assert G.number_of_edges() == (97 * 3)
+
+ G = nx.barabasi_albert_graph(100, 3, seed, nx.complete_graph(5))
+ assert G.number_of_edges() == (10 + 95 * 3)
+
+ G = nx.extended_barabasi_albert_graph(100, 1, 0, 0, seed)
+ assert G.number_of_edges() == 99
+ G = nx.extended_barabasi_albert_graph(100, 3, 0, 0, seed)
+ assert G.number_of_edges() == 97 * 3
+ G = nx.extended_barabasi_albert_graph(100, 1, 0, 0.5, seed)
+ assert G.number_of_edges() == 99
+ G = nx.extended_barabasi_albert_graph(100, 2, 0.5, 0, seed)
+ assert G.number_of_edges() > 100 * 3
+ assert G.number_of_edges() < 100 * 4
+
+ G = nx.extended_barabasi_albert_graph(100, 2, 0.3, 0.3, seed)
+ assert G.number_of_edges() > 100 * 2
+ assert G.number_of_edges() < 100 * 4
+
+ G = nx.powerlaw_cluster_graph(100, 1, 1.0, seed)
+ G = nx.powerlaw_cluster_graph(100, 3, 0.0, seed)
+ assert G.number_of_edges() == (97 * 3)
+
+ G = nx.random_regular_graph(10, 20, seed)
+
+ pytest.raises(nx.NetworkXError, nx.random_regular_graph, 3, 21)
+ pytest.raises(nx.NetworkXError, nx.random_regular_graph, 33, 21)
+
+ constructor = [(10, 20, 0.8), (20, 40, 0.8)]
+ G = nx.random_shell_graph(constructor, seed)
+
+ def is_caterpillar(g):
+ """
+ A tree is a caterpillar iff all nodes of degree >=3 are surrounded
+ by at most two nodes of degree two or greater.
+ ref: http://mathworld.wolfram.com/CaterpillarGraph.html
+ """
+ deg_over_3 = [n for n in g if g.degree(n) >= 3]
+ for n in deg_over_3:
+ nbh_deg_over_2 = [nbh for nbh in g.neighbors(n) if g.degree(nbh) >= 2]
+ if not len(nbh_deg_over_2) <= 2:
+ return False
+ return True
+
+ def is_lobster(g):
+ """
+ A tree is a lobster if it has the property that the removal of leaf
+ nodes leaves a caterpillar graph (Gallian 2007)
+ ref: http://mathworld.wolfram.com/LobsterGraph.html
+ """
+ non_leafs = [n for n in g if g.degree(n) > 1]
+ return is_caterpillar(g.subgraph(non_leafs))
+
+ G = nx.random_lobster(10, 0.1, 0.5, seed)
+ assert max(G.degree(n) for n in G.nodes()) > 3
+ assert is_lobster(G)
+ pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 0.1, 1, seed)
+ pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 1, 1, seed)
+ pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 1, 0.5, seed)
+
+ # docstring says this should be a caterpillar
+ G = nx.random_lobster(10, 0.1, 0.0, seed)
+ assert is_caterpillar(G)
+
+ # difficult to find seed that requires few tries
+ seq = nx.random_powerlaw_tree_sequence(10, 3, seed=14, tries=1)
+ G = nx.random_powerlaw_tree(10, 3, seed=14, tries=1)
+
+ def test_dual_barabasi_albert(self, m1=1, m2=4, p=0.5):
+ """
+ Tests that the dual BA random graph generated behaves consistently.
+
+ Tests the exceptions are raised as expected.
+
+ The graphs generation are repeated several times to prevent lucky shots
+
+ """
+ seeds = [42, 314, 2718]
+ initial_graph = nx.complete_graph(10)
+
+ for seed in seeds:
+ # This should be BA with m = m1
+ BA1 = nx.barabasi_albert_graph(100, m1, seed)
+ DBA1 = nx.dual_barabasi_albert_graph(100, m1, m2, 1, seed)
+ assert BA1.edges() == DBA1.edges()
+
+ # This should be BA with m = m2
+ BA2 = nx.barabasi_albert_graph(100, m2, seed)
+ DBA2 = nx.dual_barabasi_albert_graph(100, m1, m2, 0, seed)
+ assert BA2.edges() == DBA2.edges()
+
+ BA3 = nx.barabasi_albert_graph(100, m1, seed)
+ DBA3 = nx.dual_barabasi_albert_graph(100, m1, m1, p, seed)
+ # We can't compare edges here since randomness is "consumed" when drawing
+ # between m1 and m2
+ assert BA3.size() == DBA3.size()
+
+ DBA = nx.dual_barabasi_albert_graph(100, m1, m2, p, seed, initial_graph)
+ BA1 = nx.barabasi_albert_graph(100, m1, seed, initial_graph)
+ BA2 = nx.barabasi_albert_graph(100, m2, seed, initial_graph)
+ assert (
+ min(BA1.size(), BA2.size()) <= DBA.size() <= max(BA1.size(), BA2.size())
+ )
+
+ # Testing exceptions
+ dbag = nx.dual_barabasi_albert_graph
+ pytest.raises(nx.NetworkXError, dbag, m1, m1, m2, 0)
+ pytest.raises(nx.NetworkXError, dbag, m2, m1, m2, 0)
+ pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, -0.5)
+ pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, 1.5)
+ initial = nx.complete_graph(max(m1, m2) - 1)
+ pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, p, initial_graph=initial)
+
+ def test_extended_barabasi_albert(self, m=2):
+ """
+ Tests that the extended BA random graph generated behaves consistently.
+
+ Tests the exceptions are raised as expected.
+
+ The graphs generation are repeated several times to prevent lucky-shots
+
+ """
+ seeds = [42, 314, 2718]
+
+ for seed in seeds:
+ BA_model = nx.barabasi_albert_graph(100, m, seed)
+ BA_model_edges = BA_model.number_of_edges()
+
+ # This behaves just like BA, the number of edges must be the same
+ G1 = nx.extended_barabasi_albert_graph(100, m, 0, 0, seed)
+ assert G1.size() == BA_model_edges
+
+ # More than twice more edges should have been added
+ G1 = nx.extended_barabasi_albert_graph(100, m, 0.8, 0, seed)
+ assert G1.size() > BA_model_edges * 2
+
+ # Only edge rewiring, so the number of edges less than original
+ G2 = nx.extended_barabasi_albert_graph(100, m, 0, 0.8, seed)
+ assert G2.size() == BA_model_edges
+
+ # Mixed scenario: less edges than G1 and more edges than G2
+ G3 = nx.extended_barabasi_albert_graph(100, m, 0.3, 0.3, seed)
+ assert G3.size() > G2.size()
+ assert G3.size() < G1.size()
+
+ # Testing exceptions
+ ebag = nx.extended_barabasi_albert_graph
+ pytest.raises(nx.NetworkXError, ebag, m, m, 0, 0)
+ pytest.raises(nx.NetworkXError, ebag, 1, 0.5, 0, 0)
+ pytest.raises(nx.NetworkXError, ebag, 100, 2, 0.5, 0.5)
+
+ def test_random_zero_regular_graph(self):
+ """Tests that a 0-regular graph has the correct number of nodes and
+ edges.
+
+ """
+ seed = 42
+ G = nx.random_regular_graph(0, 10, seed)
+ assert len(G) == 10
+ assert G.number_of_edges() == 0
+
+ def test_gnm(self):
+ G = nx.gnm_random_graph(10, 3)
+ assert len(G) == 10
+ assert G.number_of_edges() == 3
+
+ G = nx.gnm_random_graph(10, 3, seed=42)
+ assert len(G) == 10
+ assert G.number_of_edges() == 3
+
+ G = nx.gnm_random_graph(10, 100)
+ assert len(G) == 10
+ assert G.number_of_edges() == 45
+
+ G = nx.gnm_random_graph(10, 100, directed=True)
+ assert len(G) == 10
+ assert G.number_of_edges() == 90
+
+ G = nx.gnm_random_graph(10, -1.1)
+ assert len(G) == 10
+ assert G.number_of_edges() == 0
+
+ def test_watts_strogatz_big_k(self):
+ # Test to make sure than n <= k
+ pytest.raises(nx.NetworkXError, nx.watts_strogatz_graph, 10, 11, 0.25)
+ pytest.raises(nx.NetworkXError, nx.newman_watts_strogatz_graph, 10, 11, 0.25)
+
+ # could create an infinite loop, now doesn't
+ # infinite loop used to occur when a node has degree n-1 and needs to rewire
+ nx.watts_strogatz_graph(10, 9, 0.25, seed=0)
+ nx.newman_watts_strogatz_graph(10, 9, 0.5, seed=0)
+
+ # Test k==n scenario
+ nx.watts_strogatz_graph(10, 10, 0.25, seed=0)
+ nx.newman_watts_strogatz_graph(10, 10, 0.25, seed=0)
+
+ def test_random_kernel_graph(self):
+ def integral(u, w, z):
+ return c * (z - w)
+
+ def root(u, w, r):
+ return r / c + w
+
+ c = 1
+ graph = nx.random_kernel_graph(1000, integral, root)
+ graph = nx.random_kernel_graph(1000, integral, root, seed=42)
+ assert len(graph) == 1000
+
+
+@pytest.mark.parametrize(
+ ("k", "expected_num_nodes", "expected_num_edges"),
+ [
+ (2, 10, 10),
+ (4, 10, 20),
+ ],
+)
+def test_watts_strogatz(k, expected_num_nodes, expected_num_edges):
+ G = nx.watts_strogatz_graph(10, k, 0.25, seed=42)
+ assert len(G) == expected_num_nodes
+ assert G.number_of_edges() == expected_num_edges
+
+
+def test_newman_watts_strogatz_zero_probability():
+ G = nx.newman_watts_strogatz_graph(10, 2, 0.0, seed=42)
+ assert len(G) == 10
+ assert G.number_of_edges() == 10
+
+
+def test_newman_watts_strogatz_nonzero_probability():
+ G = nx.newman_watts_strogatz_graph(10, 4, 0.25, seed=42)
+ assert len(G) == 10
+ assert G.number_of_edges() >= 20
+
+
+def test_connected_watts_strogatz():
+ G = nx.connected_watts_strogatz_graph(10, 2, 0.1, tries=10, seed=42)
+ assert len(G) == 10
+ assert G.number_of_edges() == 10
+
+
+def test_connected_watts_strogatz_zero_tries():
+ with pytest.raises(nx.NetworkXError, match="Maximum number of tries exceeded"):
+ nx.connected_watts_strogatz_graph(10, 2, 0.1, tries=0)
+
+
+@pytest.mark.parametrize(
+ "generator, kwargs",
+ [
+ (nx.fast_gnp_random_graph, {"n": 20, "p": 0.2, "directed": False}),
+ (nx.fast_gnp_random_graph, {"n": 20, "p": 0.2, "directed": True}),
+ (nx.gnp_random_graph, {"n": 20, "p": 0.2, "directed": False}),
+ (nx.gnp_random_graph, {"n": 20, "p": 0.2, "directed": True}),
+ (nx.dense_gnm_random_graph, {"n": 30, "m": 4}),
+ (nx.gnm_random_graph, {"n": 30, "m": 4, "directed": False}),
+ (nx.gnm_random_graph, {"n": 30, "m": 4, "directed": True}),
+ (nx.newman_watts_strogatz_graph, {"n": 50, "k": 5, "p": 0.1}),
+ (nx.watts_strogatz_graph, {"n": 50, "k": 5, "p": 0.1}),
+ (nx.connected_watts_strogatz_graph, {"n": 50, "k": 5, "p": 0.1}),
+ (nx.random_regular_graph, {"d": 5, "n": 20}),
+ (nx.barabasi_albert_graph, {"n": 40, "m": 3}),
+ (nx.dual_barabasi_albert_graph, {"n": 40, "m1": 3, "m2": 2, "p": 0.1}),
+ (nx.extended_barabasi_albert_graph, {"n": 40, "m": 3, "p": 0.1, "q": 0.2}),
+ (nx.powerlaw_cluster_graph, {"n": 40, "m": 3, "p": 0.1}),
+ (nx.random_lobster, {"n": 40, "p1": 0.1, "p2": 0.2}),
+ (nx.random_shell_graph, {"constructor": [(10, 20, 0.8), (20, 40, 0.8)]}),
+ (nx.random_powerlaw_tree, {"n": 10, "seed": 14, "tries": 1}),
+ (
+ nx.random_kernel_graph,
+ {
+ "n": 10,
+ "kernel_integral": lambda u, w, z: z - w,
+ "kernel_root": lambda u, w, r: r + w,
+ },
+ ),
+ ],
+)
+@pytest.mark.parametrize("create_using_instance", [False, True])
+def test_create_using(generator, kwargs, create_using_instance):
+ class DummyGraph(nx.Graph):
+ pass
+
+ class DummyDiGraph(nx.DiGraph):
+ pass
+
+ create_using_type = DummyDiGraph if kwargs.get("directed") else DummyGraph
+ create_using = create_using_type() if create_using_instance else create_using_type
+ graph = generator(**kwargs, create_using=create_using)
+ assert isinstance(graph, create_using_type)
+
+
+@pytest.mark.parametrize("directed", [True, False])
+@pytest.mark.parametrize("fn", (nx.fast_gnp_random_graph, nx.gnp_random_graph))
+def test_gnp_fns_disallow_multigraph(fn, directed):
+ with pytest.raises(nx.NetworkXError, match="must not be a multi-graph"):
+ fn(20, 0.2, create_using=nx.MultiGraph)
+
+
+@pytest.mark.parametrize("fn", (nx.gnm_random_graph, nx.dense_gnm_random_graph))
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_gnm_fns_disallow_directed_and_multigraph(fn, graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ fn(10, 20, create_using=graphtype)
+
+
+@pytest.mark.parametrize(
+ "fn",
+ (
+ nx.newman_watts_strogatz_graph,
+ nx.watts_strogatz_graph,
+ nx.connected_watts_strogatz_graph,
+ ),
+)
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_watts_strogatz_disallow_directed_and_multigraph(fn, graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ fn(10, 2, 0.2, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_random_regular_graph_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.random_regular_graph(2, 10, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_barabasi_albert_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.barabasi_albert_graph(10, 3, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_dual_barabasi_albert_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.dual_barabasi_albert_graph(10, 2, 1, 0.4, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_extended_barabasi_albert_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.extended_barabasi_albert_graph(10, 2, 0.2, 0.3, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_powerlaw_cluster_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.powerlaw_cluster_graph(10, 5, 0.2, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_random_lobster_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.random_lobster(10, 0.1, 0.1, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_random_shell_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.random_shell_graph([(10, 20, 2), (10, 20, 5)], create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_random_powerlaw_tree_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.random_powerlaw_tree(10, create_using=graphtype)
+
+
+@pytest.mark.parametrize("graphtype", (nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph))
+def test_random_kernel_disallow_directed_and_multigraph(graphtype):
+ with pytest.raises(nx.NetworkXError, match="must not be"):
+ nx.random_kernel_graph(
+ 10, lambda y, a, b: a + b, lambda u, w, r: r + w, create_using=graphtype
+ )
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_small.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_small.py
new file mode 100644
index 00000000..355d6d36
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_small.py
@@ -0,0 +1,208 @@
+import pytest
+
+import networkx as nx
+from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic
+
+is_isomorphic = graph_could_be_isomorphic
+
+"""Generators - Small
+=====================
+
+Some small graphs
+"""
+
+null = nx.null_graph()
+
+
+class TestGeneratorsSmall:
+ def test__LCF_graph(self):
+ # If n<=0, then return the null_graph
+ G = nx.LCF_graph(-10, [1, 2], 100)
+ assert is_isomorphic(G, null)
+ G = nx.LCF_graph(0, [1, 2], 3)
+ assert is_isomorphic(G, null)
+ G = nx.LCF_graph(0, [1, 2], 10)
+ assert is_isomorphic(G, null)
+
+ # Test that LCF(n,[],0) == cycle_graph(n)
+ for a, b, c in [(5, [], 0), (10, [], 0), (5, [], 1), (10, [], 10)]:
+ G = nx.LCF_graph(a, b, c)
+ assert is_isomorphic(G, nx.cycle_graph(a))
+
+ # Generate the utility graph K_{3,3}
+ G = nx.LCF_graph(6, [3, -3], 3)
+ utility_graph = nx.complete_bipartite_graph(3, 3)
+ assert is_isomorphic(G, utility_graph)
+
+ with pytest.raises(nx.NetworkXError, match="Directed Graph not supported"):
+ G = nx.LCF_graph(6, [3, -3], 3, create_using=nx.DiGraph)
+
+ def test_properties_named_small_graphs(self):
+ G = nx.bull_graph()
+ assert sorted(G) == list(range(5))
+ assert G.number_of_edges() == 5
+ assert sorted(d for n, d in G.degree()) == [1, 1, 2, 3, 3]
+ assert nx.diameter(G) == 3
+ assert nx.radius(G) == 2
+
+ G = nx.chvatal_graph()
+ assert sorted(G) == list(range(12))
+ assert G.number_of_edges() == 24
+ assert [d for n, d in G.degree()] == 12 * [4]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 2
+
+ G = nx.cubical_graph()
+ assert sorted(G) == list(range(8))
+ assert G.number_of_edges() == 12
+ assert [d for n, d in G.degree()] == 8 * [3]
+ assert nx.diameter(G) == 3
+ assert nx.radius(G) == 3
+
+ G = nx.desargues_graph()
+ assert sorted(G) == list(range(20))
+ assert G.number_of_edges() == 30
+ assert [d for n, d in G.degree()] == 20 * [3]
+
+ G = nx.diamond_graph()
+ assert sorted(G) == list(range(4))
+ assert sorted(d for n, d in G.degree()) == [2, 2, 3, 3]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 1
+
+ G = nx.dodecahedral_graph()
+ assert sorted(G) == list(range(20))
+ assert G.number_of_edges() == 30
+ assert [d for n, d in G.degree()] == 20 * [3]
+ assert nx.diameter(G) == 5
+ assert nx.radius(G) == 5
+
+ G = nx.frucht_graph()
+ assert sorted(G) == list(range(12))
+ assert G.number_of_edges() == 18
+ assert [d for n, d in G.degree()] == 12 * [3]
+ assert nx.diameter(G) == 4
+ assert nx.radius(G) == 3
+
+ G = nx.heawood_graph()
+ assert sorted(G) == list(range(14))
+ assert G.number_of_edges() == 21
+ assert [d for n, d in G.degree()] == 14 * [3]
+ assert nx.diameter(G) == 3
+ assert nx.radius(G) == 3
+
+ G = nx.hoffman_singleton_graph()
+ assert sorted(G) == list(range(50))
+ assert G.number_of_edges() == 175
+ assert [d for n, d in G.degree()] == 50 * [7]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 2
+
+ G = nx.house_graph()
+ assert sorted(G) == list(range(5))
+ assert G.number_of_edges() == 6
+ assert sorted(d for n, d in G.degree()) == [2, 2, 2, 3, 3]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 2
+
+ G = nx.house_x_graph()
+ assert sorted(G) == list(range(5))
+ assert G.number_of_edges() == 8
+ assert sorted(d for n, d in G.degree()) == [2, 3, 3, 4, 4]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 1
+
+ G = nx.icosahedral_graph()
+ assert sorted(G) == list(range(12))
+ assert G.number_of_edges() == 30
+ assert [d for n, d in G.degree()] == [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
+ assert nx.diameter(G) == 3
+ assert nx.radius(G) == 3
+
+ G = nx.krackhardt_kite_graph()
+ assert sorted(G) == list(range(10))
+ assert G.number_of_edges() == 18
+ assert sorted(d for n, d in G.degree()) == [1, 2, 3, 3, 3, 4, 4, 5, 5, 6]
+
+ G = nx.moebius_kantor_graph()
+ assert sorted(G) == list(range(16))
+ assert G.number_of_edges() == 24
+ assert [d for n, d in G.degree()] == 16 * [3]
+ assert nx.diameter(G) == 4
+
+ G = nx.octahedral_graph()
+ assert sorted(G) == list(range(6))
+ assert G.number_of_edges() == 12
+ assert [d for n, d in G.degree()] == 6 * [4]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 2
+
+ G = nx.pappus_graph()
+ assert sorted(G) == list(range(18))
+ assert G.number_of_edges() == 27
+ assert [d for n, d in G.degree()] == 18 * [3]
+ assert nx.diameter(G) == 4
+
+ G = nx.petersen_graph()
+ assert sorted(G) == list(range(10))
+ assert G.number_of_edges() == 15
+ assert [d for n, d in G.degree()] == 10 * [3]
+ assert nx.diameter(G) == 2
+ assert nx.radius(G) == 2
+
+ G = nx.sedgewick_maze_graph()
+ assert sorted(G) == list(range(8))
+ assert G.number_of_edges() == 10
+ assert sorted(d for n, d in G.degree()) == [1, 2, 2, 2, 3, 3, 3, 4]
+
+ G = nx.tetrahedral_graph()
+ assert sorted(G) == list(range(4))
+ assert G.number_of_edges() == 6
+ assert [d for n, d in G.degree()] == [3, 3, 3, 3]
+ assert nx.diameter(G) == 1
+ assert nx.radius(G) == 1
+
+ G = nx.truncated_cube_graph()
+ assert sorted(G) == list(range(24))
+ assert G.number_of_edges() == 36
+ assert [d for n, d in G.degree()] == 24 * [3]
+
+ G = nx.truncated_tetrahedron_graph()
+ assert sorted(G) == list(range(12))
+ assert G.number_of_edges() == 18
+ assert [d for n, d in G.degree()] == 12 * [3]
+
+ G = nx.tutte_graph()
+ assert sorted(G) == list(range(46))
+ assert G.number_of_edges() == 69
+ assert [d for n, d in G.degree()] == 46 * [3]
+
+ # Test create_using with directed or multigraphs on small graphs
+ pytest.raises(nx.NetworkXError, nx.tutte_graph, create_using=nx.DiGraph)
+ MG = nx.tutte_graph(create_using=nx.MultiGraph)
+ assert sorted(MG.edges()) == sorted(G.edges())
+
+
+@pytest.mark.parametrize(
+ "fn",
+ (
+ nx.bull_graph,
+ nx.chvatal_graph,
+ nx.cubical_graph,
+ nx.diamond_graph,
+ nx.house_graph,
+ nx.house_x_graph,
+ nx.icosahedral_graph,
+ nx.krackhardt_kite_graph,
+ nx.octahedral_graph,
+ nx.petersen_graph,
+ nx.truncated_cube_graph,
+ nx.tutte_graph,
+ ),
+)
+@pytest.mark.parametrize(
+ "create_using", (nx.DiGraph, nx.MultiDiGraph, nx.DiGraph([(0, 1)]))
+)
+def tests_raises_with_directed_create_using(fn, create_using):
+ with pytest.raises(nx.NetworkXError, match="Directed Graph not supported"):
+ fn(create_using=create_using)
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_spectral_graph_forge.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_spectral_graph_forge.py
new file mode 100644
index 00000000..b554bfd7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_spectral_graph_forge.py
@@ -0,0 +1,49 @@
+import pytest
+
+pytest.importorskip("numpy")
+pytest.importorskip("scipy")
+
+
+from networkx import is_isomorphic
+from networkx.exception import NetworkXError
+from networkx.generators import karate_club_graph
+from networkx.generators.spectral_graph_forge import spectral_graph_forge
+from networkx.utils import nodes_equal
+
+
+def test_spectral_graph_forge():
+ G = karate_club_graph()
+
+ seed = 54321
+
+ # common cases, just checking node number preserving and difference
+ # between identity and modularity cases
+ H = spectral_graph_forge(G, 0.1, transformation="identity", seed=seed)
+ assert nodes_equal(G, H)
+
+ I = spectral_graph_forge(G, 0.1, transformation="identity", seed=seed)
+ assert nodes_equal(G, H)
+ assert is_isomorphic(I, H)
+
+ I = spectral_graph_forge(G, 0.1, transformation="modularity", seed=seed)
+ assert nodes_equal(G, I)
+
+ assert not is_isomorphic(I, H)
+
+ # with all the eigenvectors, output graph is identical to the input one
+ H = spectral_graph_forge(G, 1, transformation="modularity", seed=seed)
+ assert nodes_equal(G, H)
+ assert is_isomorphic(G, H)
+
+ # invalid alpha input value, it is silently truncated in [0,1]
+ H = spectral_graph_forge(G, -1, transformation="identity", seed=seed)
+ assert nodes_equal(G, H)
+
+ H = spectral_graph_forge(G, 10, transformation="identity", seed=seed)
+ assert nodes_equal(G, H)
+ assert is_isomorphic(G, H)
+
+ # invalid transformation mode, checking the error raising
+ pytest.raises(
+ NetworkXError, spectral_graph_forge, G, 0.1, transformation="unknown", seed=seed
+ )
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_stochastic.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_stochastic.py
new file mode 100644
index 00000000..0404d9d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_stochastic.py
@@ -0,0 +1,72 @@
+"""Unit tests for the :mod:`networkx.generators.stochastic` module."""
+
+import pytest
+
+import networkx as nx
+
+
+class TestStochasticGraph:
+ """Unit tests for the :func:`~networkx.stochastic_graph` function."""
+
+ def test_default_weights(self):
+ G = nx.DiGraph()
+ G.add_edge(0, 1)
+ G.add_edge(0, 2)
+ S = nx.stochastic_graph(G)
+ assert nx.is_isomorphic(G, S)
+ assert sorted(S.edges(data=True)) == [
+ (0, 1, {"weight": 0.5}),
+ (0, 2, {"weight": 0.5}),
+ ]
+
+ def test_in_place(self):
+ """Tests for an in-place reweighting of the edges of the graph."""
+ G = nx.DiGraph()
+ G.add_edge(0, 1, weight=1)
+ G.add_edge(0, 2, weight=1)
+ nx.stochastic_graph(G, copy=False)
+ assert sorted(G.edges(data=True)) == [
+ (0, 1, {"weight": 0.5}),
+ (0, 2, {"weight": 0.5}),
+ ]
+
+ def test_arbitrary_weights(self):
+ G = nx.DiGraph()
+ G.add_edge(0, 1, weight=1)
+ G.add_edge(0, 2, weight=1)
+ S = nx.stochastic_graph(G)
+ assert sorted(S.edges(data=True)) == [
+ (0, 1, {"weight": 0.5}),
+ (0, 2, {"weight": 0.5}),
+ ]
+
+ def test_multidigraph(self):
+ G = nx.MultiDiGraph()
+ G.add_edges_from([(0, 1), (0, 1), (0, 2), (0, 2)])
+ S = nx.stochastic_graph(G)
+ d = {"weight": 0.25}
+ assert sorted(S.edges(data=True)) == [
+ (0, 1, d),
+ (0, 1, d),
+ (0, 2, d),
+ (0, 2, d),
+ ]
+
+ def test_zero_weights(self):
+ """Smoke test: ensure ZeroDivisionError is not raised."""
+ G = nx.DiGraph()
+ G.add_edge(0, 1, weight=0)
+ G.add_edge(0, 2, weight=0)
+ S = nx.stochastic_graph(G)
+ assert sorted(S.edges(data=True)) == [
+ (0, 1, {"weight": 0}),
+ (0, 2, {"weight": 0}),
+ ]
+
+ def test_graph_disallowed(self):
+ with pytest.raises(nx.NetworkXNotImplemented):
+ nx.stochastic_graph(nx.Graph())
+
+ def test_multigraph_disallowed(self):
+ with pytest.raises(nx.NetworkXNotImplemented):
+ nx.stochastic_graph(nx.MultiGraph())
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_sudoku.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_sudoku.py
new file mode 100644
index 00000000..7c3560aa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_sudoku.py
@@ -0,0 +1,92 @@
+"""Unit tests for the :mod:`networkx.generators.sudoku_graph` module."""
+
+import pytest
+
+import networkx as nx
+
+
+def test_sudoku_negative():
+ """Raise an error when generating a Sudoku graph of order -1."""
+ pytest.raises(nx.NetworkXError, nx.sudoku_graph, n=-1)
+
+
+@pytest.mark.parametrize("n", [0, 1, 2, 3, 4])
+def test_sudoku_generator(n):
+ """Generate Sudoku graphs of various sizes and verify their properties."""
+ G = nx.sudoku_graph(n)
+ expected_nodes = n**4
+ expected_degree = (n - 1) * (3 * n + 1)
+ expected_edges = expected_nodes * expected_degree // 2
+ assert not G.is_directed()
+ assert not G.is_multigraph()
+ assert G.number_of_nodes() == expected_nodes
+ assert G.number_of_edges() == expected_edges
+ assert all(d == expected_degree for _, d in G.degree)
+
+ if n == 2:
+ assert sorted(G.neighbors(6)) == [2, 3, 4, 5, 7, 10, 14]
+ elif n == 3:
+ assert sorted(G.neighbors(42)) == [
+ 6,
+ 15,
+ 24,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 51,
+ 52,
+ 53,
+ 60,
+ 69,
+ 78,
+ ]
+ elif n == 4:
+ assert sorted(G.neighbors(0)) == [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 32,
+ 33,
+ 34,
+ 35,
+ 48,
+ 49,
+ 50,
+ 51,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ ]
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_time_series.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_time_series.py
new file mode 100644
index 00000000..5d0cc90a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_time_series.py
@@ -0,0 +1,64 @@
+"""Unit tests for the :mod:`networkx.generators.time_series` module."""
+
+import itertools
+
+import networkx as nx
+
+
+def test_visibility_graph__empty_series__empty_graph():
+ null_graph = nx.visibility_graph([]) # move along nothing to see here
+ assert nx.is_empty(null_graph)
+
+
+def test_visibility_graph__single_value_ts__single_node_graph():
+ node_graph = nx.visibility_graph([10]) # So Lonely
+ assert node_graph.number_of_nodes() == 1
+ assert node_graph.number_of_edges() == 0
+
+
+def test_visibility_graph__two_values_ts__single_edge_graph():
+ edge_graph = nx.visibility_graph([10, 20]) # Two of Us
+ assert list(edge_graph.edges) == [(0, 1)]
+
+
+def test_visibility_graph__convex_series__complete_graph():
+ series = [i**2 for i in range(10)] # no obstructions
+ expected_series_length = len(series)
+
+ actual_graph = nx.visibility_graph(series)
+
+ assert actual_graph.number_of_nodes() == expected_series_length
+ assert actual_graph.number_of_edges() == 45
+ assert nx.is_isomorphic(actual_graph, nx.complete_graph(expected_series_length))
+
+
+def test_visibility_graph__concave_series__path_graph():
+ series = [-(i**2) for i in range(10)] # Slip Slidin' Away
+ expected_node_count = len(series)
+
+ actual_graph = nx.visibility_graph(series)
+
+ assert actual_graph.number_of_nodes() == expected_node_count
+ assert actual_graph.number_of_edges() == expected_node_count - 1
+ assert nx.is_isomorphic(actual_graph, nx.path_graph(expected_node_count))
+
+
+def test_visibility_graph__flat_series__path_graph():
+ series = [0] * 10 # living in 1D flatland
+ expected_node_count = len(series)
+
+ actual_graph = nx.visibility_graph(series)
+
+ assert actual_graph.number_of_nodes() == expected_node_count
+ assert actual_graph.number_of_edges() == expected_node_count - 1
+ assert nx.is_isomorphic(actual_graph, nx.path_graph(expected_node_count))
+
+
+def test_visibility_graph_cyclic_series():
+ series = list(itertools.islice(itertools.cycle((2, 1, 3)), 17)) # It's so bumpy!
+ expected_node_count = len(series)
+
+ actual_graph = nx.visibility_graph(series)
+
+ assert actual_graph.number_of_nodes() == expected_node_count
+ assert actual_graph.number_of_edges() == 25
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_trees.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_trees.py
new file mode 100644
index 00000000..7932436b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_trees.py
@@ -0,0 +1,195 @@
+import random
+
+import pytest
+
+import networkx as nx
+from networkx.utils import arbitrary_element, graphs_equal
+
+
+@pytest.mark.parametrize("prefix_tree_fn", (nx.prefix_tree, nx.prefix_tree_recursive))
+def test_basic_prefix_tree(prefix_tree_fn):
+ # This example is from the Wikipedia article "Trie"
+ # <https://en.wikipedia.org/wiki/Trie>.
+ strings = ["a", "to", "tea", "ted", "ten", "i", "in", "inn"]
+ T = prefix_tree_fn(strings)
+ root, NIL = 0, -1
+
+ def source_label(v):
+ return T.nodes[v]["source"]
+
+ # First, we check that the tree has the expected
+ # structure. Recall that each node that corresponds to one of
+ # the input strings has an edge to the NIL node.
+ #
+ # Consider the three children at level 1 in the trie.
+ a, i, t = sorted(T[root], key=source_label)
+ # Check the 'a' branch.
+ assert len(T[a]) == 1
+ nil = arbitrary_element(T[a])
+ assert len(T[nil]) == 0
+ # Check the 'i' branch.
+ assert len(T[i]) == 2
+ nil, in_ = sorted(T[i], key=source_label)
+ assert len(T[nil]) == 0
+ assert len(T[in_]) == 2
+ nil, inn = sorted(T[in_], key=source_label)
+ assert len(T[nil]) == 0
+ assert len(T[inn]) == 1
+ nil = arbitrary_element(T[inn])
+ assert len(T[nil]) == 0
+ # Check the 't' branch.
+ te, to = sorted(T[t], key=source_label)
+ assert len(T[to]) == 1
+ nil = arbitrary_element(T[to])
+ assert len(T[nil]) == 0
+ tea, ted, ten = sorted(T[te], key=source_label)
+ assert len(T[tea]) == 1
+ assert len(T[ted]) == 1
+ assert len(T[ten]) == 1
+ nil = arbitrary_element(T[tea])
+ assert len(T[nil]) == 0
+ nil = arbitrary_element(T[ted])
+ assert len(T[nil]) == 0
+ nil = arbitrary_element(T[ten])
+ assert len(T[nil]) == 0
+
+ # Next, we check that the "sources" of each of the nodes is the
+ # rightmost letter in the string corresponding to the path to
+ # that node.
+ assert source_label(root) is None
+ assert source_label(a) == "a"
+ assert source_label(i) == "i"
+ assert source_label(t) == "t"
+ assert source_label(in_) == "n"
+ assert source_label(inn) == "n"
+ assert source_label(to) == "o"
+ assert source_label(te) == "e"
+ assert source_label(tea) == "a"
+ assert source_label(ted) == "d"
+ assert source_label(ten) == "n"
+ assert source_label(NIL) == "NIL"
+
+
+@pytest.mark.parametrize(
+ "strings",
+ (
+ ["a", "to", "tea", "ted", "ten", "i", "in", "inn"],
+ ["ab", "abs", "ad"],
+ ["ab", "abs", "ad", ""],
+ ["distant", "disparaging", "distant", "diamond", "ruby"],
+ ),
+)
+def test_implementations_consistent(strings):
+ """Ensure results are consistent between prefix_tree implementations."""
+ assert graphs_equal(nx.prefix_tree(strings), nx.prefix_tree_recursive(strings))
+
+
+def test_random_labeled_rooted_tree():
+ for i in range(1, 10):
+ t1 = nx.random_labeled_rooted_tree(i, seed=42)
+ t2 = nx.random_labeled_rooted_tree(i, seed=42)
+ assert nx.utils.misc.graphs_equal(t1, t2)
+ assert nx.is_tree(t1)
+ assert "root" in t1.graph
+ assert "roots" not in t1.graph
+
+
+def test_random_labeled_tree_n_zero():
+ """Tests if n = 0 then the NetworkXPointlessConcept exception is raised."""
+ with pytest.raises(nx.NetworkXPointlessConcept):
+ T = nx.random_labeled_tree(0, seed=1234)
+ with pytest.raises(nx.NetworkXPointlessConcept):
+ T = nx.random_labeled_rooted_tree(0, seed=1234)
+
+
+def test_random_labeled_rooted_forest():
+ for i in range(1, 10):
+ t1 = nx.random_labeled_rooted_forest(i, seed=42)
+ t2 = nx.random_labeled_rooted_forest(i, seed=42)
+ assert nx.utils.misc.graphs_equal(t1, t2)
+ for c in nx.connected_components(t1):
+ assert nx.is_tree(t1.subgraph(c))
+ assert "root" not in t1.graph
+ assert "roots" in t1.graph
+
+
+def test_random_labeled_rooted_forest_n_zero():
+ """Tests generation of empty labeled forests."""
+ F = nx.random_labeled_rooted_forest(0, seed=1234)
+ assert len(F) == 0
+ assert len(F.graph["roots"]) == 0
+
+
+def test_random_unlabeled_rooted_tree():
+ for i in range(1, 10):
+ t1 = nx.random_unlabeled_rooted_tree(i, seed=42)
+ t2 = nx.random_unlabeled_rooted_tree(i, seed=42)
+ assert nx.utils.misc.graphs_equal(t1, t2)
+ assert nx.is_tree(t1)
+ assert "root" in t1.graph
+ assert "roots" not in t1.graph
+ t = nx.random_unlabeled_rooted_tree(15, number_of_trees=10, seed=43)
+ random.seed(43)
+ s = nx.random_unlabeled_rooted_tree(15, number_of_trees=10, seed=random)
+ for i in range(10):
+ assert nx.utils.misc.graphs_equal(t[i], s[i])
+ assert nx.is_tree(t[i])
+ assert "root" in t[i].graph
+ assert "roots" not in t[i].graph
+
+
+def test_random_unlabeled_tree_n_zero():
+ """Tests if n = 0 then the NetworkXPointlessConcept exception is raised."""
+ with pytest.raises(nx.NetworkXPointlessConcept):
+ T = nx.random_unlabeled_tree(0, seed=1234)
+ with pytest.raises(nx.NetworkXPointlessConcept):
+ T = nx.random_unlabeled_rooted_tree(0, seed=1234)
+
+
+def test_random_unlabeled_rooted_forest():
+ with pytest.raises(ValueError):
+ nx.random_unlabeled_rooted_forest(10, q=0, seed=42)
+ for i in range(1, 10):
+ for q in range(1, i + 1):
+ t1 = nx.random_unlabeled_rooted_forest(i, q=q, seed=42)
+ t2 = nx.random_unlabeled_rooted_forest(i, q=q, seed=42)
+ assert nx.utils.misc.graphs_equal(t1, t2)
+ for c in nx.connected_components(t1):
+ assert nx.is_tree(t1.subgraph(c))
+ assert len(c) <= q
+ assert "root" not in t1.graph
+ assert "roots" in t1.graph
+ t = nx.random_unlabeled_rooted_forest(15, number_of_forests=10, seed=43)
+ random.seed(43)
+ s = nx.random_unlabeled_rooted_forest(15, number_of_forests=10, seed=random)
+ for i in range(10):
+ assert nx.utils.misc.graphs_equal(t[i], s[i])
+ for c in nx.connected_components(t[i]):
+ assert nx.is_tree(t[i].subgraph(c))
+ assert "root" not in t[i].graph
+ assert "roots" in t[i].graph
+
+
+def test_random_unlabeled_forest_n_zero():
+ """Tests generation of empty unlabeled forests."""
+ F = nx.random_unlabeled_rooted_forest(0, seed=1234)
+ assert len(F) == 0
+ assert len(F.graph["roots"]) == 0
+
+
+def test_random_unlabeled_tree():
+ for i in range(1, 10):
+ t1 = nx.random_unlabeled_tree(i, seed=42)
+ t2 = nx.random_unlabeled_tree(i, seed=42)
+ assert nx.utils.misc.graphs_equal(t1, t2)
+ assert nx.is_tree(t1)
+ assert "root" not in t1.graph
+ assert "roots" not in t1.graph
+ t = nx.random_unlabeled_tree(10, number_of_trees=10, seed=43)
+ random.seed(43)
+ s = nx.random_unlabeled_tree(10, number_of_trees=10, seed=random)
+ for i in range(10):
+ assert nx.utils.misc.graphs_equal(t[i], s[i])
+ assert nx.is_tree(t[i])
+ assert "root" not in t[i].graph
+ assert "roots" not in t[i].graph
diff --git a/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_triads.py b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_triads.py
new file mode 100644
index 00000000..463844be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/generators/tests/test_triads.py
@@ -0,0 +1,15 @@
+"""Unit tests for the :mod:`networkx.generators.triads` module."""
+
+import pytest
+
+from networkx import triad_graph
+
+
+def test_triad_graph():
+ G = triad_graph("030T")
+ assert [tuple(e) for e in ("ab", "ac", "cb")] == sorted(G.edges())
+
+
+def test_invalid_name():
+ with pytest.raises(ValueError):
+ triad_graph("bogus")