koichi12 commited on
Commit
8c635bc
·
verified ·
1 Parent(s): bb81abe

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +9 -0
  2. .venv/lib/python3.11/site-packages/OpenSSL/__pycache__/SSL.cpython-311.pyc +3 -0
  3. .venv/lib/python3.11/site-packages/OpenSSL/__pycache__/crypto.cpython-311.pyc +3 -0
  4. .venv/lib/python3.11/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-311.pyc +3 -0
  5. .venv/lib/python3.11/site-packages/dill/__pycache__/_dill.cpython-311.pyc +3 -0
  6. .venv/lib/python3.11/site-packages/networkx/algorithms/community/__pycache__/__init__.cpython-311.pyc +0 -0
  7. .venv/lib/python3.11/site-packages/networkx/algorithms/community/__pycache__/asyn_fluid.cpython-311.pyc +0 -0
  8. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__init__.py +0 -0
  9. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  10. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_asyn_fluid.cpython-311.pyc +0 -0
  11. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_centrality.cpython-311.pyc +0 -0
  12. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_divisive.cpython-311.pyc +0 -0
  13. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_kclique.cpython-311.pyc +0 -0
  14. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_kernighan_lin.cpython-311.pyc +0 -0
  15. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_label_propagation.cpython-311.pyc +0 -0
  16. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_louvain.cpython-311.pyc +0 -0
  17. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_lukes.cpython-311.pyc +0 -0
  18. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_modularity_max.cpython-311.pyc +0 -0
  19. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_quality.cpython-311.pyc +0 -0
  20. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_utils.cpython-311.pyc +0 -0
  21. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_asyn_fluid.py +136 -0
  22. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_centrality.py +85 -0
  23. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_divisive.py +106 -0
  24. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kclique.py +91 -0
  25. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kernighan_lin.py +92 -0
  26. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_label_propagation.py +241 -0
  27. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_louvain.py +264 -0
  28. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_lukes.py +152 -0
  29. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_modularity_max.py +340 -0
  30. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_quality.py +139 -0
  31. .venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_utils.py +26 -0
  32. .venv/lib/python3.11/site-packages/networkx/algorithms/components/__init__.py +6 -0
  33. .venv/lib/python3.11/site-packages/networkx/algorithms/components/__pycache__/connected.cpython-311.pyc +0 -0
  34. .venv/lib/python3.11/site-packages/networkx/algorithms/components/__pycache__/semiconnected.cpython-311.pyc +0 -0
  35. .venv/lib/python3.11/site-packages/networkx/algorithms/components/attracting.py +115 -0
  36. .venv/lib/python3.11/site-packages/networkx/algorithms/components/biconnected.py +394 -0
  37. .venv/lib/python3.11/site-packages/networkx/algorithms/components/connected.py +216 -0
  38. .venv/lib/python3.11/site-packages/networkx/algorithms/components/semiconnected.py +71 -0
  39. .venv/lib/python3.11/site-packages/networkx/algorithms/components/strongly_connected.py +351 -0
  40. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__init__.py +0 -0
  41. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  42. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_attracting.cpython-311.pyc +0 -0
  43. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_biconnected.cpython-311.pyc +0 -0
  44. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_connected.cpython-311.pyc +0 -0
  45. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_semiconnected.cpython-311.pyc +0 -0
  46. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_strongly_connected.cpython-311.pyc +0 -0
  47. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_weakly_connected.cpython-311.pyc +0 -0
  48. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_attracting.py +70 -0
  49. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_biconnected.py +248 -0
  50. .venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_connected.py +138 -0
.gitattributes CHANGED
@@ -315,3 +315,12 @@ tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia/cudnn/lib/
315
  .venv/lib/python3.11/site-packages/torio/lib/_torio_ffmpeg4.so filter=lfs diff=lfs merge=lfs -text
316
  .venv/lib/python3.11/site-packages/torio/lib/libtorio_ffmpeg5.so filter=lfs diff=lfs merge=lfs -text
317
  .venv/lib/python3.11/site-packages/torio/lib/libtorio_ffmpeg4.so filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
315
  .venv/lib/python3.11/site-packages/torio/lib/_torio_ffmpeg4.so filter=lfs diff=lfs merge=lfs -text
316
  .venv/lib/python3.11/site-packages/torio/lib/libtorio_ffmpeg5.so filter=lfs diff=lfs merge=lfs -text
317
  .venv/lib/python3.11/site-packages/torio/lib/libtorio_ffmpeg4.so filter=lfs diff=lfs merge=lfs -text
318
+ .venv/lib/python3.11/site-packages/torio/lib/_torio_ffmpeg5.so filter=lfs diff=lfs merge=lfs -text
319
+ .venv/lib/python3.11/site-packages/pyparsing/__pycache__/core.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
320
+ .venv/lib/python3.11/site-packages/virtualenv/seed/wheels/embed/setuptools-75.8.0-py3-none-any.whl filter=lfs diff=lfs merge=lfs -text
321
+ .venv/lib/python3.11/site-packages/virtualenv/seed/wheels/embed/pip-24.3.1-py3-none-any.whl filter=lfs diff=lfs merge=lfs -text
322
+ .venv/lib/python3.11/site-packages/virtualenv/seed/wheels/embed/setuptools-75.3.0-py3-none-any.whl filter=lfs diff=lfs merge=lfs -text
323
+ .venv/lib/python3.11/site-packages/dill/__pycache__/_dill.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
324
+ .venv/lib/python3.11/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
325
+ .venv/lib/python3.11/site-packages/OpenSSL/__pycache__/crypto.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
326
+ .venv/lib/python3.11/site-packages/OpenSSL/__pycache__/SSL.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
.venv/lib/python3.11/site-packages/OpenSSL/__pycache__/SSL.cpython-311.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a493cb641bc908b30044a1fc6fb728bbc91c0f852e49bef13ba491d6d755cf86
3
+ size 134204
.venv/lib/python3.11/site-packages/OpenSSL/__pycache__/crypto.cpython-311.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:59ec647782b61d4ba40e7dfec9a092995869a21af2d0da0a013f4ebd9fc8a6ea
3
+ size 136411
.venv/lib/python3.11/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-311.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bd6324004edddf538ea21e765ac2acffea44f407979e9ae8fb425a6fd776b679
3
+ size 145998
.venv/lib/python3.11/site-packages/dill/__pycache__/_dill.cpython-311.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bb74df91c650121f76e6a66835e9ee767f18baf206ada57a05dbcf50297c43cf
3
+ size 112760
.venv/lib/python3.11/site-packages/networkx/algorithms/community/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.48 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/__pycache__/asyn_fluid.cpython-311.pyc ADDED
Binary file (6.61 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (208 Bytes). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_asyn_fluid.cpython-311.pyc ADDED
Binary file (7.53 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_centrality.cpython-311.pyc ADDED
Binary file (6.19 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_divisive.cpython-311.pyc ADDED
Binary file (9.27 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_kclique.cpython-311.pyc ADDED
Binary file (5.58 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_kernighan_lin.cpython-311.pyc ADDED
Binary file (5.53 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_label_propagation.cpython-311.pyc ADDED
Binary file (19.1 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_louvain.cpython-311.pyc ADDED
Binary file (14.3 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_lukes.cpython-311.pyc ADDED
Binary file (6.9 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_modularity_max.cpython-311.pyc ADDED
Binary file (15 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_quality.cpython-311.pyc ADDED
Binary file (9.56 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/test_utils.cpython-311.pyc ADDED
Binary file (1.97 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_asyn_fluid.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx import Graph, NetworkXError
5
+ from networkx.algorithms.community import asyn_fluidc
6
+
7
+
8
+ @pytest.mark.parametrize("graph_constructor", (nx.DiGraph, nx.MultiGraph))
9
+ def test_raises_on_directed_and_multigraphs(graph_constructor):
10
+ G = graph_constructor([(0, 1), (1, 2)])
11
+ with pytest.raises(nx.NetworkXNotImplemented):
12
+ nx.community.asyn_fluidc(G, 1)
13
+
14
+
15
+ def test_exceptions():
16
+ test = Graph()
17
+ test.add_node("a")
18
+ pytest.raises(NetworkXError, asyn_fluidc, test, "hi")
19
+ pytest.raises(NetworkXError, asyn_fluidc, test, -1)
20
+ pytest.raises(NetworkXError, asyn_fluidc, test, 3)
21
+ test.add_node("b")
22
+ pytest.raises(NetworkXError, asyn_fluidc, test, 1)
23
+
24
+
25
+ def test_single_node():
26
+ test = Graph()
27
+
28
+ test.add_node("a")
29
+
30
+ # ground truth
31
+ ground_truth = {frozenset(["a"])}
32
+
33
+ communities = asyn_fluidc(test, 1)
34
+ result = {frozenset(c) for c in communities}
35
+ assert result == ground_truth
36
+
37
+
38
+ def test_two_nodes():
39
+ test = Graph()
40
+
41
+ test.add_edge("a", "b")
42
+
43
+ # ground truth
44
+ ground_truth = {frozenset(["a"]), frozenset(["b"])}
45
+
46
+ communities = asyn_fluidc(test, 2)
47
+ result = {frozenset(c) for c in communities}
48
+ assert result == ground_truth
49
+
50
+
51
+ def test_two_clique_communities():
52
+ test = Graph()
53
+
54
+ # c1
55
+ test.add_edge("a", "b")
56
+ test.add_edge("a", "c")
57
+ test.add_edge("b", "c")
58
+
59
+ # connection
60
+ test.add_edge("c", "d")
61
+
62
+ # c2
63
+ test.add_edge("d", "e")
64
+ test.add_edge("d", "f")
65
+ test.add_edge("f", "e")
66
+
67
+ # ground truth
68
+ ground_truth = {frozenset(["a", "c", "b"]), frozenset(["e", "d", "f"])}
69
+
70
+ communities = asyn_fluidc(test, 2, seed=7)
71
+ result = {frozenset(c) for c in communities}
72
+ assert result == ground_truth
73
+
74
+
75
+ def test_five_clique_ring():
76
+ test = Graph()
77
+
78
+ # c1
79
+ test.add_edge("1a", "1b")
80
+ test.add_edge("1a", "1c")
81
+ test.add_edge("1a", "1d")
82
+ test.add_edge("1b", "1c")
83
+ test.add_edge("1b", "1d")
84
+ test.add_edge("1c", "1d")
85
+
86
+ # c2
87
+ test.add_edge("2a", "2b")
88
+ test.add_edge("2a", "2c")
89
+ test.add_edge("2a", "2d")
90
+ test.add_edge("2b", "2c")
91
+ test.add_edge("2b", "2d")
92
+ test.add_edge("2c", "2d")
93
+
94
+ # c3
95
+ test.add_edge("3a", "3b")
96
+ test.add_edge("3a", "3c")
97
+ test.add_edge("3a", "3d")
98
+ test.add_edge("3b", "3c")
99
+ test.add_edge("3b", "3d")
100
+ test.add_edge("3c", "3d")
101
+
102
+ # c4
103
+ test.add_edge("4a", "4b")
104
+ test.add_edge("4a", "4c")
105
+ test.add_edge("4a", "4d")
106
+ test.add_edge("4b", "4c")
107
+ test.add_edge("4b", "4d")
108
+ test.add_edge("4c", "4d")
109
+
110
+ # c5
111
+ test.add_edge("5a", "5b")
112
+ test.add_edge("5a", "5c")
113
+ test.add_edge("5a", "5d")
114
+ test.add_edge("5b", "5c")
115
+ test.add_edge("5b", "5d")
116
+ test.add_edge("5c", "5d")
117
+
118
+ # connections
119
+ test.add_edge("1a", "2c")
120
+ test.add_edge("2a", "3c")
121
+ test.add_edge("3a", "4c")
122
+ test.add_edge("4a", "5c")
123
+ test.add_edge("5a", "1c")
124
+
125
+ # ground truth
126
+ ground_truth = {
127
+ frozenset(["1a", "1b", "1c", "1d"]),
128
+ frozenset(["2a", "2b", "2c", "2d"]),
129
+ frozenset(["3a", "3b", "3c", "3d"]),
130
+ frozenset(["4a", "4b", "4c", "4d"]),
131
+ frozenset(["5a", "5b", "5c", "5d"]),
132
+ }
133
+
134
+ communities = asyn_fluidc(test, 5, seed=9)
135
+ result = {frozenset(c) for c in communities}
136
+ assert result == ground_truth
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_centrality.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.community.centrality`
2
+ module.
3
+
4
+ """
5
+
6
+ from operator import itemgetter
7
+
8
+ import networkx as nx
9
+
10
+
11
+ def set_of_sets(iterable):
12
+ return set(map(frozenset, iterable))
13
+
14
+
15
+ def validate_communities(result, expected):
16
+ assert set_of_sets(result) == set_of_sets(expected)
17
+
18
+
19
+ def validate_possible_communities(result, *expected):
20
+ assert any(set_of_sets(result) == set_of_sets(p) for p in expected)
21
+
22
+
23
+ class TestGirvanNewman:
24
+ """Unit tests for the
25
+ :func:`networkx.algorithms.community.centrality.girvan_newman`
26
+ function.
27
+
28
+ """
29
+
30
+ def test_no_edges(self):
31
+ G = nx.empty_graph(3)
32
+ communities = list(nx.community.girvan_newman(G))
33
+ assert len(communities) == 1
34
+ validate_communities(communities[0], [{0}, {1}, {2}])
35
+
36
+ def test_undirected(self):
37
+ # Start with the graph .-.-.-.
38
+ G = nx.path_graph(4)
39
+ communities = list(nx.community.girvan_newman(G))
40
+ assert len(communities) == 3
41
+ # After one removal, we get the graph .-. .-.
42
+ validate_communities(communities[0], [{0, 1}, {2, 3}])
43
+ # After the next, we get the graph .-. . ., but there are two
44
+ # symmetric possible versions.
45
+ validate_possible_communities(
46
+ communities[1], [{0}, {1}, {2, 3}], [{0, 1}, {2}, {3}]
47
+ )
48
+ # After the last removal, we always get the empty graph.
49
+ validate_communities(communities[2], [{0}, {1}, {2}, {3}])
50
+
51
+ def test_directed(self):
52
+ G = nx.DiGraph(nx.path_graph(4))
53
+ communities = list(nx.community.girvan_newman(G))
54
+ assert len(communities) == 3
55
+ validate_communities(communities[0], [{0, 1}, {2, 3}])
56
+ validate_possible_communities(
57
+ communities[1], [{0}, {1}, {2, 3}], [{0, 1}, {2}, {3}]
58
+ )
59
+ validate_communities(communities[2], [{0}, {1}, {2}, {3}])
60
+
61
+ def test_selfloops(self):
62
+ G = nx.path_graph(4)
63
+ G.add_edge(0, 0)
64
+ G.add_edge(2, 2)
65
+ communities = list(nx.community.girvan_newman(G))
66
+ assert len(communities) == 3
67
+ validate_communities(communities[0], [{0, 1}, {2, 3}])
68
+ validate_possible_communities(
69
+ communities[1], [{0}, {1}, {2, 3}], [{0, 1}, {2}, {3}]
70
+ )
71
+ validate_communities(communities[2], [{0}, {1}, {2}, {3}])
72
+
73
+ def test_most_valuable_edge(self):
74
+ G = nx.Graph()
75
+ G.add_weighted_edges_from([(0, 1, 3), (1, 2, 2), (2, 3, 1)])
76
+ # Let the most valuable edge be the one with the highest weight.
77
+
78
+ def heaviest(G):
79
+ return max(G.edges(data="weight"), key=itemgetter(2))[:2]
80
+
81
+ communities = list(nx.community.girvan_newman(G, heaviest))
82
+ assert len(communities) == 3
83
+ validate_communities(communities[0], [{0}, {1, 2, 3}])
84
+ validate_communities(communities[1], [{0}, {1}, {2, 3}])
85
+ validate_communities(communities[2], [{0}, {1}, {2}, {3}])
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_divisive.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+
5
+
6
+ def test_edge_betweenness_partition():
7
+ G = nx.barbell_graph(3, 0)
8
+ C = nx.community.edge_betweenness_partition(G, 2)
9
+ answer = [{0, 1, 2}, {3, 4, 5}]
10
+ assert len(C) == len(answer)
11
+ for s in answer:
12
+ assert s in C
13
+
14
+ G = nx.barbell_graph(3, 1)
15
+ C = nx.community.edge_betweenness_partition(G, 3)
16
+ answer = [{0, 1, 2}, {4, 5, 6}, {3}]
17
+ assert len(C) == len(answer)
18
+ for s in answer:
19
+ assert s in C
20
+
21
+ C = nx.community.edge_betweenness_partition(G, 7)
22
+ answer = [{n} for n in G]
23
+ assert len(C) == len(answer)
24
+ for s in answer:
25
+ assert s in C
26
+
27
+ C = nx.community.edge_betweenness_partition(G, 1)
28
+ assert C == [set(G)]
29
+
30
+ C = nx.community.edge_betweenness_partition(G, 1, weight="weight")
31
+ assert C == [set(G)]
32
+
33
+ with pytest.raises(nx.NetworkXError):
34
+ nx.community.edge_betweenness_partition(G, 0)
35
+
36
+ with pytest.raises(nx.NetworkXError):
37
+ nx.community.edge_betweenness_partition(G, -1)
38
+
39
+ with pytest.raises(nx.NetworkXError):
40
+ nx.community.edge_betweenness_partition(G, 10)
41
+
42
+
43
+ def test_edge_current_flow_betweenness_partition():
44
+ pytest.importorskip("scipy")
45
+
46
+ G = nx.barbell_graph(3, 0)
47
+ C = nx.community.edge_current_flow_betweenness_partition(G, 2)
48
+ answer = [{0, 1, 2}, {3, 4, 5}]
49
+ assert len(C) == len(answer)
50
+ for s in answer:
51
+ assert s in C
52
+
53
+ G = nx.barbell_graph(3, 1)
54
+ C = nx.community.edge_current_flow_betweenness_partition(G, 2)
55
+ answers = [[{0, 1, 2, 3}, {4, 5, 6}], [{0, 1, 2}, {3, 4, 5, 6}]]
56
+ assert len(C) == len(answers[0])
57
+ assert any(all(s in answer for s in C) for answer in answers)
58
+
59
+ C = nx.community.edge_current_flow_betweenness_partition(G, 3)
60
+ answer = [{0, 1, 2}, {4, 5, 6}, {3}]
61
+ assert len(C) == len(answer)
62
+ for s in answer:
63
+ assert s in C
64
+
65
+ C = nx.community.edge_current_flow_betweenness_partition(G, 4)
66
+ answers = [[{1, 2}, {4, 5, 6}, {3}, {0}], [{0, 1, 2}, {5, 6}, {3}, {4}]]
67
+ assert len(C) == len(answers[0])
68
+ assert any(all(s in answer for s in C) for answer in answers)
69
+
70
+ C = nx.community.edge_current_flow_betweenness_partition(G, 5)
71
+ answer = [{1, 2}, {5, 6}, {3}, {0}, {4}]
72
+ assert len(C) == len(answer)
73
+ for s in answer:
74
+ assert s in C
75
+
76
+ C = nx.community.edge_current_flow_betweenness_partition(G, 6)
77
+ answers = [[{2}, {5, 6}, {3}, {0}, {4}, {1}], [{1, 2}, {6}, {3}, {0}, {4}, {5}]]
78
+ assert len(C) == len(answers[0])
79
+ assert any(all(s in answer for s in C) for answer in answers)
80
+
81
+ C = nx.community.edge_current_flow_betweenness_partition(G, 7)
82
+ answer = [{n} for n in G]
83
+ assert len(C) == len(answer)
84
+ for s in answer:
85
+ assert s in C
86
+
87
+ C = nx.community.edge_current_flow_betweenness_partition(G, 1)
88
+ assert C == [set(G)]
89
+
90
+ C = nx.community.edge_current_flow_betweenness_partition(G, 1, weight="weight")
91
+ assert C == [set(G)]
92
+
93
+ with pytest.raises(nx.NetworkXError):
94
+ nx.community.edge_current_flow_betweenness_partition(G, 0)
95
+
96
+ with pytest.raises(nx.NetworkXError):
97
+ nx.community.edge_current_flow_betweenness_partition(G, -1)
98
+
99
+ with pytest.raises(nx.NetworkXError):
100
+ nx.community.edge_current_flow_betweenness_partition(G, 10)
101
+
102
+ N = 10
103
+ G = nx.empty_graph(N)
104
+ for i in range(2, N - 1):
105
+ C = nx.community.edge_current_flow_betweenness_partition(G, i)
106
+ assert C == [{n} for n in G]
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kclique.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import combinations
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+
7
+
8
+ def test_overlapping_K5():
9
+ G = nx.Graph()
10
+ G.add_edges_from(combinations(range(5), 2)) # Add a five clique
11
+ G.add_edges_from(combinations(range(2, 7), 2)) # Add another five clique
12
+ c = list(nx.community.k_clique_communities(G, 4))
13
+ assert c == [frozenset(range(7))]
14
+ c = set(nx.community.k_clique_communities(G, 5))
15
+ assert c == {frozenset(range(5)), frozenset(range(2, 7))}
16
+
17
+
18
+ def test_isolated_K5():
19
+ G = nx.Graph()
20
+ G.add_edges_from(combinations(range(5), 2)) # Add a five clique
21
+ G.add_edges_from(combinations(range(5, 10), 2)) # Add another five clique
22
+ c = set(nx.community.k_clique_communities(G, 5))
23
+ assert c == {frozenset(range(5)), frozenset(range(5, 10))}
24
+
25
+
26
+ class TestZacharyKarateClub:
27
+ def setup_method(self):
28
+ self.G = nx.karate_club_graph()
29
+
30
+ def _check_communities(self, k, expected):
31
+ communities = set(nx.community.k_clique_communities(self.G, k))
32
+ assert communities == expected
33
+
34
+ def test_k2(self):
35
+ # clique percolation with k=2 is just connected components
36
+ expected = {frozenset(self.G)}
37
+ self._check_communities(2, expected)
38
+
39
+ def test_k3(self):
40
+ comm1 = [
41
+ 0,
42
+ 1,
43
+ 2,
44
+ 3,
45
+ 7,
46
+ 8,
47
+ 12,
48
+ 13,
49
+ 14,
50
+ 15,
51
+ 17,
52
+ 18,
53
+ 19,
54
+ 20,
55
+ 21,
56
+ 22,
57
+ 23,
58
+ 26,
59
+ 27,
60
+ 28,
61
+ 29,
62
+ 30,
63
+ 31,
64
+ 32,
65
+ 33,
66
+ ]
67
+ comm2 = [0, 4, 5, 6, 10, 16]
68
+ comm3 = [24, 25, 31]
69
+ expected = {frozenset(comm1), frozenset(comm2), frozenset(comm3)}
70
+ self._check_communities(3, expected)
71
+
72
+ def test_k4(self):
73
+ expected = {
74
+ frozenset([0, 1, 2, 3, 7, 13]),
75
+ frozenset([8, 32, 30, 33]),
76
+ frozenset([32, 33, 29, 23]),
77
+ }
78
+ self._check_communities(4, expected)
79
+
80
+ def test_k5(self):
81
+ expected = {frozenset([0, 1, 2, 3, 7, 13])}
82
+ self._check_communities(5, expected)
83
+
84
+ def test_k6(self):
85
+ expected = set()
86
+ self._check_communities(6, expected)
87
+
88
+
89
+ def test_bad_k():
90
+ with pytest.raises(nx.NetworkXError):
91
+ list(nx.community.k_clique_communities(nx.Graph(), 1))
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kernighan_lin.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.community.kernighan_lin`
2
+ module.
3
+ """
4
+
5
+ from itertools import permutations
6
+
7
+ import pytest
8
+
9
+ import networkx as nx
10
+ from networkx.algorithms.community import kernighan_lin_bisection
11
+
12
+
13
+ def assert_partition_equal(x, y):
14
+ assert set(map(frozenset, x)) == set(map(frozenset, y))
15
+
16
+
17
+ def test_partition():
18
+ G = nx.barbell_graph(3, 0)
19
+ C = kernighan_lin_bisection(G)
20
+ assert_partition_equal(C, [{0, 1, 2}, {3, 4, 5}])
21
+
22
+
23
+ def test_partition_argument():
24
+ G = nx.barbell_graph(3, 0)
25
+ partition = [{0, 1, 2}, {3, 4, 5}]
26
+ C = kernighan_lin_bisection(G, partition)
27
+ assert_partition_equal(C, partition)
28
+
29
+
30
+ def test_partition_argument_non_integer_nodes():
31
+ G = nx.Graph([("A", "B"), ("A", "C"), ("B", "C"), ("C", "D")])
32
+ partition = ({"A", "B"}, {"C", "D"})
33
+ C = kernighan_lin_bisection(G, partition)
34
+ assert_partition_equal(C, partition)
35
+
36
+
37
+ def test_seed_argument():
38
+ G = nx.barbell_graph(3, 0)
39
+ C = kernighan_lin_bisection(G, seed=1)
40
+ assert_partition_equal(C, [{0, 1, 2}, {3, 4, 5}])
41
+
42
+
43
+ def test_non_disjoint_partition():
44
+ with pytest.raises(nx.NetworkXError):
45
+ G = nx.barbell_graph(3, 0)
46
+ partition = ({0, 1, 2}, {2, 3, 4, 5})
47
+ kernighan_lin_bisection(G, partition)
48
+
49
+
50
+ def test_too_many_blocks():
51
+ with pytest.raises(nx.NetworkXError):
52
+ G = nx.barbell_graph(3, 0)
53
+ partition = ({0, 1}, {2}, {3, 4, 5})
54
+ kernighan_lin_bisection(G, partition)
55
+
56
+
57
+ def test_multigraph():
58
+ G = nx.cycle_graph(4)
59
+ M = nx.MultiGraph(G.edges())
60
+ M.add_edges_from(G.edges())
61
+ M.remove_edge(1, 2)
62
+ for labels in permutations(range(4)):
63
+ mapping = dict(zip(M, labels))
64
+ A, B = kernighan_lin_bisection(nx.relabel_nodes(M, mapping), seed=0)
65
+ assert_partition_equal(
66
+ [A, B], [{mapping[0], mapping[1]}, {mapping[2], mapping[3]}]
67
+ )
68
+
69
+
70
+ def test_max_iter_argument():
71
+ G = nx.Graph(
72
+ [
73
+ ("A", "B", {"weight": 1}),
74
+ ("A", "C", {"weight": 2}),
75
+ ("A", "D", {"weight": 3}),
76
+ ("A", "E", {"weight": 2}),
77
+ ("A", "F", {"weight": 4}),
78
+ ("B", "C", {"weight": 1}),
79
+ ("B", "D", {"weight": 4}),
80
+ ("B", "E", {"weight": 2}),
81
+ ("B", "F", {"weight": 1}),
82
+ ("C", "D", {"weight": 3}),
83
+ ("C", "E", {"weight": 2}),
84
+ ("C", "F", {"weight": 1}),
85
+ ("D", "E", {"weight": 4}),
86
+ ("D", "F", {"weight": 3}),
87
+ ("E", "F", {"weight": 2}),
88
+ ]
89
+ )
90
+ partition = ({"A", "B", "C"}, {"D", "E", "F"})
91
+ C = kernighan_lin_bisection(G, partition, max_iter=1)
92
+ assert_partition_equal(C, ({"A", "F", "C"}, {"D", "E", "B"}))
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_label_propagation.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import chain, combinations
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+
7
+
8
+ def test_directed_not_supported():
9
+ with pytest.raises(nx.NetworkXNotImplemented):
10
+ # not supported for directed graphs
11
+ test = nx.DiGraph()
12
+ test.add_edge("a", "b")
13
+ test.add_edge("a", "c")
14
+ test.add_edge("b", "d")
15
+ result = nx.community.label_propagation_communities(test)
16
+
17
+
18
+ def test_iterator_vs_iterable():
19
+ G = nx.empty_graph("a")
20
+ assert list(nx.community.label_propagation_communities(G)) == [{"a"}]
21
+ for community in nx.community.label_propagation_communities(G):
22
+ assert community == {"a"}
23
+ pytest.raises(TypeError, next, nx.community.label_propagation_communities(G))
24
+
25
+
26
+ def test_one_node():
27
+ test = nx.Graph()
28
+ test.add_node("a")
29
+
30
+ # The expected communities are:
31
+ ground_truth = {frozenset(["a"])}
32
+
33
+ communities = nx.community.label_propagation_communities(test)
34
+ result = {frozenset(c) for c in communities}
35
+ assert result == ground_truth
36
+
37
+
38
+ def test_unconnected_communities():
39
+ test = nx.Graph()
40
+ # community 1
41
+ test.add_edge("a", "c")
42
+ test.add_edge("a", "d")
43
+ test.add_edge("d", "c")
44
+ # community 2
45
+ test.add_edge("b", "e")
46
+ test.add_edge("e", "f")
47
+ test.add_edge("f", "b")
48
+
49
+ # The expected communities are:
50
+ ground_truth = {frozenset(["a", "c", "d"]), frozenset(["b", "e", "f"])}
51
+
52
+ communities = nx.community.label_propagation_communities(test)
53
+ result = {frozenset(c) for c in communities}
54
+ assert result == ground_truth
55
+
56
+
57
+ def test_connected_communities():
58
+ test = nx.Graph()
59
+ # community 1
60
+ test.add_edge("a", "b")
61
+ test.add_edge("c", "a")
62
+ test.add_edge("c", "b")
63
+ test.add_edge("d", "a")
64
+ test.add_edge("d", "b")
65
+ test.add_edge("d", "c")
66
+ test.add_edge("e", "a")
67
+ test.add_edge("e", "b")
68
+ test.add_edge("e", "c")
69
+ test.add_edge("e", "d")
70
+ # community 2
71
+ test.add_edge("1", "2")
72
+ test.add_edge("3", "1")
73
+ test.add_edge("3", "2")
74
+ test.add_edge("4", "1")
75
+ test.add_edge("4", "2")
76
+ test.add_edge("4", "3")
77
+ test.add_edge("5", "1")
78
+ test.add_edge("5", "2")
79
+ test.add_edge("5", "3")
80
+ test.add_edge("5", "4")
81
+ # edge between community 1 and 2
82
+ test.add_edge("a", "1")
83
+ # community 3
84
+ test.add_edge("x", "y")
85
+ # community 4 with only a single node
86
+ test.add_node("z")
87
+
88
+ # The expected communities are:
89
+ ground_truth1 = {
90
+ frozenset(["a", "b", "c", "d", "e"]),
91
+ frozenset(["1", "2", "3", "4", "5"]),
92
+ frozenset(["x", "y"]),
93
+ frozenset(["z"]),
94
+ }
95
+ ground_truth2 = {
96
+ frozenset(["a", "b", "c", "d", "e", "1", "2", "3", "4", "5"]),
97
+ frozenset(["x", "y"]),
98
+ frozenset(["z"]),
99
+ }
100
+ ground_truth = (ground_truth1, ground_truth2)
101
+
102
+ communities = nx.community.label_propagation_communities(test)
103
+ result = {frozenset(c) for c in communities}
104
+ assert result in ground_truth
105
+
106
+
107
+ def test_termination():
108
+ # ensure termination of asyn_lpa_communities in two cases
109
+ # that led to an endless loop in a previous version
110
+ test1 = nx.karate_club_graph()
111
+ test2 = nx.caveman_graph(2, 10)
112
+ test2.add_edges_from([(0, 20), (20, 10)])
113
+ nx.community.asyn_lpa_communities(test1)
114
+ nx.community.asyn_lpa_communities(test2)
115
+
116
+
117
+ class TestAsynLpaCommunities:
118
+ def _check_communities(self, G, expected):
119
+ """Checks that the communities computed from the given graph ``G``
120
+ using the :func:`~networkx.asyn_lpa_communities` function match
121
+ the set of nodes given in ``expected``.
122
+
123
+ ``expected`` must be a :class:`set` of :class:`frozenset`
124
+ instances, each element of which is a node in the graph.
125
+
126
+ """
127
+ communities = nx.community.asyn_lpa_communities(G)
128
+ result = {frozenset(c) for c in communities}
129
+ assert result == expected
130
+
131
+ def test_null_graph(self):
132
+ G = nx.null_graph()
133
+ ground_truth = set()
134
+ self._check_communities(G, ground_truth)
135
+
136
+ def test_single_node(self):
137
+ G = nx.empty_graph(1)
138
+ ground_truth = {frozenset([0])}
139
+ self._check_communities(G, ground_truth)
140
+
141
+ def test_simple_communities(self):
142
+ # This graph is the disjoint union of two triangles.
143
+ G = nx.Graph(["ab", "ac", "bc", "de", "df", "fe"])
144
+ ground_truth = {frozenset("abc"), frozenset("def")}
145
+ self._check_communities(G, ground_truth)
146
+
147
+ def test_seed_argument(self):
148
+ G = nx.Graph(["ab", "ac", "bc", "de", "df", "fe"])
149
+ ground_truth = {frozenset("abc"), frozenset("def")}
150
+ communities = nx.community.asyn_lpa_communities(G, seed=1)
151
+ result = {frozenset(c) for c in communities}
152
+ assert result == ground_truth
153
+
154
+ def test_several_communities(self):
155
+ # This graph is the disjoint union of five triangles.
156
+ ground_truth = {frozenset(range(3 * i, 3 * (i + 1))) for i in range(5)}
157
+ edges = chain.from_iterable(combinations(c, 2) for c in ground_truth)
158
+ G = nx.Graph(edges)
159
+ self._check_communities(G, ground_truth)
160
+
161
+
162
+ class TestFastLabelPropagationCommunities:
163
+ N = 100 # number of nodes
164
+ K = 15 # average node degree
165
+
166
+ def _check_communities(self, G, truth, weight=None, seed=42):
167
+ C = nx.community.fast_label_propagation_communities(G, weight=weight, seed=seed)
168
+ assert {frozenset(c) for c in C} == truth
169
+
170
+ def test_null_graph(self):
171
+ G = nx.null_graph()
172
+ truth = set()
173
+ self._check_communities(G, truth)
174
+
175
+ def test_empty_graph(self):
176
+ G = nx.empty_graph(self.N)
177
+ truth = {frozenset([i]) for i in G}
178
+ self._check_communities(G, truth)
179
+
180
+ def test_star_graph(self):
181
+ G = nx.star_graph(self.N)
182
+ truth = {frozenset(G)}
183
+ self._check_communities(G, truth)
184
+
185
+ def test_complete_graph(self):
186
+ G = nx.complete_graph(self.N)
187
+ truth = {frozenset(G)}
188
+ self._check_communities(G, truth)
189
+
190
+ def test_bipartite_graph(self):
191
+ G = nx.complete_bipartite_graph(self.N // 2, self.N // 2)
192
+ truth = {frozenset(G)}
193
+ self._check_communities(G, truth)
194
+
195
+ def test_random_graph(self):
196
+ G = nx.gnm_random_graph(self.N, self.N * self.K // 2, seed=42)
197
+ truth = {frozenset(G)}
198
+ self._check_communities(G, truth)
199
+
200
+ def test_disjoin_cliques(self):
201
+ G = nx.Graph(["ab", "AB", "AC", "BC", "12", "13", "14", "23", "24", "34"])
202
+ truth = {frozenset("ab"), frozenset("ABC"), frozenset("1234")}
203
+ self._check_communities(G, truth)
204
+
205
+ def test_ring_of_cliques(self):
206
+ N, K = self.N, self.K
207
+ G = nx.ring_of_cliques(N, K)
208
+ truth = {frozenset([K * i + k for k in range(K)]) for i in range(N)}
209
+ self._check_communities(G, truth)
210
+
211
+ def test_larger_graph(self):
212
+ G = nx.gnm_random_graph(100 * self.N, 50 * self.N * self.K, seed=42)
213
+ nx.community.fast_label_propagation_communities(G)
214
+
215
+ def test_graph_type(self):
216
+ G1 = nx.complete_graph(self.N, nx.MultiDiGraph())
217
+ G2 = nx.MultiGraph(G1)
218
+ G3 = nx.DiGraph(G1)
219
+ G4 = nx.Graph(G1)
220
+ truth = {frozenset(G1)}
221
+ self._check_communities(G1, truth)
222
+ self._check_communities(G2, truth)
223
+ self._check_communities(G3, truth)
224
+ self._check_communities(G4, truth)
225
+
226
+ def test_weight_argument(self):
227
+ G = nx.MultiDiGraph()
228
+ G.add_edge(1, 2, weight=1.41)
229
+ G.add_edge(2, 1, weight=1.41)
230
+ G.add_edge(2, 3)
231
+ G.add_edge(3, 4, weight=3.14)
232
+ truth = {frozenset({1, 2}), frozenset({3, 4})}
233
+ self._check_communities(G, truth, weight="weight")
234
+
235
+ def test_seed_argument(self):
236
+ G = nx.karate_club_graph()
237
+ C = nx.community.fast_label_propagation_communities(G, seed=2023)
238
+ truth = {frozenset(c) for c in C}
239
+ self._check_communities(G, truth, seed=2023)
240
+ # smoke test that seed=None works
241
+ C = nx.community.fast_label_propagation_communities(G, seed=None)
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_louvain.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+
5
+
6
+ def test_modularity_increase():
7
+ G = nx.LFR_benchmark_graph(
8
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
9
+ )
10
+ partition = [{u} for u in G.nodes()]
11
+ mod = nx.community.modularity(G, partition)
12
+ partition = nx.community.louvain_communities(G)
13
+
14
+ assert nx.community.modularity(G, partition) > mod
15
+
16
+
17
+ def test_valid_partition():
18
+ G = nx.LFR_benchmark_graph(
19
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
20
+ )
21
+ H = G.to_directed()
22
+ partition = nx.community.louvain_communities(G)
23
+ partition2 = nx.community.louvain_communities(H)
24
+
25
+ assert nx.community.is_partition(G, partition)
26
+ assert nx.community.is_partition(H, partition2)
27
+
28
+
29
+ def test_karate_club_partition():
30
+ G = nx.karate_club_graph()
31
+ part = [
32
+ {0, 1, 2, 3, 7, 9, 11, 12, 13, 17, 19, 21},
33
+ {16, 4, 5, 6, 10},
34
+ {23, 25, 27, 28, 24, 31},
35
+ {32, 33, 8, 14, 15, 18, 20, 22, 26, 29, 30},
36
+ ]
37
+ partition = nx.community.louvain_communities(G, seed=2, weight=None)
38
+
39
+ assert part == partition
40
+
41
+
42
+ def test_partition_iterator():
43
+ G = nx.path_graph(15)
44
+ parts_iter = nx.community.louvain_partitions(G, seed=42)
45
+ first_part = next(parts_iter)
46
+ first_copy = [s.copy() for s in first_part]
47
+
48
+ # gh-5901 reports sets changing after next partition is yielded
49
+ assert first_copy[0] == first_part[0]
50
+ second_part = next(parts_iter)
51
+ assert first_copy[0] == first_part[0]
52
+
53
+
54
+ def test_undirected_selfloops():
55
+ G = nx.karate_club_graph()
56
+ expected_partition = nx.community.louvain_communities(G, seed=2, weight=None)
57
+ part = [
58
+ {0, 1, 2, 3, 7, 9, 11, 12, 13, 17, 19, 21},
59
+ {16, 4, 5, 6, 10},
60
+ {23, 25, 27, 28, 24, 31},
61
+ {32, 33, 8, 14, 15, 18, 20, 22, 26, 29, 30},
62
+ ]
63
+ assert expected_partition == part
64
+
65
+ G.add_weighted_edges_from([(i, i, i * 1000) for i in range(9)])
66
+ # large self-loop weight impacts partition
67
+ partition = nx.community.louvain_communities(G, seed=2, weight="weight")
68
+ assert part != partition
69
+
70
+ # small self-loop weights aren't enough to impact partition in this graph
71
+ partition = nx.community.louvain_communities(G, seed=2, weight=None)
72
+ assert part == partition
73
+
74
+
75
+ def test_directed_selfloops():
76
+ G = nx.DiGraph()
77
+ G.add_nodes_from(range(11))
78
+ G_edges = [
79
+ (0, 2),
80
+ (0, 1),
81
+ (1, 0),
82
+ (2, 1),
83
+ (2, 0),
84
+ (3, 4),
85
+ (4, 3),
86
+ (7, 8),
87
+ (8, 7),
88
+ (9, 10),
89
+ (10, 9),
90
+ ]
91
+ G.add_edges_from(G_edges)
92
+ G_expected_partition = nx.community.louvain_communities(G, seed=123, weight=None)
93
+
94
+ G.add_weighted_edges_from([(i, i, i * 1000) for i in range(3)])
95
+ # large self-loop weight impacts partition
96
+ G_partition = nx.community.louvain_communities(G, seed=123, weight="weight")
97
+ assert G_partition != G_expected_partition
98
+
99
+ # small self-loop weights aren't enough to impact partition in this graph
100
+ G_partition = nx.community.louvain_communities(G, seed=123, weight=None)
101
+ assert G_partition == G_expected_partition
102
+
103
+
104
+ def test_directed_partition():
105
+ """
106
+ Test 2 cases that were looping infinitely
107
+ from issues #5175 and #5704
108
+ """
109
+ G = nx.DiGraph()
110
+ H = nx.DiGraph()
111
+ G.add_nodes_from(range(10))
112
+ H.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
113
+ G_edges = [
114
+ (0, 2),
115
+ (0, 1),
116
+ (1, 0),
117
+ (2, 1),
118
+ (2, 0),
119
+ (3, 4),
120
+ (4, 3),
121
+ (7, 8),
122
+ (8, 7),
123
+ (9, 10),
124
+ (10, 9),
125
+ ]
126
+ H_edges = [
127
+ (1, 2),
128
+ (1, 6),
129
+ (1, 9),
130
+ (2, 3),
131
+ (2, 4),
132
+ (2, 5),
133
+ (3, 4),
134
+ (4, 3),
135
+ (4, 5),
136
+ (5, 4),
137
+ (6, 7),
138
+ (6, 8),
139
+ (9, 10),
140
+ (9, 11),
141
+ (10, 11),
142
+ (11, 10),
143
+ ]
144
+ G.add_edges_from(G_edges)
145
+ H.add_edges_from(H_edges)
146
+
147
+ G_expected_partition = [{0, 1, 2}, {3, 4}, {5}, {6}, {8, 7}, {9, 10}]
148
+ G_partition = nx.community.louvain_communities(G, seed=123, weight=None)
149
+
150
+ H_expected_partition = [{2, 3, 4, 5}, {8, 1, 6, 7}, {9, 10, 11}]
151
+ H_partition = nx.community.louvain_communities(H, seed=123, weight=None)
152
+
153
+ assert G_partition == G_expected_partition
154
+ assert H_partition == H_expected_partition
155
+
156
+
157
+ def test_none_weight_param():
158
+ G = nx.karate_club_graph()
159
+ nx.set_edge_attributes(
160
+ G, {edge: i * i for i, edge in enumerate(G.edges)}, name="foo"
161
+ )
162
+
163
+ part = [
164
+ {0, 1, 2, 3, 7, 9, 11, 12, 13, 17, 19, 21},
165
+ {16, 4, 5, 6, 10},
166
+ {23, 25, 27, 28, 24, 31},
167
+ {32, 33, 8, 14, 15, 18, 20, 22, 26, 29, 30},
168
+ ]
169
+ partition1 = nx.community.louvain_communities(G, weight=None, seed=2)
170
+ partition2 = nx.community.louvain_communities(G, weight="foo", seed=2)
171
+ partition3 = nx.community.louvain_communities(G, weight="weight", seed=2)
172
+
173
+ assert part == partition1
174
+ assert part != partition2
175
+ assert part != partition3
176
+ assert partition2 != partition3
177
+
178
+
179
+ def test_quality():
180
+ G = nx.LFR_benchmark_graph(
181
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
182
+ )
183
+ H = nx.gn_graph(200, seed=1234)
184
+ I = nx.MultiGraph(G)
185
+ J = nx.MultiDiGraph(H)
186
+
187
+ partition = nx.community.louvain_communities(G)
188
+ partition2 = nx.community.louvain_communities(H)
189
+ partition3 = nx.community.louvain_communities(I)
190
+ partition4 = nx.community.louvain_communities(J)
191
+
192
+ quality = nx.community.partition_quality(G, partition)[0]
193
+ quality2 = nx.community.partition_quality(H, partition2)[0]
194
+ quality3 = nx.community.partition_quality(I, partition3)[0]
195
+ quality4 = nx.community.partition_quality(J, partition4)[0]
196
+
197
+ assert quality >= 0.65
198
+ assert quality2 >= 0.65
199
+ assert quality3 >= 0.65
200
+ assert quality4 >= 0.65
201
+
202
+
203
+ def test_multigraph():
204
+ G = nx.karate_club_graph()
205
+ H = nx.MultiGraph(G)
206
+ G.add_edge(0, 1, weight=10)
207
+ H.add_edge(0, 1, weight=9)
208
+ G.add_edge(0, 9, foo=20)
209
+ H.add_edge(0, 9, foo=20)
210
+
211
+ partition1 = nx.community.louvain_communities(G, seed=1234)
212
+ partition2 = nx.community.louvain_communities(H, seed=1234)
213
+ partition3 = nx.community.louvain_communities(H, weight="foo", seed=1234)
214
+
215
+ assert partition1 == partition2 != partition3
216
+
217
+
218
+ def test_resolution():
219
+ G = nx.LFR_benchmark_graph(
220
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
221
+ )
222
+
223
+ partition1 = nx.community.louvain_communities(G, resolution=0.5, seed=12)
224
+ partition2 = nx.community.louvain_communities(G, seed=12)
225
+ partition3 = nx.community.louvain_communities(G, resolution=2, seed=12)
226
+
227
+ assert len(partition1) <= len(partition2) <= len(partition3)
228
+
229
+
230
+ def test_threshold():
231
+ G = nx.LFR_benchmark_graph(
232
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
233
+ )
234
+ partition1 = nx.community.louvain_communities(G, threshold=0.3, seed=2)
235
+ partition2 = nx.community.louvain_communities(G, seed=2)
236
+ mod1 = nx.community.modularity(G, partition1)
237
+ mod2 = nx.community.modularity(G, partition2)
238
+
239
+ assert mod1 <= mod2
240
+
241
+
242
+ def test_empty_graph():
243
+ G = nx.Graph()
244
+ G.add_nodes_from(range(5))
245
+ expected = [{0}, {1}, {2}, {3}, {4}]
246
+ assert nx.community.louvain_communities(G) == expected
247
+
248
+
249
+ def test_max_level():
250
+ G = nx.LFR_benchmark_graph(
251
+ 250, 3, 1.5, 0.009, average_degree=5, min_community=20, seed=10
252
+ )
253
+ parts_iter = nx.community.louvain_partitions(G, seed=42)
254
+ for max_level, expected in enumerate(parts_iter, 1):
255
+ partition = nx.community.louvain_communities(G, max_level=max_level, seed=42)
256
+ assert partition == expected
257
+ assert max_level > 1 # Ensure we are actually testing max_level
258
+ # max_level is an upper limit; it's okay if we stop before it's hit.
259
+ partition = nx.community.louvain_communities(G, max_level=max_level + 1, seed=42)
260
+ assert partition == expected
261
+ with pytest.raises(
262
+ ValueError, match="max_level argument must be a positive integer"
263
+ ):
264
+ nx.community.louvain_communities(G, max_level=0)
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_lukes.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import product
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+
7
+ EWL = "e_weight"
8
+ NWL = "n_weight"
9
+
10
+
11
+ # first test from the Lukes original paper
12
+ def paper_1_case(float_edge_wt=False, explicit_node_wt=True, directed=False):
13
+ # problem-specific constants
14
+ limit = 3
15
+
16
+ # configuration
17
+ if float_edge_wt:
18
+ shift = 0.001
19
+ else:
20
+ shift = 0
21
+
22
+ if directed:
23
+ example_1 = nx.DiGraph()
24
+ else:
25
+ example_1 = nx.Graph()
26
+
27
+ # graph creation
28
+ example_1.add_edge(1, 2, **{EWL: 3 + shift})
29
+ example_1.add_edge(1, 4, **{EWL: 2 + shift})
30
+ example_1.add_edge(2, 3, **{EWL: 4 + shift})
31
+ example_1.add_edge(2, 5, **{EWL: 6 + shift})
32
+
33
+ # node weights
34
+ if explicit_node_wt:
35
+ nx.set_node_attributes(example_1, 1, NWL)
36
+ wtu = NWL
37
+ else:
38
+ wtu = None
39
+
40
+ # partitioning
41
+ clusters_1 = {
42
+ frozenset(x)
43
+ for x in nx.community.lukes_partitioning(
44
+ example_1, limit, node_weight=wtu, edge_weight=EWL
45
+ )
46
+ }
47
+
48
+ return clusters_1
49
+
50
+
51
+ # second test from the Lukes original paper
52
+ def paper_2_case(explicit_edge_wt=True, directed=False):
53
+ # problem specific constants
54
+ byte_block_size = 32
55
+
56
+ # configuration
57
+ if directed:
58
+ example_2 = nx.DiGraph()
59
+ else:
60
+ example_2 = nx.Graph()
61
+
62
+ if explicit_edge_wt:
63
+ edic = {EWL: 1}
64
+ wtu = EWL
65
+ else:
66
+ edic = {}
67
+ wtu = None
68
+
69
+ # graph creation
70
+ example_2.add_edge("name", "home_address", **edic)
71
+ example_2.add_edge("name", "education", **edic)
72
+ example_2.add_edge("education", "bs", **edic)
73
+ example_2.add_edge("education", "ms", **edic)
74
+ example_2.add_edge("education", "phd", **edic)
75
+ example_2.add_edge("name", "telephone", **edic)
76
+ example_2.add_edge("telephone", "home", **edic)
77
+ example_2.add_edge("telephone", "office", **edic)
78
+ example_2.add_edge("office", "no1", **edic)
79
+ example_2.add_edge("office", "no2", **edic)
80
+
81
+ example_2.nodes["name"][NWL] = 20
82
+ example_2.nodes["education"][NWL] = 10
83
+ example_2.nodes["bs"][NWL] = 1
84
+ example_2.nodes["ms"][NWL] = 1
85
+ example_2.nodes["phd"][NWL] = 1
86
+ example_2.nodes["home_address"][NWL] = 8
87
+ example_2.nodes["telephone"][NWL] = 8
88
+ example_2.nodes["home"][NWL] = 8
89
+ example_2.nodes["office"][NWL] = 4
90
+ example_2.nodes["no1"][NWL] = 1
91
+ example_2.nodes["no2"][NWL] = 1
92
+
93
+ # partitioning
94
+ clusters_2 = {
95
+ frozenset(x)
96
+ for x in nx.community.lukes_partitioning(
97
+ example_2, byte_block_size, node_weight=NWL, edge_weight=wtu
98
+ )
99
+ }
100
+
101
+ return clusters_2
102
+
103
+
104
+ def test_paper_1_case():
105
+ ground_truth = {frozenset([1, 4]), frozenset([2, 3, 5])}
106
+
107
+ tf = (True, False)
108
+ for flt, nwt, drc in product(tf, tf, tf):
109
+ part = paper_1_case(flt, nwt, drc)
110
+ assert part == ground_truth
111
+
112
+
113
+ def test_paper_2_case():
114
+ ground_truth = {
115
+ frozenset(["education", "bs", "ms", "phd"]),
116
+ frozenset(["name", "home_address"]),
117
+ frozenset(["telephone", "home", "office", "no1", "no2"]),
118
+ }
119
+
120
+ tf = (True, False)
121
+ for ewt, drc in product(tf, tf):
122
+ part = paper_2_case(ewt, drc)
123
+ assert part == ground_truth
124
+
125
+
126
+ def test_mandatory_tree():
127
+ not_a_tree = nx.complete_graph(4)
128
+
129
+ with pytest.raises(nx.NotATree):
130
+ nx.community.lukes_partitioning(not_a_tree, 5)
131
+
132
+
133
+ def test_mandatory_integrality():
134
+ byte_block_size = 32
135
+
136
+ ex_1_broken = nx.DiGraph()
137
+
138
+ ex_1_broken.add_edge(1, 2, **{EWL: 3.2})
139
+ ex_1_broken.add_edge(1, 4, **{EWL: 2.4})
140
+ ex_1_broken.add_edge(2, 3, **{EWL: 4.0})
141
+ ex_1_broken.add_edge(2, 5, **{EWL: 6.3})
142
+
143
+ ex_1_broken.nodes[1][NWL] = 1.2 # !
144
+ ex_1_broken.nodes[2][NWL] = 1
145
+ ex_1_broken.nodes[3][NWL] = 1
146
+ ex_1_broken.nodes[4][NWL] = 1
147
+ ex_1_broken.nodes[5][NWL] = 2
148
+
149
+ with pytest.raises(TypeError):
150
+ nx.community.lukes_partitioning(
151
+ ex_1_broken, byte_block_size, node_weight=NWL, edge_weight=EWL
152
+ )
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_modularity_max.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx.algorithms.community import (
5
+ greedy_modularity_communities,
6
+ naive_greedy_modularity_communities,
7
+ )
8
+
9
+
10
+ @pytest.mark.parametrize(
11
+ "func", (greedy_modularity_communities, naive_greedy_modularity_communities)
12
+ )
13
+ def test_modularity_communities(func):
14
+ G = nx.karate_club_graph()
15
+ john_a = frozenset(
16
+ [8, 14, 15, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]
17
+ )
18
+ mr_hi = frozenset([0, 4, 5, 6, 10, 11, 16, 19])
19
+ overlap = frozenset([1, 2, 3, 7, 9, 12, 13, 17, 21])
20
+ expected = {john_a, overlap, mr_hi}
21
+ assert set(func(G, weight=None)) == expected
22
+
23
+
24
+ @pytest.mark.parametrize(
25
+ "func", (greedy_modularity_communities, naive_greedy_modularity_communities)
26
+ )
27
+ def test_modularity_communities_categorical_labels(func):
28
+ # Using other than 0-starting contiguous integers as node-labels.
29
+ G = nx.Graph(
30
+ [
31
+ ("a", "b"),
32
+ ("a", "c"),
33
+ ("b", "c"),
34
+ ("b", "d"), # inter-community edge
35
+ ("d", "e"),
36
+ ("d", "f"),
37
+ ("d", "g"),
38
+ ("f", "g"),
39
+ ("d", "e"),
40
+ ("f", "e"),
41
+ ]
42
+ )
43
+ expected = {frozenset({"f", "g", "e", "d"}), frozenset({"a", "b", "c"})}
44
+ assert set(func(G)) == expected
45
+
46
+
47
+ def test_greedy_modularity_communities_components():
48
+ # Test for gh-5530
49
+ G = nx.Graph([(0, 1), (2, 3), (4, 5), (5, 6)])
50
+ # usual case with 3 components
51
+ assert greedy_modularity_communities(G) == [{4, 5, 6}, {0, 1}, {2, 3}]
52
+ # best_n can make the algorithm continue even when modularity goes down
53
+ assert greedy_modularity_communities(G, best_n=3) == [{4, 5, 6}, {0, 1}, {2, 3}]
54
+ assert greedy_modularity_communities(G, best_n=2) == [{0, 1, 4, 5, 6}, {2, 3}]
55
+ assert greedy_modularity_communities(G, best_n=1) == [{0, 1, 2, 3, 4, 5, 6}]
56
+
57
+
58
+ def test_greedy_modularity_communities_relabeled():
59
+ # Test for gh-4966
60
+ G = nx.balanced_tree(2, 2)
61
+ mapping = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h"}
62
+ G = nx.relabel_nodes(G, mapping)
63
+ expected = [frozenset({"e", "d", "a", "b"}), frozenset({"c", "f", "g"})]
64
+ assert greedy_modularity_communities(G) == expected
65
+
66
+
67
+ def test_greedy_modularity_communities_directed():
68
+ G = nx.DiGraph(
69
+ [
70
+ ("a", "b"),
71
+ ("a", "c"),
72
+ ("b", "c"),
73
+ ("b", "d"), # inter-community edge
74
+ ("d", "e"),
75
+ ("d", "f"),
76
+ ("d", "g"),
77
+ ("f", "g"),
78
+ ("d", "e"),
79
+ ("f", "e"),
80
+ ]
81
+ )
82
+ expected = [frozenset({"f", "g", "e", "d"}), frozenset({"a", "b", "c"})]
83
+ assert greedy_modularity_communities(G) == expected
84
+
85
+ # with loops
86
+ G = nx.DiGraph()
87
+ G.add_edges_from(
88
+ [(1, 1), (1, 2), (1, 3), (2, 3), (1, 4), (4, 4), (5, 5), (4, 5), (4, 6), (5, 6)]
89
+ )
90
+ expected = [frozenset({1, 2, 3}), frozenset({4, 5, 6})]
91
+ assert greedy_modularity_communities(G) == expected
92
+
93
+
94
+ @pytest.mark.parametrize(
95
+ "func", (greedy_modularity_communities, naive_greedy_modularity_communities)
96
+ )
97
+ def test_modularity_communities_weighted(func):
98
+ G = nx.balanced_tree(2, 3)
99
+ for a, b in G.edges:
100
+ if ((a == 1) or (a == 2)) and (b != 0):
101
+ G[a][b]["weight"] = 10.0
102
+ else:
103
+ G[a][b]["weight"] = 1.0
104
+
105
+ expected = [{0, 1, 3, 4, 7, 8, 9, 10}, {2, 5, 6, 11, 12, 13, 14}]
106
+
107
+ assert func(G, weight="weight") == expected
108
+ assert func(G, weight="weight", resolution=0.9) == expected
109
+ assert func(G, weight="weight", resolution=0.3) == expected
110
+ assert func(G, weight="weight", resolution=1.1) != expected
111
+
112
+
113
+ def test_modularity_communities_floating_point():
114
+ # check for floating point error when used as key in the mapped_queue dict.
115
+ # Test for gh-4992 and gh-5000
116
+ G = nx.Graph()
117
+ G.add_weighted_edges_from(
118
+ [(0, 1, 12), (1, 4, 71), (2, 3, 15), (2, 4, 10), (3, 6, 13)]
119
+ )
120
+ expected = [{0, 1, 4}, {2, 3, 6}]
121
+ assert greedy_modularity_communities(G, weight="weight") == expected
122
+ assert (
123
+ greedy_modularity_communities(G, weight="weight", resolution=0.99) == expected
124
+ )
125
+
126
+
127
+ def test_modularity_communities_directed_weighted():
128
+ G = nx.DiGraph()
129
+ G.add_weighted_edges_from(
130
+ [
131
+ (1, 2, 5),
132
+ (1, 3, 3),
133
+ (2, 3, 6),
134
+ (2, 6, 1),
135
+ (1, 4, 1),
136
+ (4, 5, 3),
137
+ (4, 6, 7),
138
+ (5, 6, 2),
139
+ (5, 7, 5),
140
+ (5, 8, 4),
141
+ (6, 8, 3),
142
+ ]
143
+ )
144
+ expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})]
145
+ assert greedy_modularity_communities(G, weight="weight") == expected
146
+
147
+ # A large weight of the edge (2, 6) causes 6 to change group, even if it shares
148
+ # only one connection with the new group and 3 with the old one.
149
+ G[2][6]["weight"] = 20
150
+ expected = [frozenset({1, 2, 3, 6}), frozenset({4, 5, 7, 8})]
151
+ assert greedy_modularity_communities(G, weight="weight") == expected
152
+
153
+
154
+ def test_greedy_modularity_communities_multigraph():
155
+ G = nx.MultiGraph()
156
+ G.add_edges_from(
157
+ [
158
+ (1, 2),
159
+ (1, 2),
160
+ (1, 3),
161
+ (2, 3),
162
+ (1, 4),
163
+ (2, 4),
164
+ (4, 5),
165
+ (5, 6),
166
+ (5, 7),
167
+ (5, 7),
168
+ (6, 7),
169
+ (7, 8),
170
+ (5, 8),
171
+ ]
172
+ )
173
+ expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})]
174
+ assert greedy_modularity_communities(G) == expected
175
+
176
+ # Converting (4, 5) into a multi-edge causes node 4 to change group.
177
+ G.add_edge(4, 5)
178
+ expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})]
179
+ assert greedy_modularity_communities(G) == expected
180
+
181
+
182
+ def test_greedy_modularity_communities_multigraph_weighted():
183
+ G = nx.MultiGraph()
184
+ G.add_weighted_edges_from(
185
+ [
186
+ (1, 2, 5),
187
+ (1, 2, 3),
188
+ (1, 3, 6),
189
+ (1, 3, 6),
190
+ (2, 3, 4),
191
+ (1, 4, 1),
192
+ (1, 4, 1),
193
+ (2, 4, 3),
194
+ (2, 4, 3),
195
+ (4, 5, 1),
196
+ (5, 6, 3),
197
+ (5, 6, 7),
198
+ (5, 6, 4),
199
+ (5, 7, 9),
200
+ (5, 7, 9),
201
+ (6, 7, 8),
202
+ (7, 8, 2),
203
+ (7, 8, 2),
204
+ (5, 8, 6),
205
+ (5, 8, 6),
206
+ ]
207
+ )
208
+ expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})]
209
+ assert greedy_modularity_communities(G, weight="weight") == expected
210
+
211
+ # Adding multi-edge (4, 5, 16) causes node 4 to change group.
212
+ G.add_edge(4, 5, weight=16)
213
+ expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})]
214
+ assert greedy_modularity_communities(G, weight="weight") == expected
215
+
216
+ # Increasing the weight of edge (1, 4) causes node 4 to return to the former group.
217
+ G[1][4][1]["weight"] = 3
218
+ expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})]
219
+ assert greedy_modularity_communities(G, weight="weight") == expected
220
+
221
+
222
+ def test_greed_modularity_communities_multidigraph():
223
+ G = nx.MultiDiGraph()
224
+ G.add_edges_from(
225
+ [
226
+ (1, 2),
227
+ (1, 2),
228
+ (3, 1),
229
+ (2, 3),
230
+ (2, 3),
231
+ (3, 2),
232
+ (1, 4),
233
+ (2, 4),
234
+ (4, 2),
235
+ (4, 5),
236
+ (5, 6),
237
+ (5, 6),
238
+ (6, 5),
239
+ (5, 7),
240
+ (6, 7),
241
+ (7, 8),
242
+ (5, 8),
243
+ (8, 4),
244
+ ]
245
+ )
246
+ expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})]
247
+ assert greedy_modularity_communities(G, weight="weight") == expected
248
+
249
+
250
+ def test_greed_modularity_communities_multidigraph_weighted():
251
+ G = nx.MultiDiGraph()
252
+ G.add_weighted_edges_from(
253
+ [
254
+ (1, 2, 5),
255
+ (1, 2, 3),
256
+ (3, 1, 6),
257
+ (1, 3, 6),
258
+ (3, 2, 4),
259
+ (1, 4, 2),
260
+ (1, 4, 5),
261
+ (2, 4, 3),
262
+ (3, 2, 8),
263
+ (4, 2, 3),
264
+ (4, 3, 5),
265
+ (4, 5, 2),
266
+ (5, 6, 3),
267
+ (5, 6, 7),
268
+ (6, 5, 4),
269
+ (5, 7, 9),
270
+ (5, 7, 9),
271
+ (7, 6, 8),
272
+ (7, 8, 2),
273
+ (8, 7, 2),
274
+ (5, 8, 6),
275
+ (5, 8, 6),
276
+ ]
277
+ )
278
+ expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})]
279
+ assert greedy_modularity_communities(G, weight="weight") == expected
280
+
281
+
282
+ def test_resolution_parameter_impact():
283
+ G = nx.barbell_graph(5, 3)
284
+
285
+ gamma = 1
286
+ expected = [frozenset(range(5)), frozenset(range(8, 13)), frozenset(range(5, 8))]
287
+ assert greedy_modularity_communities(G, resolution=gamma) == expected
288
+ assert naive_greedy_modularity_communities(G, resolution=gamma) == expected
289
+
290
+ gamma = 2.5
291
+ expected = [{0, 1, 2, 3}, {9, 10, 11, 12}, {5, 6, 7}, {4}, {8}]
292
+ assert greedy_modularity_communities(G, resolution=gamma) == expected
293
+ assert naive_greedy_modularity_communities(G, resolution=gamma) == expected
294
+
295
+ gamma = 0.3
296
+ expected = [frozenset(range(8)), frozenset(range(8, 13))]
297
+ assert greedy_modularity_communities(G, resolution=gamma) == expected
298
+ assert naive_greedy_modularity_communities(G, resolution=gamma) == expected
299
+
300
+
301
+ def test_cutoff_parameter():
302
+ G = nx.circular_ladder_graph(4)
303
+
304
+ # No aggregation:
305
+ expected = [{k} for k in range(8)]
306
+ assert greedy_modularity_communities(G, cutoff=8) == expected
307
+
308
+ # Aggregation to half order (number of nodes)
309
+ expected = [{k, k + 1} for k in range(0, 8, 2)]
310
+ assert greedy_modularity_communities(G, cutoff=4) == expected
311
+
312
+ # Default aggregation case (here, 2 communities emerge)
313
+ expected = [frozenset(range(4)), frozenset(range(4, 8))]
314
+ assert greedy_modularity_communities(G, cutoff=1) == expected
315
+
316
+
317
+ def test_best_n():
318
+ G = nx.barbell_graph(5, 3)
319
+
320
+ # Same result as without enforcing cutoff:
321
+ best_n = 3
322
+ expected = [frozenset(range(5)), frozenset(range(8, 13)), frozenset(range(5, 8))]
323
+ assert greedy_modularity_communities(G, best_n=best_n) == expected
324
+
325
+ # One additional merging step:
326
+ best_n = 2
327
+ expected = [frozenset(range(8)), frozenset(range(8, 13))]
328
+ assert greedy_modularity_communities(G, best_n=best_n) == expected
329
+
330
+ # Two additional merging steps:
331
+ best_n = 1
332
+ expected = [frozenset(range(13))]
333
+ assert greedy_modularity_communities(G, best_n=best_n) == expected
334
+
335
+
336
+ def test_greedy_modularity_communities_corner_cases():
337
+ G = nx.empty_graph()
338
+ assert nx.community.greedy_modularity_communities(G) == []
339
+ G.add_nodes_from(range(3))
340
+ assert nx.community.greedy_modularity_communities(G) == [{0}, {1}, {2}]
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_quality.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.community.quality`
2
+ module.
3
+
4
+ """
5
+
6
+ import pytest
7
+
8
+ import networkx as nx
9
+ from networkx import barbell_graph
10
+ from networkx.algorithms.community import modularity, partition_quality
11
+ from networkx.algorithms.community.quality import inter_community_edges
12
+
13
+
14
+ class TestPerformance:
15
+ """Unit tests for the :func:`performance` function."""
16
+
17
+ def test_bad_partition(self):
18
+ """Tests that a poor partition has a low performance measure."""
19
+ G = barbell_graph(3, 0)
20
+ partition = [{0, 1, 4}, {2, 3, 5}]
21
+ assert 8 / 15 == pytest.approx(partition_quality(G, partition)[1], abs=1e-7)
22
+
23
+ def test_good_partition(self):
24
+ """Tests that a good partition has a high performance measure."""
25
+ G = barbell_graph(3, 0)
26
+ partition = [{0, 1, 2}, {3, 4, 5}]
27
+ assert 14 / 15 == pytest.approx(partition_quality(G, partition)[1], abs=1e-7)
28
+
29
+
30
+ class TestCoverage:
31
+ """Unit tests for the :func:`coverage` function."""
32
+
33
+ def test_bad_partition(self):
34
+ """Tests that a poor partition has a low coverage measure."""
35
+ G = barbell_graph(3, 0)
36
+ partition = [{0, 1, 4}, {2, 3, 5}]
37
+ assert 3 / 7 == pytest.approx(partition_quality(G, partition)[0], abs=1e-7)
38
+
39
+ def test_good_partition(self):
40
+ """Tests that a good partition has a high coverage measure."""
41
+ G = barbell_graph(3, 0)
42
+ partition = [{0, 1, 2}, {3, 4, 5}]
43
+ assert 6 / 7 == pytest.approx(partition_quality(G, partition)[0], abs=1e-7)
44
+
45
+
46
+ def test_modularity():
47
+ G = nx.barbell_graph(3, 0)
48
+ C = [{0, 1, 4}, {2, 3, 5}]
49
+ assert (-16 / (14**2)) == pytest.approx(modularity(G, C), abs=1e-7)
50
+ C = [{0, 1, 2}, {3, 4, 5}]
51
+ assert (35 * 2) / (14**2) == pytest.approx(modularity(G, C), abs=1e-7)
52
+
53
+ n = 1000
54
+ G = nx.erdos_renyi_graph(n, 0.09, seed=42, directed=True)
55
+ C = [set(range(n // 2)), set(range(n // 2, n))]
56
+ assert 0.00017154251389292754 == pytest.approx(modularity(G, C), abs=1e-7)
57
+
58
+ G = nx.margulis_gabber_galil_graph(10)
59
+ mid_value = G.number_of_nodes() // 2
60
+ nodes = list(G.nodes)
61
+ C = [set(nodes[:mid_value]), set(nodes[mid_value:])]
62
+ assert 0.13 == pytest.approx(modularity(G, C), abs=1e-7)
63
+
64
+ G = nx.DiGraph()
65
+ G.add_edges_from([(2, 1), (2, 3), (3, 4)])
66
+ C = [{1, 2}, {3, 4}]
67
+ assert 2 / 9 == pytest.approx(modularity(G, C), abs=1e-7)
68
+
69
+
70
+ def test_modularity_resolution():
71
+ G = nx.barbell_graph(3, 0)
72
+ C = [{0, 1, 4}, {2, 3, 5}]
73
+ assert modularity(G, C) == pytest.approx(3 / 7 - 100 / 14**2)
74
+ gamma = 2
75
+ result = modularity(G, C, resolution=gamma)
76
+ assert result == pytest.approx(3 / 7 - gamma * 100 / 14**2)
77
+ gamma = 0.2
78
+ result = modularity(G, C, resolution=gamma)
79
+ assert result == pytest.approx(3 / 7 - gamma * 100 / 14**2)
80
+
81
+ C = [{0, 1, 2}, {3, 4, 5}]
82
+ assert modularity(G, C) == pytest.approx(6 / 7 - 98 / 14**2)
83
+ gamma = 2
84
+ result = modularity(G, C, resolution=gamma)
85
+ assert result == pytest.approx(6 / 7 - gamma * 98 / 14**2)
86
+ gamma = 0.2
87
+ result = modularity(G, C, resolution=gamma)
88
+ assert result == pytest.approx(6 / 7 - gamma * 98 / 14**2)
89
+
90
+ G = nx.barbell_graph(5, 3)
91
+ C = [frozenset(range(5)), frozenset(range(8, 13)), frozenset(range(5, 8))]
92
+ gamma = 1
93
+ result = modularity(G, C, resolution=gamma)
94
+ # This C is maximal for gamma=1: modularity = 0.518229
95
+ assert result == pytest.approx((22 / 24) - gamma * (918 / (48**2)))
96
+ gamma = 2
97
+ result = modularity(G, C, resolution=gamma)
98
+ assert result == pytest.approx((22 / 24) - gamma * (918 / (48**2)))
99
+ gamma = 0.2
100
+ result = modularity(G, C, resolution=gamma)
101
+ assert result == pytest.approx((22 / 24) - gamma * (918 / (48**2)))
102
+
103
+ C = [{0, 1, 2, 3}, {9, 10, 11, 12}, {5, 6, 7}, {4}, {8}]
104
+ gamma = 1
105
+ result = modularity(G, C, resolution=gamma)
106
+ assert result == pytest.approx((14 / 24) - gamma * (598 / (48**2)))
107
+ gamma = 2.5
108
+ result = modularity(G, C, resolution=gamma)
109
+ # This C is maximal for gamma=2.5: modularity = -0.06553819
110
+ assert result == pytest.approx((14 / 24) - gamma * (598 / (48**2)))
111
+ gamma = 0.2
112
+ result = modularity(G, C, resolution=gamma)
113
+ assert result == pytest.approx((14 / 24) - gamma * (598 / (48**2)))
114
+
115
+ C = [frozenset(range(8)), frozenset(range(8, 13))]
116
+ gamma = 1
117
+ result = modularity(G, C, resolution=gamma)
118
+ assert result == pytest.approx((23 / 24) - gamma * (1170 / (48**2)))
119
+ gamma = 2
120
+ result = modularity(G, C, resolution=gamma)
121
+ assert result == pytest.approx((23 / 24) - gamma * (1170 / (48**2)))
122
+ gamma = 0.3
123
+ result = modularity(G, C, resolution=gamma)
124
+ # This C is maximal for gamma=0.3: modularity = 0.805990
125
+ assert result == pytest.approx((23 / 24) - gamma * (1170 / (48**2)))
126
+
127
+
128
+ def test_inter_community_edges_with_digraphs():
129
+ G = nx.complete_graph(2, create_using=nx.DiGraph())
130
+ partition = [{0}, {1}]
131
+ assert inter_community_edges(G, partition) == 2
132
+
133
+ G = nx.complete_graph(10, create_using=nx.DiGraph())
134
+ partition = [{0}, {1, 2}, {3, 4, 5}, {6, 7, 8, 9}]
135
+ assert inter_community_edges(G, partition) == 70
136
+
137
+ G = nx.cycle_graph(4, create_using=nx.DiGraph())
138
+ partition = [{0, 1}, {2, 3}]
139
+ assert inter_community_edges(G, partition) == 2
.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_utils.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.community.utils` module."""
2
+
3
+ import networkx as nx
4
+
5
+
6
+ def test_is_partition():
7
+ G = nx.empty_graph(3)
8
+ assert nx.community.is_partition(G, [{0, 1}, {2}])
9
+ assert nx.community.is_partition(G, ({0, 1}, {2}))
10
+ assert nx.community.is_partition(G, ([0, 1], [2]))
11
+ assert nx.community.is_partition(G, [[0, 1], [2]])
12
+
13
+
14
+ def test_not_covering():
15
+ G = nx.empty_graph(3)
16
+ assert not nx.community.is_partition(G, [{0}, {1}])
17
+
18
+
19
+ def test_not_disjoint():
20
+ G = nx.empty_graph(3)
21
+ assert not nx.community.is_partition(G, [{0, 1}, {1, 2}])
22
+
23
+
24
+ def test_not_node():
25
+ G = nx.empty_graph(3)
26
+ assert not nx.community.is_partition(G, [{0, 1}, {3}])
.venv/lib/python3.11/site-packages/networkx/algorithms/components/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from .connected import *
2
+ from .strongly_connected import *
3
+ from .weakly_connected import *
4
+ from .attracting import *
5
+ from .biconnected import *
6
+ from .semiconnected import *
.venv/lib/python3.11/site-packages/networkx/algorithms/components/__pycache__/connected.cpython-311.pyc ADDED
Binary file (6.25 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/__pycache__/semiconnected.cpython-311.pyc ADDED
Binary file (2.99 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/attracting.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Attracting components."""
2
+
3
+ import networkx as nx
4
+ from networkx.utils.decorators import not_implemented_for
5
+
6
+ __all__ = [
7
+ "number_attracting_components",
8
+ "attracting_components",
9
+ "is_attracting_component",
10
+ ]
11
+
12
+
13
+ @not_implemented_for("undirected")
14
+ @nx._dispatchable
15
+ def attracting_components(G):
16
+ """Generates the attracting components in `G`.
17
+
18
+ An attracting component in a directed graph `G` is a strongly connected
19
+ component with the property that a random walker on the graph will never
20
+ leave the component, once it enters the component.
21
+
22
+ The nodes in attracting components can also be thought of as recurrent
23
+ nodes. If a random walker enters the attractor containing the node, then
24
+ the node will be visited infinitely often.
25
+
26
+ To obtain induced subgraphs on each component use:
27
+ ``(G.subgraph(c).copy() for c in attracting_components(G))``
28
+
29
+ Parameters
30
+ ----------
31
+ G : DiGraph, MultiDiGraph
32
+ The graph to be analyzed.
33
+
34
+ Returns
35
+ -------
36
+ attractors : generator of sets
37
+ A generator of sets of nodes, one for each attracting component of G.
38
+
39
+ Raises
40
+ ------
41
+ NetworkXNotImplemented
42
+ If the input graph is undirected.
43
+
44
+ See Also
45
+ --------
46
+ number_attracting_components
47
+ is_attracting_component
48
+
49
+ """
50
+ scc = list(nx.strongly_connected_components(G))
51
+ cG = nx.condensation(G, scc)
52
+ for n in cG:
53
+ if cG.out_degree(n) == 0:
54
+ yield scc[n]
55
+
56
+
57
+ @not_implemented_for("undirected")
58
+ @nx._dispatchable
59
+ def number_attracting_components(G):
60
+ """Returns the number of attracting components in `G`.
61
+
62
+ Parameters
63
+ ----------
64
+ G : DiGraph, MultiDiGraph
65
+ The graph to be analyzed.
66
+
67
+ Returns
68
+ -------
69
+ n : int
70
+ The number of attracting components in G.
71
+
72
+ Raises
73
+ ------
74
+ NetworkXNotImplemented
75
+ If the input graph is undirected.
76
+
77
+ See Also
78
+ --------
79
+ attracting_components
80
+ is_attracting_component
81
+
82
+ """
83
+ return sum(1 for ac in attracting_components(G))
84
+
85
+
86
+ @not_implemented_for("undirected")
87
+ @nx._dispatchable
88
+ def is_attracting_component(G):
89
+ """Returns True if `G` consists of a single attracting component.
90
+
91
+ Parameters
92
+ ----------
93
+ G : DiGraph, MultiDiGraph
94
+ The graph to be analyzed.
95
+
96
+ Returns
97
+ -------
98
+ attracting : bool
99
+ True if `G` has a single attracting component. Otherwise, False.
100
+
101
+ Raises
102
+ ------
103
+ NetworkXNotImplemented
104
+ If the input graph is undirected.
105
+
106
+ See Also
107
+ --------
108
+ attracting_components
109
+ number_attracting_components
110
+
111
+ """
112
+ ac = list(attracting_components(G))
113
+ if len(ac) == 1:
114
+ return len(ac[0]) == len(G)
115
+ return False
.venv/lib/python3.11/site-packages/networkx/algorithms/components/biconnected.py ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Biconnected components and articulation points."""
2
+
3
+ from itertools import chain
4
+
5
+ import networkx as nx
6
+ from networkx.utils.decorators import not_implemented_for
7
+
8
+ __all__ = [
9
+ "biconnected_components",
10
+ "biconnected_component_edges",
11
+ "is_biconnected",
12
+ "articulation_points",
13
+ ]
14
+
15
+
16
+ @not_implemented_for("directed")
17
+ @nx._dispatchable
18
+ def is_biconnected(G):
19
+ """Returns True if the graph is biconnected, False otherwise.
20
+
21
+ A graph is biconnected if, and only if, it cannot be disconnected by
22
+ removing only one node (and all edges incident on that node). If
23
+ removing a node increases the number of disconnected components
24
+ in the graph, that node is called an articulation point, or cut
25
+ vertex. A biconnected graph has no articulation points.
26
+
27
+ Parameters
28
+ ----------
29
+ G : NetworkX Graph
30
+ An undirected graph.
31
+
32
+ Returns
33
+ -------
34
+ biconnected : bool
35
+ True if the graph is biconnected, False otherwise.
36
+
37
+ Raises
38
+ ------
39
+ NetworkXNotImplemented
40
+ If the input graph is not undirected.
41
+
42
+ Examples
43
+ --------
44
+ >>> G = nx.path_graph(4)
45
+ >>> print(nx.is_biconnected(G))
46
+ False
47
+ >>> G.add_edge(0, 3)
48
+ >>> print(nx.is_biconnected(G))
49
+ True
50
+
51
+ See Also
52
+ --------
53
+ biconnected_components
54
+ articulation_points
55
+ biconnected_component_edges
56
+ is_strongly_connected
57
+ is_weakly_connected
58
+ is_connected
59
+ is_semiconnected
60
+
61
+ Notes
62
+ -----
63
+ The algorithm to find articulation points and biconnected
64
+ components is implemented using a non-recursive depth-first-search
65
+ (DFS) that keeps track of the highest level that back edges reach
66
+ in the DFS tree. A node `n` is an articulation point if, and only
67
+ if, there exists a subtree rooted at `n` such that there is no
68
+ back edge from any successor of `n` that links to a predecessor of
69
+ `n` in the DFS tree. By keeping track of all the edges traversed
70
+ by the DFS we can obtain the biconnected components because all
71
+ edges of a bicomponent will be traversed consecutively between
72
+ articulation points.
73
+
74
+ References
75
+ ----------
76
+ .. [1] Hopcroft, J.; Tarjan, R. (1973).
77
+ "Efficient algorithms for graph manipulation".
78
+ Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
79
+
80
+ """
81
+ bccs = biconnected_components(G)
82
+ try:
83
+ bcc = next(bccs)
84
+ except StopIteration:
85
+ # No bicomponents (empty graph?)
86
+ return False
87
+ try:
88
+ next(bccs)
89
+ except StopIteration:
90
+ # Only one bicomponent
91
+ return len(bcc) == len(G)
92
+ else:
93
+ # Multiple bicomponents
94
+ return False
95
+
96
+
97
+ @not_implemented_for("directed")
98
+ @nx._dispatchable
99
+ def biconnected_component_edges(G):
100
+ """Returns a generator of lists of edges, one list for each biconnected
101
+ component of the input graph.
102
+
103
+ Biconnected components are maximal subgraphs such that the removal of a
104
+ node (and all edges incident on that node) will not disconnect the
105
+ subgraph. Note that nodes may be part of more than one biconnected
106
+ component. Those nodes are articulation points, or cut vertices.
107
+ However, each edge belongs to one, and only one, biconnected component.
108
+
109
+ Notice that by convention a dyad is considered a biconnected component.
110
+
111
+ Parameters
112
+ ----------
113
+ G : NetworkX Graph
114
+ An undirected graph.
115
+
116
+ Returns
117
+ -------
118
+ edges : generator of lists
119
+ Generator of lists of edges, one list for each bicomponent.
120
+
121
+ Raises
122
+ ------
123
+ NetworkXNotImplemented
124
+ If the input graph is not undirected.
125
+
126
+ Examples
127
+ --------
128
+ >>> G = nx.barbell_graph(4, 2)
129
+ >>> print(nx.is_biconnected(G))
130
+ False
131
+ >>> bicomponents_edges = list(nx.biconnected_component_edges(G))
132
+ >>> len(bicomponents_edges)
133
+ 5
134
+ >>> G.add_edge(2, 8)
135
+ >>> print(nx.is_biconnected(G))
136
+ True
137
+ >>> bicomponents_edges = list(nx.biconnected_component_edges(G))
138
+ >>> len(bicomponents_edges)
139
+ 1
140
+
141
+ See Also
142
+ --------
143
+ is_biconnected,
144
+ biconnected_components,
145
+ articulation_points,
146
+
147
+ Notes
148
+ -----
149
+ The algorithm to find articulation points and biconnected
150
+ components is implemented using a non-recursive depth-first-search
151
+ (DFS) that keeps track of the highest level that back edges reach
152
+ in the DFS tree. A node `n` is an articulation point if, and only
153
+ if, there exists a subtree rooted at `n` such that there is no
154
+ back edge from any successor of `n` that links to a predecessor of
155
+ `n` in the DFS tree. By keeping track of all the edges traversed
156
+ by the DFS we can obtain the biconnected components because all
157
+ edges of a bicomponent will be traversed consecutively between
158
+ articulation points.
159
+
160
+ References
161
+ ----------
162
+ .. [1] Hopcroft, J.; Tarjan, R. (1973).
163
+ "Efficient algorithms for graph manipulation".
164
+ Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
165
+
166
+ """
167
+ yield from _biconnected_dfs(G, components=True)
168
+
169
+
170
+ @not_implemented_for("directed")
171
+ @nx._dispatchable
172
+ def biconnected_components(G):
173
+ """Returns a generator of sets of nodes, one set for each biconnected
174
+ component of the graph
175
+
176
+ Biconnected components are maximal subgraphs such that the removal of a
177
+ node (and all edges incident on that node) will not disconnect the
178
+ subgraph. Note that nodes may be part of more than one biconnected
179
+ component. Those nodes are articulation points, or cut vertices. The
180
+ removal of articulation points will increase the number of connected
181
+ components of the graph.
182
+
183
+ Notice that by convention a dyad is considered a biconnected component.
184
+
185
+ Parameters
186
+ ----------
187
+ G : NetworkX Graph
188
+ An undirected graph.
189
+
190
+ Returns
191
+ -------
192
+ nodes : generator
193
+ Generator of sets of nodes, one set for each biconnected component.
194
+
195
+ Raises
196
+ ------
197
+ NetworkXNotImplemented
198
+ If the input graph is not undirected.
199
+
200
+ Examples
201
+ --------
202
+ >>> G = nx.lollipop_graph(5, 1)
203
+ >>> print(nx.is_biconnected(G))
204
+ False
205
+ >>> bicomponents = list(nx.biconnected_components(G))
206
+ >>> len(bicomponents)
207
+ 2
208
+ >>> G.add_edge(0, 5)
209
+ >>> print(nx.is_biconnected(G))
210
+ True
211
+ >>> bicomponents = list(nx.biconnected_components(G))
212
+ >>> len(bicomponents)
213
+ 1
214
+
215
+ You can generate a sorted list of biconnected components, largest
216
+ first, using sort.
217
+
218
+ >>> G.remove_edge(0, 5)
219
+ >>> [len(c) for c in sorted(nx.biconnected_components(G), key=len, reverse=True)]
220
+ [5, 2]
221
+
222
+ If you only want the largest connected component, it's more
223
+ efficient to use max instead of sort.
224
+
225
+ >>> Gc = max(nx.biconnected_components(G), key=len)
226
+
227
+ To create the components as subgraphs use:
228
+ ``(G.subgraph(c).copy() for c in biconnected_components(G))``
229
+
230
+ See Also
231
+ --------
232
+ is_biconnected
233
+ articulation_points
234
+ biconnected_component_edges
235
+ k_components : this function is a special case where k=2
236
+ bridge_components : similar to this function, but is defined using
237
+ 2-edge-connectivity instead of 2-node-connectivity.
238
+
239
+ Notes
240
+ -----
241
+ The algorithm to find articulation points and biconnected
242
+ components is implemented using a non-recursive depth-first-search
243
+ (DFS) that keeps track of the highest level that back edges reach
244
+ in the DFS tree. A node `n` is an articulation point if, and only
245
+ if, there exists a subtree rooted at `n` such that there is no
246
+ back edge from any successor of `n` that links to a predecessor of
247
+ `n` in the DFS tree. By keeping track of all the edges traversed
248
+ by the DFS we can obtain the biconnected components because all
249
+ edges of a bicomponent will be traversed consecutively between
250
+ articulation points.
251
+
252
+ References
253
+ ----------
254
+ .. [1] Hopcroft, J.; Tarjan, R. (1973).
255
+ "Efficient algorithms for graph manipulation".
256
+ Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
257
+
258
+ """
259
+ for comp in _biconnected_dfs(G, components=True):
260
+ yield set(chain.from_iterable(comp))
261
+
262
+
263
+ @not_implemented_for("directed")
264
+ @nx._dispatchable
265
+ def articulation_points(G):
266
+ """Yield the articulation points, or cut vertices, of a graph.
267
+
268
+ An articulation point or cut vertex is any node whose removal (along with
269
+ all its incident edges) increases the number of connected components of
270
+ a graph. An undirected connected graph without articulation points is
271
+ biconnected. Articulation points belong to more than one biconnected
272
+ component of a graph.
273
+
274
+ Notice that by convention a dyad is considered a biconnected component.
275
+
276
+ Parameters
277
+ ----------
278
+ G : NetworkX Graph
279
+ An undirected graph.
280
+
281
+ Yields
282
+ ------
283
+ node
284
+ An articulation point in the graph.
285
+
286
+ Raises
287
+ ------
288
+ NetworkXNotImplemented
289
+ If the input graph is not undirected.
290
+
291
+ Examples
292
+ --------
293
+
294
+ >>> G = nx.barbell_graph(4, 2)
295
+ >>> print(nx.is_biconnected(G))
296
+ False
297
+ >>> len(list(nx.articulation_points(G)))
298
+ 4
299
+ >>> G.add_edge(2, 8)
300
+ >>> print(nx.is_biconnected(G))
301
+ True
302
+ >>> len(list(nx.articulation_points(G)))
303
+ 0
304
+
305
+ See Also
306
+ --------
307
+ is_biconnected
308
+ biconnected_components
309
+ biconnected_component_edges
310
+
311
+ Notes
312
+ -----
313
+ The algorithm to find articulation points and biconnected
314
+ components is implemented using a non-recursive depth-first-search
315
+ (DFS) that keeps track of the highest level that back edges reach
316
+ in the DFS tree. A node `n` is an articulation point if, and only
317
+ if, there exists a subtree rooted at `n` such that there is no
318
+ back edge from any successor of `n` that links to a predecessor of
319
+ `n` in the DFS tree. By keeping track of all the edges traversed
320
+ by the DFS we can obtain the biconnected components because all
321
+ edges of a bicomponent will be traversed consecutively between
322
+ articulation points.
323
+
324
+ References
325
+ ----------
326
+ .. [1] Hopcroft, J.; Tarjan, R. (1973).
327
+ "Efficient algorithms for graph manipulation".
328
+ Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
329
+
330
+ """
331
+ seen = set()
332
+ for articulation in _biconnected_dfs(G, components=False):
333
+ if articulation not in seen:
334
+ seen.add(articulation)
335
+ yield articulation
336
+
337
+
338
+ @not_implemented_for("directed")
339
+ def _biconnected_dfs(G, components=True):
340
+ # depth-first search algorithm to generate articulation points
341
+ # and biconnected components
342
+ visited = set()
343
+ for start in G:
344
+ if start in visited:
345
+ continue
346
+ discovery = {start: 0} # time of first discovery of node during search
347
+ low = {start: 0}
348
+ root_children = 0
349
+ visited.add(start)
350
+ edge_stack = []
351
+ stack = [(start, start, iter(G[start]))]
352
+ edge_index = {}
353
+ while stack:
354
+ grandparent, parent, children = stack[-1]
355
+ try:
356
+ child = next(children)
357
+ if grandparent == child:
358
+ continue
359
+ if child in visited:
360
+ if discovery[child] <= discovery[parent]: # back edge
361
+ low[parent] = min(low[parent], discovery[child])
362
+ if components:
363
+ edge_index[parent, child] = len(edge_stack)
364
+ edge_stack.append((parent, child))
365
+ else:
366
+ low[child] = discovery[child] = len(discovery)
367
+ visited.add(child)
368
+ stack.append((parent, child, iter(G[child])))
369
+ if components:
370
+ edge_index[parent, child] = len(edge_stack)
371
+ edge_stack.append((parent, child))
372
+
373
+ except StopIteration:
374
+ stack.pop()
375
+ if len(stack) > 1:
376
+ if low[parent] >= discovery[grandparent]:
377
+ if components:
378
+ ind = edge_index[grandparent, parent]
379
+ yield edge_stack[ind:]
380
+ del edge_stack[ind:]
381
+
382
+ else:
383
+ yield grandparent
384
+ low[grandparent] = min(low[parent], low[grandparent])
385
+ elif stack: # length 1 so grandparent is root
386
+ root_children += 1
387
+ if components:
388
+ ind = edge_index[grandparent, parent]
389
+ yield edge_stack[ind:]
390
+ del edge_stack[ind:]
391
+ if not components:
392
+ # root node is articulation point if it has more than 1 child
393
+ if root_children > 1:
394
+ yield start
.venv/lib/python3.11/site-packages/networkx/algorithms/components/connected.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Connected components."""
2
+
3
+ import networkx as nx
4
+ from networkx.utils.decorators import not_implemented_for
5
+
6
+ from ...utils import arbitrary_element
7
+
8
+ __all__ = [
9
+ "number_connected_components",
10
+ "connected_components",
11
+ "is_connected",
12
+ "node_connected_component",
13
+ ]
14
+
15
+
16
+ @not_implemented_for("directed")
17
+ @nx._dispatchable
18
+ def connected_components(G):
19
+ """Generate connected components.
20
+
21
+ Parameters
22
+ ----------
23
+ G : NetworkX graph
24
+ An undirected graph
25
+
26
+ Returns
27
+ -------
28
+ comp : generator of sets
29
+ A generator of sets of nodes, one for each component of G.
30
+
31
+ Raises
32
+ ------
33
+ NetworkXNotImplemented
34
+ If G is directed.
35
+
36
+ Examples
37
+ --------
38
+ Generate a sorted list of connected components, largest first.
39
+
40
+ >>> G = nx.path_graph(4)
41
+ >>> nx.add_path(G, [10, 11, 12])
42
+ >>> [len(c) for c in sorted(nx.connected_components(G), key=len, reverse=True)]
43
+ [4, 3]
44
+
45
+ If you only want the largest connected component, it's more
46
+ efficient to use max instead of sort.
47
+
48
+ >>> largest_cc = max(nx.connected_components(G), key=len)
49
+
50
+ To create the induced subgraph of each component use:
51
+
52
+ >>> S = [G.subgraph(c).copy() for c in nx.connected_components(G)]
53
+
54
+ See Also
55
+ --------
56
+ strongly_connected_components
57
+ weakly_connected_components
58
+
59
+ Notes
60
+ -----
61
+ For undirected graphs only.
62
+
63
+ """
64
+ seen = set()
65
+ n = len(G)
66
+ for v in G:
67
+ if v not in seen:
68
+ c = _plain_bfs(G, n, v)
69
+ seen.update(c)
70
+ yield c
71
+
72
+
73
+ @not_implemented_for("directed")
74
+ @nx._dispatchable
75
+ def number_connected_components(G):
76
+ """Returns the number of connected components.
77
+
78
+ Parameters
79
+ ----------
80
+ G : NetworkX graph
81
+ An undirected graph.
82
+
83
+ Returns
84
+ -------
85
+ n : integer
86
+ Number of connected components
87
+
88
+ Raises
89
+ ------
90
+ NetworkXNotImplemented
91
+ If G is directed.
92
+
93
+ Examples
94
+ --------
95
+ >>> G = nx.Graph([(0, 1), (1, 2), (5, 6), (3, 4)])
96
+ >>> nx.number_connected_components(G)
97
+ 3
98
+
99
+ See Also
100
+ --------
101
+ connected_components
102
+ number_weakly_connected_components
103
+ number_strongly_connected_components
104
+
105
+ Notes
106
+ -----
107
+ For undirected graphs only.
108
+
109
+ """
110
+ return sum(1 for cc in connected_components(G))
111
+
112
+
113
+ @not_implemented_for("directed")
114
+ @nx._dispatchable
115
+ def is_connected(G):
116
+ """Returns True if the graph is connected, False otherwise.
117
+
118
+ Parameters
119
+ ----------
120
+ G : NetworkX Graph
121
+ An undirected graph.
122
+
123
+ Returns
124
+ -------
125
+ connected : bool
126
+ True if the graph is connected, false otherwise.
127
+
128
+ Raises
129
+ ------
130
+ NetworkXNotImplemented
131
+ If G is directed.
132
+
133
+ Examples
134
+ --------
135
+ >>> G = nx.path_graph(4)
136
+ >>> print(nx.is_connected(G))
137
+ True
138
+
139
+ See Also
140
+ --------
141
+ is_strongly_connected
142
+ is_weakly_connected
143
+ is_semiconnected
144
+ is_biconnected
145
+ connected_components
146
+
147
+ Notes
148
+ -----
149
+ For undirected graphs only.
150
+
151
+ """
152
+ n = len(G)
153
+ if n == 0:
154
+ raise nx.NetworkXPointlessConcept(
155
+ "Connectivity is undefined for the null graph."
156
+ )
157
+ return sum(1 for node in _plain_bfs(G, n, arbitrary_element(G))) == len(G)
158
+
159
+
160
+ @not_implemented_for("directed")
161
+ @nx._dispatchable
162
+ def node_connected_component(G, n):
163
+ """Returns the set of nodes in the component of graph containing node n.
164
+
165
+ Parameters
166
+ ----------
167
+ G : NetworkX Graph
168
+ An undirected graph.
169
+
170
+ n : node label
171
+ A node in G
172
+
173
+ Returns
174
+ -------
175
+ comp : set
176
+ A set of nodes in the component of G containing node n.
177
+
178
+ Raises
179
+ ------
180
+ NetworkXNotImplemented
181
+ If G is directed.
182
+
183
+ Examples
184
+ --------
185
+ >>> G = nx.Graph([(0, 1), (1, 2), (5, 6), (3, 4)])
186
+ >>> nx.node_connected_component(G, 0) # nodes of component that contains node 0
187
+ {0, 1, 2}
188
+
189
+ See Also
190
+ --------
191
+ connected_components
192
+
193
+ Notes
194
+ -----
195
+ For undirected graphs only.
196
+
197
+ """
198
+ return _plain_bfs(G, len(G), n)
199
+
200
+
201
+ def _plain_bfs(G, n, source):
202
+ """A fast BFS node generator"""
203
+ adj = G._adj
204
+ seen = {source}
205
+ nextlevel = [source]
206
+ while nextlevel:
207
+ thislevel = nextlevel
208
+ nextlevel = []
209
+ for v in thislevel:
210
+ for w in adj[v]:
211
+ if w not in seen:
212
+ seen.add(w)
213
+ nextlevel.append(w)
214
+ if len(seen) == n:
215
+ return seen
216
+ return seen
.venv/lib/python3.11/site-packages/networkx/algorithms/components/semiconnected.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Semiconnectedness."""
2
+
3
+ import networkx as nx
4
+ from networkx.utils import not_implemented_for, pairwise
5
+
6
+ __all__ = ["is_semiconnected"]
7
+
8
+
9
+ @not_implemented_for("undirected")
10
+ @nx._dispatchable
11
+ def is_semiconnected(G):
12
+ r"""Returns True if the graph is semiconnected, False otherwise.
13
+
14
+ A graph is semiconnected if and only if for any pair of nodes, either one
15
+ is reachable from the other, or they are mutually reachable.
16
+
17
+ This function uses a theorem that states that a DAG is semiconnected
18
+ if for any topological sort, for node $v_n$ in that sort, there is an
19
+ edge $(v_i, v_{i+1})$. That allows us to check if a non-DAG `G` is
20
+ semiconnected by condensing the graph: i.e. constructing a new graph `H`
21
+ with nodes being the strongly connected components of `G`, and edges
22
+ (scc_1, scc_2) if there is a edge $(v_1, v_2)$ in `G` for some
23
+ $v_1 \in scc_1$ and $v_2 \in scc_2$. That results in a DAG, so we compute
24
+ the topological sort of `H` and check if for every $n$ there is an edge
25
+ $(scc_n, scc_{n+1})$.
26
+
27
+ Parameters
28
+ ----------
29
+ G : NetworkX graph
30
+ A directed graph.
31
+
32
+ Returns
33
+ -------
34
+ semiconnected : bool
35
+ True if the graph is semiconnected, False otherwise.
36
+
37
+ Raises
38
+ ------
39
+ NetworkXNotImplemented
40
+ If the input graph is undirected.
41
+
42
+ NetworkXPointlessConcept
43
+ If the graph is empty.
44
+
45
+ Examples
46
+ --------
47
+ >>> G = nx.path_graph(4, create_using=nx.DiGraph())
48
+ >>> print(nx.is_semiconnected(G))
49
+ True
50
+ >>> G = nx.DiGraph([(1, 2), (3, 2)])
51
+ >>> print(nx.is_semiconnected(G))
52
+ False
53
+
54
+ See Also
55
+ --------
56
+ is_strongly_connected
57
+ is_weakly_connected
58
+ is_connected
59
+ is_biconnected
60
+ """
61
+ if len(G) == 0:
62
+ raise nx.NetworkXPointlessConcept(
63
+ "Connectivity is undefined for the null graph."
64
+ )
65
+
66
+ if not nx.is_weakly_connected(G):
67
+ return False
68
+
69
+ H = nx.condensation(G)
70
+
71
+ return all(H.has_edge(u, v) for u, v in pairwise(nx.topological_sort(H)))
.venv/lib/python3.11/site-packages/networkx/algorithms/components/strongly_connected.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Strongly connected components."""
2
+
3
+ import networkx as nx
4
+ from networkx.utils.decorators import not_implemented_for
5
+
6
+ __all__ = [
7
+ "number_strongly_connected_components",
8
+ "strongly_connected_components",
9
+ "is_strongly_connected",
10
+ "kosaraju_strongly_connected_components",
11
+ "condensation",
12
+ ]
13
+
14
+
15
+ @not_implemented_for("undirected")
16
+ @nx._dispatchable
17
+ def strongly_connected_components(G):
18
+ """Generate nodes in strongly connected components of graph.
19
+
20
+ Parameters
21
+ ----------
22
+ G : NetworkX Graph
23
+ A directed graph.
24
+
25
+ Returns
26
+ -------
27
+ comp : generator of sets
28
+ A generator of sets of nodes, one for each strongly connected
29
+ component of G.
30
+
31
+ Raises
32
+ ------
33
+ NetworkXNotImplemented
34
+ If G is undirected.
35
+
36
+ Examples
37
+ --------
38
+ Generate a sorted list of strongly connected components, largest first.
39
+
40
+ >>> G = nx.cycle_graph(4, create_using=nx.DiGraph())
41
+ >>> nx.add_cycle(G, [10, 11, 12])
42
+ >>> [
43
+ ... len(c)
44
+ ... for c in sorted(nx.strongly_connected_components(G), key=len, reverse=True)
45
+ ... ]
46
+ [4, 3]
47
+
48
+ If you only want the largest component, it's more efficient to
49
+ use max instead of sort.
50
+
51
+ >>> largest = max(nx.strongly_connected_components(G), key=len)
52
+
53
+ See Also
54
+ --------
55
+ connected_components
56
+ weakly_connected_components
57
+ kosaraju_strongly_connected_components
58
+
59
+ Notes
60
+ -----
61
+ Uses Tarjan's algorithm[1]_ with Nuutila's modifications[2]_.
62
+ Nonrecursive version of algorithm.
63
+
64
+ References
65
+ ----------
66
+ .. [1] Depth-first search and linear graph algorithms, R. Tarjan
67
+ SIAM Journal of Computing 1(2):146-160, (1972).
68
+
69
+ .. [2] On finding the strongly connected components in a directed graph.
70
+ E. Nuutila and E. Soisalon-Soinen
71
+ Information Processing Letters 49(1): 9-14, (1994)..
72
+
73
+ """
74
+ preorder = {}
75
+ lowlink = {}
76
+ scc_found = set()
77
+ scc_queue = []
78
+ i = 0 # Preorder counter
79
+ neighbors = {v: iter(G[v]) for v in G}
80
+ for source in G:
81
+ if source not in scc_found:
82
+ queue = [source]
83
+ while queue:
84
+ v = queue[-1]
85
+ if v not in preorder:
86
+ i = i + 1
87
+ preorder[v] = i
88
+ done = True
89
+ for w in neighbors[v]:
90
+ if w not in preorder:
91
+ queue.append(w)
92
+ done = False
93
+ break
94
+ if done:
95
+ lowlink[v] = preorder[v]
96
+ for w in G[v]:
97
+ if w not in scc_found:
98
+ if preorder[w] > preorder[v]:
99
+ lowlink[v] = min([lowlink[v], lowlink[w]])
100
+ else:
101
+ lowlink[v] = min([lowlink[v], preorder[w]])
102
+ queue.pop()
103
+ if lowlink[v] == preorder[v]:
104
+ scc = {v}
105
+ while scc_queue and preorder[scc_queue[-1]] > preorder[v]:
106
+ k = scc_queue.pop()
107
+ scc.add(k)
108
+ scc_found.update(scc)
109
+ yield scc
110
+ else:
111
+ scc_queue.append(v)
112
+
113
+
114
+ @not_implemented_for("undirected")
115
+ @nx._dispatchable
116
+ def kosaraju_strongly_connected_components(G, source=None):
117
+ """Generate nodes in strongly connected components of graph.
118
+
119
+ Parameters
120
+ ----------
121
+ G : NetworkX Graph
122
+ A directed graph.
123
+
124
+ Returns
125
+ -------
126
+ comp : generator of sets
127
+ A generator of sets of nodes, one for each strongly connected
128
+ component of G.
129
+
130
+ Raises
131
+ ------
132
+ NetworkXNotImplemented
133
+ If G is undirected.
134
+
135
+ Examples
136
+ --------
137
+ Generate a sorted list of strongly connected components, largest first.
138
+
139
+ >>> G = nx.cycle_graph(4, create_using=nx.DiGraph())
140
+ >>> nx.add_cycle(G, [10, 11, 12])
141
+ >>> [
142
+ ... len(c)
143
+ ... for c in sorted(
144
+ ... nx.kosaraju_strongly_connected_components(G), key=len, reverse=True
145
+ ... )
146
+ ... ]
147
+ [4, 3]
148
+
149
+ If you only want the largest component, it's more efficient to
150
+ use max instead of sort.
151
+
152
+ >>> largest = max(nx.kosaraju_strongly_connected_components(G), key=len)
153
+
154
+ See Also
155
+ --------
156
+ strongly_connected_components
157
+
158
+ Notes
159
+ -----
160
+ Uses Kosaraju's algorithm.
161
+
162
+ """
163
+ post = list(nx.dfs_postorder_nodes(G.reverse(copy=False), source=source))
164
+
165
+ seen = set()
166
+ while post:
167
+ r = post.pop()
168
+ if r in seen:
169
+ continue
170
+ c = nx.dfs_preorder_nodes(G, r)
171
+ new = {v for v in c if v not in seen}
172
+ seen.update(new)
173
+ yield new
174
+
175
+
176
+ @not_implemented_for("undirected")
177
+ @nx._dispatchable
178
+ def number_strongly_connected_components(G):
179
+ """Returns number of strongly connected components in graph.
180
+
181
+ Parameters
182
+ ----------
183
+ G : NetworkX graph
184
+ A directed graph.
185
+
186
+ Returns
187
+ -------
188
+ n : integer
189
+ Number of strongly connected components
190
+
191
+ Raises
192
+ ------
193
+ NetworkXNotImplemented
194
+ If G is undirected.
195
+
196
+ Examples
197
+ --------
198
+ >>> G = nx.DiGraph(
199
+ ... [(0, 1), (1, 2), (2, 0), (2, 3), (4, 5), (3, 4), (5, 6), (6, 3), (6, 7)]
200
+ ... )
201
+ >>> nx.number_strongly_connected_components(G)
202
+ 3
203
+
204
+ See Also
205
+ --------
206
+ strongly_connected_components
207
+ number_connected_components
208
+ number_weakly_connected_components
209
+
210
+ Notes
211
+ -----
212
+ For directed graphs only.
213
+ """
214
+ return sum(1 for scc in strongly_connected_components(G))
215
+
216
+
217
+ @not_implemented_for("undirected")
218
+ @nx._dispatchable
219
+ def is_strongly_connected(G):
220
+ """Test directed graph for strong connectivity.
221
+
222
+ A directed graph is strongly connected if and only if every vertex in
223
+ the graph is reachable from every other vertex.
224
+
225
+ Parameters
226
+ ----------
227
+ G : NetworkX Graph
228
+ A directed graph.
229
+
230
+ Returns
231
+ -------
232
+ connected : bool
233
+ True if the graph is strongly connected, False otherwise.
234
+
235
+ Examples
236
+ --------
237
+ >>> G = nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4), (4, 2)])
238
+ >>> nx.is_strongly_connected(G)
239
+ True
240
+ >>> G.remove_edge(2, 3)
241
+ >>> nx.is_strongly_connected(G)
242
+ False
243
+
244
+ Raises
245
+ ------
246
+ NetworkXNotImplemented
247
+ If G is undirected.
248
+
249
+ See Also
250
+ --------
251
+ is_weakly_connected
252
+ is_semiconnected
253
+ is_connected
254
+ is_biconnected
255
+ strongly_connected_components
256
+
257
+ Notes
258
+ -----
259
+ For directed graphs only.
260
+ """
261
+ if len(G) == 0:
262
+ raise nx.NetworkXPointlessConcept(
263
+ """Connectivity is undefined for the null graph."""
264
+ )
265
+
266
+ return len(next(strongly_connected_components(G))) == len(G)
267
+
268
+
269
+ @not_implemented_for("undirected")
270
+ @nx._dispatchable(returns_graph=True)
271
+ def condensation(G, scc=None):
272
+ """Returns the condensation of G.
273
+
274
+ The condensation of G is the graph with each of the strongly connected
275
+ components contracted into a single node.
276
+
277
+ Parameters
278
+ ----------
279
+ G : NetworkX DiGraph
280
+ A directed graph.
281
+
282
+ scc: list or generator (optional, default=None)
283
+ Strongly connected components. If provided, the elements in
284
+ `scc` must partition the nodes in `G`. If not provided, it will be
285
+ calculated as scc=nx.strongly_connected_components(G).
286
+
287
+ Returns
288
+ -------
289
+ C : NetworkX DiGraph
290
+ The condensation graph C of G. The node labels are integers
291
+ corresponding to the index of the component in the list of
292
+ strongly connected components of G. C has a graph attribute named
293
+ 'mapping' with a dictionary mapping the original nodes to the
294
+ nodes in C to which they belong. Each node in C also has a node
295
+ attribute 'members' with the set of original nodes in G that
296
+ form the SCC that the node in C represents.
297
+
298
+ Raises
299
+ ------
300
+ NetworkXNotImplemented
301
+ If G is undirected.
302
+
303
+ Examples
304
+ --------
305
+ Contracting two sets of strongly connected nodes into two distinct SCC
306
+ using the barbell graph.
307
+
308
+ >>> G = nx.barbell_graph(4, 0)
309
+ >>> G.remove_edge(3, 4)
310
+ >>> G = nx.DiGraph(G)
311
+ >>> H = nx.condensation(G)
312
+ >>> H.nodes.data()
313
+ NodeDataView({0: {'members': {0, 1, 2, 3}}, 1: {'members': {4, 5, 6, 7}}})
314
+ >>> H.graph["mapping"]
315
+ {0: 0, 1: 0, 2: 0, 3: 0, 4: 1, 5: 1, 6: 1, 7: 1}
316
+
317
+ Contracting a complete graph into one single SCC.
318
+
319
+ >>> G = nx.complete_graph(7, create_using=nx.DiGraph)
320
+ >>> H = nx.condensation(G)
321
+ >>> H.nodes
322
+ NodeView((0,))
323
+ >>> H.nodes.data()
324
+ NodeDataView({0: {'members': {0, 1, 2, 3, 4, 5, 6}}})
325
+
326
+ Notes
327
+ -----
328
+ After contracting all strongly connected components to a single node,
329
+ the resulting graph is a directed acyclic graph.
330
+
331
+ """
332
+ if scc is None:
333
+ scc = nx.strongly_connected_components(G)
334
+ mapping = {}
335
+ members = {}
336
+ C = nx.DiGraph()
337
+ # Add mapping dict as graph attribute
338
+ C.graph["mapping"] = mapping
339
+ if len(G) == 0:
340
+ return C
341
+ for i, component in enumerate(scc):
342
+ members[i] = component
343
+ mapping.update((n, i) for n in component)
344
+ number_of_components = i + 1
345
+ C.add_nodes_from(range(number_of_components))
346
+ C.add_edges_from(
347
+ (mapping[u], mapping[v]) for u, v in G.edges() if mapping[u] != mapping[v]
348
+ )
349
+ # Add a list of members (ie original nodes) to each node (ie scc) in C.
350
+ nx.set_node_attributes(C, members, "members")
351
+ return C
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (209 Bytes). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_attracting.cpython-311.pyc ADDED
Binary file (5.23 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_biconnected.cpython-311.pyc ADDED
Binary file (12.6 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_connected.cpython-311.pyc ADDED
Binary file (9.58 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_semiconnected.cpython-311.pyc ADDED
Binary file (5.5 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_strongly_connected.cpython-311.pyc ADDED
Binary file (13.2 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/__pycache__/test_weakly_connected.cpython-311.pyc ADDED
Binary file (7.49 kB). View file
 
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_attracting.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx import NetworkXNotImplemented
5
+
6
+
7
+ class TestAttractingComponents:
8
+ @classmethod
9
+ def setup_class(cls):
10
+ cls.G1 = nx.DiGraph()
11
+ cls.G1.add_edges_from(
12
+ [
13
+ (5, 11),
14
+ (11, 2),
15
+ (11, 9),
16
+ (11, 10),
17
+ (7, 11),
18
+ (7, 8),
19
+ (8, 9),
20
+ (3, 8),
21
+ (3, 10),
22
+ ]
23
+ )
24
+ cls.G2 = nx.DiGraph()
25
+ cls.G2.add_edges_from([(0, 1), (0, 2), (1, 1), (1, 2), (2, 1)])
26
+
27
+ cls.G3 = nx.DiGraph()
28
+ cls.G3.add_edges_from([(0, 1), (1, 2), (2, 1), (0, 3), (3, 4), (4, 3)])
29
+
30
+ cls.G4 = nx.DiGraph()
31
+
32
+ def test_attracting_components(self):
33
+ ac = list(nx.attracting_components(self.G1))
34
+ assert {2} in ac
35
+ assert {9} in ac
36
+ assert {10} in ac
37
+
38
+ ac = list(nx.attracting_components(self.G2))
39
+ ac = [tuple(sorted(x)) for x in ac]
40
+ assert ac == [(1, 2)]
41
+
42
+ ac = list(nx.attracting_components(self.G3))
43
+ ac = [tuple(sorted(x)) for x in ac]
44
+ assert (1, 2) in ac
45
+ assert (3, 4) in ac
46
+ assert len(ac) == 2
47
+
48
+ ac = list(nx.attracting_components(self.G4))
49
+ assert ac == []
50
+
51
+ def test_number_attacting_components(self):
52
+ assert nx.number_attracting_components(self.G1) == 3
53
+ assert nx.number_attracting_components(self.G2) == 1
54
+ assert nx.number_attracting_components(self.G3) == 2
55
+ assert nx.number_attracting_components(self.G4) == 0
56
+
57
+ def test_is_attracting_component(self):
58
+ assert not nx.is_attracting_component(self.G1)
59
+ assert not nx.is_attracting_component(self.G2)
60
+ assert not nx.is_attracting_component(self.G3)
61
+ g2 = self.G3.subgraph([1, 2])
62
+ assert nx.is_attracting_component(g2)
63
+ assert not nx.is_attracting_component(self.G4)
64
+
65
+ def test_connected_raise(self):
66
+ G = nx.Graph()
67
+ with pytest.raises(NetworkXNotImplemented):
68
+ next(nx.attracting_components(G))
69
+ pytest.raises(NetworkXNotImplemented, nx.number_attracting_components, G)
70
+ pytest.raises(NetworkXNotImplemented, nx.is_attracting_component, G)
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_biconnected.py ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx import NetworkXNotImplemented
5
+
6
+
7
+ def assert_components_edges_equal(x, y):
8
+ sx = {frozenset(frozenset(e) for e in c) for c in x}
9
+ sy = {frozenset(frozenset(e) for e in c) for c in y}
10
+ assert sx == sy
11
+
12
+
13
+ def assert_components_equal(x, y):
14
+ sx = {frozenset(c) for c in x}
15
+ sy = {frozenset(c) for c in y}
16
+ assert sx == sy
17
+
18
+
19
+ def test_barbell():
20
+ G = nx.barbell_graph(8, 4)
21
+ nx.add_path(G, [7, 20, 21, 22])
22
+ nx.add_cycle(G, [22, 23, 24, 25])
23
+ pts = set(nx.articulation_points(G))
24
+ assert pts == {7, 8, 9, 10, 11, 12, 20, 21, 22}
25
+
26
+ answer = [
27
+ {12, 13, 14, 15, 16, 17, 18, 19},
28
+ {0, 1, 2, 3, 4, 5, 6, 7},
29
+ {22, 23, 24, 25},
30
+ {11, 12},
31
+ {10, 11},
32
+ {9, 10},
33
+ {8, 9},
34
+ {7, 8},
35
+ {21, 22},
36
+ {20, 21},
37
+ {7, 20},
38
+ ]
39
+ assert_components_equal(list(nx.biconnected_components(G)), answer)
40
+
41
+ G.add_edge(2, 17)
42
+ pts = set(nx.articulation_points(G))
43
+ assert pts == {7, 20, 21, 22}
44
+
45
+
46
+ def test_articulation_points_repetitions():
47
+ G = nx.Graph()
48
+ G.add_edges_from([(0, 1), (1, 2), (1, 3)])
49
+ assert list(nx.articulation_points(G)) == [1]
50
+
51
+
52
+ def test_articulation_points_cycle():
53
+ G = nx.cycle_graph(3)
54
+ nx.add_cycle(G, [1, 3, 4])
55
+ pts = set(nx.articulation_points(G))
56
+ assert pts == {1}
57
+
58
+
59
+ def test_is_biconnected():
60
+ G = nx.cycle_graph(3)
61
+ assert nx.is_biconnected(G)
62
+ nx.add_cycle(G, [1, 3, 4])
63
+ assert not nx.is_biconnected(G)
64
+
65
+
66
+ def test_empty_is_biconnected():
67
+ G = nx.empty_graph(5)
68
+ assert not nx.is_biconnected(G)
69
+ G.add_edge(0, 1)
70
+ assert not nx.is_biconnected(G)
71
+
72
+
73
+ def test_biconnected_components_cycle():
74
+ G = nx.cycle_graph(3)
75
+ nx.add_cycle(G, [1, 3, 4])
76
+ answer = [{0, 1, 2}, {1, 3, 4}]
77
+ assert_components_equal(list(nx.biconnected_components(G)), answer)
78
+
79
+
80
+ def test_biconnected_components1():
81
+ # graph example from
82
+ # https://web.archive.org/web/20121229123447/http://www.ibluemojo.com/school/articul_algorithm.html
83
+ edges = [
84
+ (0, 1),
85
+ (0, 5),
86
+ (0, 6),
87
+ (0, 14),
88
+ (1, 5),
89
+ (1, 6),
90
+ (1, 14),
91
+ (2, 4),
92
+ (2, 10),
93
+ (3, 4),
94
+ (3, 15),
95
+ (4, 6),
96
+ (4, 7),
97
+ (4, 10),
98
+ (5, 14),
99
+ (6, 14),
100
+ (7, 9),
101
+ (8, 9),
102
+ (8, 12),
103
+ (8, 13),
104
+ (10, 15),
105
+ (11, 12),
106
+ (11, 13),
107
+ (12, 13),
108
+ ]
109
+ G = nx.Graph(edges)
110
+ pts = set(nx.articulation_points(G))
111
+ assert pts == {4, 6, 7, 8, 9}
112
+ comps = list(nx.biconnected_component_edges(G))
113
+ answer = [
114
+ [(3, 4), (15, 3), (10, 15), (10, 4), (2, 10), (4, 2)],
115
+ [(13, 12), (13, 8), (11, 13), (12, 11), (8, 12)],
116
+ [(9, 8)],
117
+ [(7, 9)],
118
+ [(4, 7)],
119
+ [(6, 4)],
120
+ [(14, 0), (5, 1), (5, 0), (14, 5), (14, 1), (6, 14), (6, 0), (1, 6), (0, 1)],
121
+ ]
122
+ assert_components_edges_equal(comps, answer)
123
+
124
+
125
+ def test_biconnected_components2():
126
+ G = nx.Graph()
127
+ nx.add_cycle(G, "ABC")
128
+ nx.add_cycle(G, "CDE")
129
+ nx.add_cycle(G, "FIJHG")
130
+ nx.add_cycle(G, "GIJ")
131
+ G.add_edge("E", "G")
132
+ comps = list(nx.biconnected_component_edges(G))
133
+ answer = [
134
+ [
135
+ tuple("GF"),
136
+ tuple("FI"),
137
+ tuple("IG"),
138
+ tuple("IJ"),
139
+ tuple("JG"),
140
+ tuple("JH"),
141
+ tuple("HG"),
142
+ ],
143
+ [tuple("EG")],
144
+ [tuple("CD"), tuple("DE"), tuple("CE")],
145
+ [tuple("AB"), tuple("BC"), tuple("AC")],
146
+ ]
147
+ assert_components_edges_equal(comps, answer)
148
+
149
+
150
+ def test_biconnected_davis():
151
+ D = nx.davis_southern_women_graph()
152
+ bcc = list(nx.biconnected_components(D))[0]
153
+ assert set(D) == bcc # All nodes in a giant bicomponent
154
+ # So no articulation points
155
+ assert len(list(nx.articulation_points(D))) == 0
156
+
157
+
158
+ def test_biconnected_karate():
159
+ K = nx.karate_club_graph()
160
+ answer = [
161
+ {
162
+ 0,
163
+ 1,
164
+ 2,
165
+ 3,
166
+ 7,
167
+ 8,
168
+ 9,
169
+ 12,
170
+ 13,
171
+ 14,
172
+ 15,
173
+ 17,
174
+ 18,
175
+ 19,
176
+ 20,
177
+ 21,
178
+ 22,
179
+ 23,
180
+ 24,
181
+ 25,
182
+ 26,
183
+ 27,
184
+ 28,
185
+ 29,
186
+ 30,
187
+ 31,
188
+ 32,
189
+ 33,
190
+ },
191
+ {0, 4, 5, 6, 10, 16},
192
+ {0, 11},
193
+ ]
194
+ bcc = list(nx.biconnected_components(K))
195
+ assert_components_equal(bcc, answer)
196
+ assert set(nx.articulation_points(K)) == {0}
197
+
198
+
199
+ def test_biconnected_eppstein():
200
+ # tests from http://www.ics.uci.edu/~eppstein/PADS/Biconnectivity.py
201
+ G1 = nx.Graph(
202
+ {
203
+ 0: [1, 2, 5],
204
+ 1: [0, 5],
205
+ 2: [0, 3, 4],
206
+ 3: [2, 4, 5, 6],
207
+ 4: [2, 3, 5, 6],
208
+ 5: [0, 1, 3, 4],
209
+ 6: [3, 4],
210
+ }
211
+ )
212
+ G2 = nx.Graph(
213
+ {
214
+ 0: [2, 5],
215
+ 1: [3, 8],
216
+ 2: [0, 3, 5],
217
+ 3: [1, 2, 6, 8],
218
+ 4: [7],
219
+ 5: [0, 2],
220
+ 6: [3, 8],
221
+ 7: [4],
222
+ 8: [1, 3, 6],
223
+ }
224
+ )
225
+ assert nx.is_biconnected(G1)
226
+ assert not nx.is_biconnected(G2)
227
+ answer_G2 = [{1, 3, 6, 8}, {0, 2, 5}, {2, 3}, {4, 7}]
228
+ bcc = list(nx.biconnected_components(G2))
229
+ assert_components_equal(bcc, answer_G2)
230
+
231
+
232
+ def test_null_graph():
233
+ G = nx.Graph()
234
+ assert not nx.is_biconnected(G)
235
+ assert list(nx.biconnected_components(G)) == []
236
+ assert list(nx.biconnected_component_edges(G)) == []
237
+ assert list(nx.articulation_points(G)) == []
238
+
239
+
240
+ def test_connected_raise():
241
+ DG = nx.DiGraph()
242
+ with pytest.raises(NetworkXNotImplemented):
243
+ next(nx.biconnected_components(DG))
244
+ with pytest.raises(NetworkXNotImplemented):
245
+ next(nx.biconnected_component_edges(DG))
246
+ with pytest.raises(NetworkXNotImplemented):
247
+ next(nx.articulation_points(DG))
248
+ pytest.raises(NetworkXNotImplemented, nx.is_biconnected, DG)
.venv/lib/python3.11/site-packages/networkx/algorithms/components/tests/test_connected.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx import NetworkXNotImplemented
5
+ from networkx import convert_node_labels_to_integers as cnlti
6
+ from networkx.classes.tests import dispatch_interface
7
+
8
+
9
+ class TestConnected:
10
+ @classmethod
11
+ def setup_class(cls):
12
+ G1 = cnlti(nx.grid_2d_graph(2, 2), first_label=0, ordering="sorted")
13
+ G2 = cnlti(nx.lollipop_graph(3, 3), first_label=4, ordering="sorted")
14
+ G3 = cnlti(nx.house_graph(), first_label=10, ordering="sorted")
15
+ cls.G = nx.union(G1, G2)
16
+ cls.G = nx.union(cls.G, G3)
17
+ cls.DG = nx.DiGraph([(1, 2), (1, 3), (2, 3)])
18
+ cls.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1)
19
+
20
+ cls.gc = []
21
+ G = nx.DiGraph()
22
+ G.add_edges_from(
23
+ [
24
+ (1, 2),
25
+ (2, 3),
26
+ (2, 8),
27
+ (3, 4),
28
+ (3, 7),
29
+ (4, 5),
30
+ (5, 3),
31
+ (5, 6),
32
+ (7, 4),
33
+ (7, 6),
34
+ (8, 1),
35
+ (8, 7),
36
+ ]
37
+ )
38
+ C = [[3, 4, 5, 7], [1, 2, 8], [6]]
39
+ cls.gc.append((G, C))
40
+
41
+ G = nx.DiGraph()
42
+ G.add_edges_from([(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)])
43
+ C = [[2, 3, 4], [1]]
44
+ cls.gc.append((G, C))
45
+
46
+ G = nx.DiGraph()
47
+ G.add_edges_from([(1, 2), (2, 3), (3, 2), (2, 1)])
48
+ C = [[1, 2, 3]]
49
+ cls.gc.append((G, C))
50
+
51
+ # Eppstein's tests
52
+ G = nx.DiGraph({0: [1], 1: [2, 3], 2: [4, 5], 3: [4, 5], 4: [6], 5: [], 6: []})
53
+ C = [[0], [1], [2], [3], [4], [5], [6]]
54
+ cls.gc.append((G, C))
55
+
56
+ G = nx.DiGraph({0: [1], 1: [2, 3, 4], 2: [0, 3], 3: [4], 4: [3]})
57
+ C = [[0, 1, 2], [3, 4]]
58
+ cls.gc.append((G, C))
59
+
60
+ G = nx.DiGraph()
61
+ C = []
62
+ cls.gc.append((G, C))
63
+
64
+ def test_connected_components(self):
65
+ # Test duplicated below
66
+ cc = nx.connected_components
67
+ G = self.G
68
+ C = {
69
+ frozenset([0, 1, 2, 3]),
70
+ frozenset([4, 5, 6, 7, 8, 9]),
71
+ frozenset([10, 11, 12, 13, 14]),
72
+ }
73
+ assert {frozenset(g) for g in cc(G)} == C
74
+
75
+ def test_connected_components_nx_loopback(self):
76
+ # This tests the @nx._dispatchable mechanism, treating nx.connected_components
77
+ # as if it were a re-implementation from another package.
78
+ # Test duplicated from above
79
+ cc = nx.connected_components
80
+ G = dispatch_interface.convert(self.G)
81
+ C = {
82
+ frozenset([0, 1, 2, 3]),
83
+ frozenset([4, 5, 6, 7, 8, 9]),
84
+ frozenset([10, 11, 12, 13, 14]),
85
+ }
86
+ if "nx_loopback" in nx.config.backends or not nx.config.backends:
87
+ # If `nx.config.backends` is empty, then `_dispatchable.__call__` takes a
88
+ # "fast path" and does not check graph inputs, so using an unknown backend
89
+ # here will still work.
90
+ assert {frozenset(g) for g in cc(G)} == C
91
+ else:
92
+ # This raises, because "nx_loopback" is not registered as a backend.
93
+ with pytest.raises(
94
+ ImportError, match="'nx_loopback' backend is not installed"
95
+ ):
96
+ cc(G)
97
+
98
+ def test_number_connected_components(self):
99
+ ncc = nx.number_connected_components
100
+ assert ncc(self.G) == 3
101
+
102
+ def test_number_connected_components2(self):
103
+ ncc = nx.number_connected_components
104
+ assert ncc(self.grid) == 1
105
+
106
+ def test_connected_components2(self):
107
+ cc = nx.connected_components
108
+ G = self.grid
109
+ C = {frozenset([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])}
110
+ assert {frozenset(g) for g in cc(G)} == C
111
+
112
+ def test_node_connected_components(self):
113
+ ncc = nx.node_connected_component
114
+ G = self.grid
115
+ C = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
116
+ assert ncc(G, 1) == C
117
+
118
+ def test_is_connected(self):
119
+ assert nx.is_connected(self.grid)
120
+ G = nx.Graph()
121
+ G.add_nodes_from([1, 2])
122
+ assert not nx.is_connected(G)
123
+
124
+ def test_connected_raise(self):
125
+ with pytest.raises(NetworkXNotImplemented):
126
+ next(nx.connected_components(self.DG))
127
+ pytest.raises(NetworkXNotImplemented, nx.number_connected_components, self.DG)
128
+ pytest.raises(NetworkXNotImplemented, nx.node_connected_component, self.DG, 1)
129
+ pytest.raises(NetworkXNotImplemented, nx.is_connected, self.DG)
130
+ pytest.raises(nx.NetworkXPointlessConcept, nx.is_connected, nx.Graph())
131
+
132
+ def test_connected_mutability(self):
133
+ G = self.grid
134
+ seen = set()
135
+ for component in nx.connected_components(G):
136
+ assert len(seen & component) == 0
137
+ seen.update(component)
138
+ component.clear()