ZhengyangZhang commited on
Commit
43b60dd
·
verified ·
1 Parent(s): 085d76f

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. lib/python3.12/site-packages/networkx/algorithms/__pycache__/bridges.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/networkx/algorithms/__pycache__/broadcasting.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/networkx/algorithms/__pycache__/chains.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/networkx/algorithms/__pycache__/chordal.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/networkx/algorithms/__pycache__/clique.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/networkx/algorithms/__pycache__/cluster.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/networkx/algorithms/__pycache__/communicability_alg.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/networkx/algorithms/__pycache__/covering.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/networkx/algorithms/__pycache__/d_separation.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/networkx/algorithms/__pycache__/efficiency_measures.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/networkx/algorithms/__pycache__/hybrid.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/networkx/algorithms/__pycache__/isolate.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/networkx/algorithms/__pycache__/link_prediction.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/networkx/algorithms/__pycache__/moral.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/networkx/algorithms/__pycache__/node_classification.cpython-312.pyc +0 -0
  16. lib/python3.12/site-packages/networkx/algorithms/__pycache__/non_randomness.cpython-312.pyc +0 -0
  17. lib/python3.12/site-packages/networkx/algorithms/__pycache__/perfect_graph.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/networkx/algorithms/__pycache__/planar_drawing.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/networkx/algorithms/__pycache__/polynomials.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/networkx/algorithms/__pycache__/regular.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/networkx/algorithms/__pycache__/richclub.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/networkx/algorithms/__pycache__/similarity.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/networkx/algorithms/__pycache__/smetric.cpython-312.pyc +0 -0
  24. lib/python3.12/site-packages/networkx/algorithms/__pycache__/structuralholes.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/networkx/algorithms/__pycache__/triads.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/networkx/algorithms/__pycache__/voronoi.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/networkx/algorithms/__pycache__/walks.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/networkx/algorithms/__pycache__/wiener.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/networkx/algorithms/bipartite/__init__.py +88 -0
  30. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/__init__.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/basic.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/centrality.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/cluster.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/covering.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/edgelist.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/extendability.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/generators.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/link_analysis.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/matching.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/matrix.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/projection.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/redundancy.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/spectral.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/networkx/algorithms/bipartite/basic.py +322 -0
  45. lib/python3.12/site-packages/networkx/algorithms/bipartite/centrality.py +290 -0
  46. lib/python3.12/site-packages/networkx/algorithms/bipartite/cluster.py +289 -0
  47. lib/python3.12/site-packages/networkx/algorithms/bipartite/covering.py +57 -0
  48. lib/python3.12/site-packages/networkx/algorithms/bipartite/edgelist.py +360 -0
  49. lib/python3.12/site-packages/networkx/algorithms/bipartite/extendability.py +105 -0
  50. lib/python3.12/site-packages/networkx/algorithms/bipartite/generators.py +603 -0
lib/python3.12/site-packages/networkx/algorithms/__pycache__/bridges.cpython-312.pyc ADDED
Binary file (7.47 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/broadcasting.cpython-312.pyc ADDED
Binary file (7.95 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/chains.cpython-312.pyc ADDED
Binary file (6.14 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/chordal.cpython-312.pyc ADDED
Binary file (16.1 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/clique.cpython-312.pyc ADDED
Binary file (33.4 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/cluster.cpython-312.pyc ADDED
Binary file (30.3 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/communicability_alg.cpython-312.pyc ADDED
Binary file (5.55 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/covering.cpython-312.pyc ADDED
Binary file (5.62 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/d_separation.cpython-312.pyc ADDED
Binary file (27.7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/efficiency_measures.cpython-312.pyc ADDED
Binary file (5.58 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/hybrid.cpython-312.pyc ADDED
Binary file (5.86 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/isolate.cpython-312.pyc ADDED
Binary file (3.27 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/link_prediction.cpython-312.pyc ADDED
Binary file (26.6 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/moral.cpython-312.pyc ADDED
Binary file (2.08 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/node_classification.cpython-312.pyc ADDED
Binary file (8.25 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/non_randomness.cpython-312.pyc ADDED
Binary file (7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/perfect_graph.cpython-312.pyc ADDED
Binary file (3.38 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/planar_drawing.cpython-312.pyc ADDED
Binary file (14.7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/polynomials.cpython-312.pyc ADDED
Binary file (12.6 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/regular.cpython-312.pyc ADDED
Binary file (7.84 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/richclub.cpython-312.pyc ADDED
Binary file (5.88 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/similarity.cpython-312.pyc ADDED
Binary file (78 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/smetric.cpython-312.pyc ADDED
Binary file (1.44 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/structuralholes.cpython-312.pyc ADDED
Binary file (14.7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/triads.cpython-312.pyc ADDED
Binary file (16 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/voronoi.cpython-312.pyc ADDED
Binary file (3.3 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/walks.cpython-312.pyc ADDED
Binary file (3.07 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/__pycache__/wiener.cpython-312.pyc ADDED
Binary file (11.7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__init__.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ r"""This module provides functions and operations for bipartite
2
+ graphs. Bipartite graphs `B = (U, V, E)` have two node sets `U,V` and edges in
3
+ `E` that only connect nodes from opposite sets. It is common in the literature
4
+ to use an spatial analogy referring to the two node sets as top and bottom nodes.
5
+
6
+ The bipartite algorithms are not imported into the networkx namespace
7
+ at the top level so the easiest way to use them is with:
8
+
9
+ >>> from networkx.algorithms import bipartite
10
+
11
+ NetworkX does not have a custom bipartite graph class but the Graph()
12
+ or DiGraph() classes can be used to represent bipartite graphs. However,
13
+ you have to keep track of which set each node belongs to, and make
14
+ sure that there is no edge between nodes of the same set. The convention used
15
+ in NetworkX is to use a node attribute named `bipartite` with values 0 or 1 to
16
+ identify the sets each node belongs to. This convention is not enforced in
17
+ the source code of bipartite functions, it's only a recommendation.
18
+
19
+ For example:
20
+
21
+ >>> B = nx.Graph()
22
+ >>> # Add nodes with the node attribute "bipartite"
23
+ >>> B.add_nodes_from([1, 2, 3, 4], bipartite=0)
24
+ >>> B.add_nodes_from(["a", "b", "c"], bipartite=1)
25
+ >>> # Add edges only between nodes of opposite node sets
26
+ >>> B.add_edges_from([(1, "a"), (1, "b"), (2, "b"), (2, "c"), (3, "c"), (4, "a")])
27
+
28
+ Many algorithms of the bipartite module of NetworkX require, as an argument, a
29
+ container with all the nodes that belong to one set, in addition to the bipartite
30
+ graph `B`. The functions in the bipartite package do not check that the node set
31
+ is actually correct nor that the input graph is actually bipartite.
32
+ If `B` is connected, you can find the two node sets using a two-coloring
33
+ algorithm:
34
+
35
+ >>> nx.is_connected(B)
36
+ True
37
+ >>> bottom_nodes, top_nodes = bipartite.sets(B)
38
+
39
+ However, if the input graph is not connected, there are more than one possible
40
+ colorations. This is the reason why we require the user to pass a container
41
+ with all nodes of one bipartite node set as an argument to most bipartite
42
+ functions. In the face of ambiguity, we refuse the temptation to guess and
43
+ raise an :exc:`AmbiguousSolution <networkx.AmbiguousSolution>`
44
+ Exception if the input graph for
45
+ :func:`bipartite.sets <networkx.algorithms.bipartite.basic.sets>`
46
+ is disconnected.
47
+
48
+ Using the `bipartite` node attribute, you can easily get the two node sets:
49
+
50
+ >>> top_nodes = {n for n, d in B.nodes(data=True) if d["bipartite"] == 0}
51
+ >>> bottom_nodes = set(B) - top_nodes
52
+
53
+ So you can easily use the bipartite algorithms that require, as an argument, a
54
+ container with all nodes that belong to one node set:
55
+
56
+ >>> print(round(bipartite.density(B, bottom_nodes), 2))
57
+ 0.5
58
+ >>> G = bipartite.projected_graph(B, top_nodes)
59
+
60
+ All bipartite graph generators in NetworkX build bipartite graphs with the
61
+ `bipartite` node attribute. Thus, you can use the same approach:
62
+
63
+ >>> RB = bipartite.random_graph(5, 7, 0.2)
64
+ >>> RB_top = {n for n, d in RB.nodes(data=True) if d["bipartite"] == 0}
65
+ >>> RB_bottom = set(RB) - RB_top
66
+ >>> list(RB_top)
67
+ [0, 1, 2, 3, 4]
68
+ >>> list(RB_bottom)
69
+ [5, 6, 7, 8, 9, 10, 11]
70
+
71
+ For other bipartite graph generators see
72
+ :mod:`Generators <networkx.algorithms.bipartite.generators>`.
73
+
74
+ """
75
+
76
+ from networkx.algorithms.bipartite.basic import *
77
+ from networkx.algorithms.bipartite.centrality import *
78
+ from networkx.algorithms.bipartite.cluster import *
79
+ from networkx.algorithms.bipartite.covering import *
80
+ from networkx.algorithms.bipartite.edgelist import *
81
+ from networkx.algorithms.bipartite.matching import *
82
+ from networkx.algorithms.bipartite.matrix import *
83
+ from networkx.algorithms.bipartite.projection import *
84
+ from networkx.algorithms.bipartite.redundancy import *
85
+ from networkx.algorithms.bipartite.spectral import *
86
+ from networkx.algorithms.bipartite.generators import *
87
+ from networkx.algorithms.bipartite.extendability import *
88
+ from networkx.algorithms.bipartite.link_analysis import *
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (4.12 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/basic.cpython-312.pyc ADDED
Binary file (10.5 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/centrality.cpython-312.pyc ADDED
Binary file (10.7 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/cluster.cpython-312.pyc ADDED
Binary file (9.4 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/covering.cpython-312.pyc ADDED
Binary file (2.52 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/edgelist.cpython-312.pyc ADDED
Binary file (12.9 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/extendability.cpython-312.pyc ADDED
Binary file (5.03 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/generators.cpython-312.pyc ADDED
Binary file (25 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/link_analysis.cpython-312.pyc ADDED
Binary file (13.9 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/matching.cpython-312.pyc ADDED
Binary file (18.9 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/matrix.cpython-312.pyc ADDED
Binary file (10 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/projection.cpython-312.pyc ADDED
Binary file (21.8 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/redundancy.cpython-312.pyc ADDED
Binary file (4.38 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/__pycache__/spectral.cpython-312.pyc ADDED
Binary file (2.6 kB). View file
 
lib/python3.12/site-packages/networkx/algorithms/bipartite/basic.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ==========================
3
+ Bipartite Graph Algorithms
4
+ ==========================
5
+ """
6
+
7
+ import networkx as nx
8
+ from networkx.algorithms.components import connected_components
9
+ from networkx.exception import AmbiguousSolution
10
+
11
+ __all__ = [
12
+ "is_bipartite",
13
+ "is_bipartite_node_set",
14
+ "color",
15
+ "sets",
16
+ "density",
17
+ "degrees",
18
+ ]
19
+
20
+
21
+ @nx._dispatchable
22
+ def color(G):
23
+ """Returns a two-coloring of the graph.
24
+
25
+ Raises an exception if the graph is not bipartite.
26
+
27
+ Parameters
28
+ ----------
29
+ G : NetworkX graph
30
+
31
+ Returns
32
+ -------
33
+ color : dictionary
34
+ A dictionary keyed by node with a 1 or 0 as data for each node color.
35
+
36
+ Raises
37
+ ------
38
+ NetworkXError
39
+ If the graph is not two-colorable.
40
+
41
+ Examples
42
+ --------
43
+ >>> from networkx.algorithms import bipartite
44
+ >>> G = nx.path_graph(4)
45
+ >>> c = bipartite.color(G)
46
+ >>> print(c)
47
+ {0: 1, 1: 0, 2: 1, 3: 0}
48
+
49
+ You can use this to set a node attribute indicating the bipartite set:
50
+
51
+ >>> nx.set_node_attributes(G, c, "bipartite")
52
+ >>> print(G.nodes[0]["bipartite"])
53
+ 1
54
+ >>> print(G.nodes[1]["bipartite"])
55
+ 0
56
+ """
57
+ if G.is_directed():
58
+ import itertools
59
+
60
+ def neighbors(v):
61
+ return itertools.chain.from_iterable([G.predecessors(v), G.successors(v)])
62
+
63
+ else:
64
+ neighbors = G.neighbors
65
+
66
+ color = {}
67
+ for n in G: # handle disconnected graphs
68
+ if n in color or len(G[n]) == 0: # skip isolates
69
+ continue
70
+ queue = [n]
71
+ color[n] = 1 # nodes seen with color (1 or 0)
72
+ while queue:
73
+ v = queue.pop()
74
+ c = 1 - color[v] # opposite color of node v
75
+ for w in neighbors(v):
76
+ if w in color:
77
+ if color[w] == color[v]:
78
+ raise nx.NetworkXError("Graph is not bipartite.")
79
+ else:
80
+ color[w] = c
81
+ queue.append(w)
82
+ # color isolates with 0
83
+ color.update(dict.fromkeys(nx.isolates(G), 0))
84
+ return color
85
+
86
+
87
+ @nx._dispatchable
88
+ def is_bipartite(G):
89
+ """Returns True if graph G is bipartite, False if not.
90
+
91
+ Parameters
92
+ ----------
93
+ G : NetworkX graph
94
+
95
+ Examples
96
+ --------
97
+ >>> from networkx.algorithms import bipartite
98
+ >>> G = nx.path_graph(4)
99
+ >>> print(bipartite.is_bipartite(G))
100
+ True
101
+
102
+ See Also
103
+ --------
104
+ color, is_bipartite_node_set
105
+ """
106
+ try:
107
+ color(G)
108
+ return True
109
+ except nx.NetworkXError:
110
+ return False
111
+
112
+
113
+ @nx._dispatchable
114
+ def is_bipartite_node_set(G, nodes):
115
+ """Returns True if nodes and G/nodes are a bipartition of G.
116
+
117
+ Parameters
118
+ ----------
119
+ G : NetworkX graph
120
+
121
+ nodes: list or container
122
+ Check if nodes are a one of a bipartite set.
123
+
124
+ Examples
125
+ --------
126
+ >>> from networkx.algorithms import bipartite
127
+ >>> G = nx.path_graph(4)
128
+ >>> X = set([1, 3])
129
+ >>> bipartite.is_bipartite_node_set(G, X)
130
+ True
131
+
132
+ Notes
133
+ -----
134
+ An exception is raised if the input nodes are not distinct, because in this
135
+ case some bipartite algorithms will yield incorrect results.
136
+ For connected graphs the bipartite sets are unique. This function handles
137
+ disconnected graphs.
138
+ """
139
+ S = set(nodes)
140
+
141
+ if len(S) < len(nodes):
142
+ # this should maybe just return False?
143
+ raise AmbiguousSolution(
144
+ "The input node set contains duplicates.\n"
145
+ "This may lead to incorrect results when using it in bipartite algorithms.\n"
146
+ "Consider using set(nodes) as the input"
147
+ )
148
+
149
+ for CC in (G.subgraph(c).copy() for c in connected_components(G)):
150
+ X, Y = sets(CC)
151
+ if not (
152
+ (X.issubset(S) and Y.isdisjoint(S)) or (Y.issubset(S) and X.isdisjoint(S))
153
+ ):
154
+ return False
155
+ return True
156
+
157
+
158
+ @nx._dispatchable
159
+ def sets(G, top_nodes=None):
160
+ """Returns bipartite node sets of graph G.
161
+
162
+ Raises an exception if the graph is not bipartite or if the input
163
+ graph is disconnected and thus more than one valid solution exists.
164
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
165
+ for further details on how bipartite graphs are handled in NetworkX.
166
+
167
+ Parameters
168
+ ----------
169
+ G : NetworkX graph
170
+
171
+ top_nodes : container, optional
172
+ Container with all nodes in one bipartite node set. If not supplied
173
+ it will be computed. But if more than one solution exists an exception
174
+ will be raised.
175
+
176
+ Returns
177
+ -------
178
+ X : set
179
+ Nodes from one side of the bipartite graph.
180
+ Y : set
181
+ Nodes from the other side.
182
+
183
+ Raises
184
+ ------
185
+ AmbiguousSolution
186
+ Raised if the input bipartite graph is disconnected and no container
187
+ with all nodes in one bipartite set is provided. When determining
188
+ the nodes in each bipartite set more than one valid solution is
189
+ possible if the input graph is disconnected.
190
+ NetworkXError
191
+ Raised if the input graph is not bipartite.
192
+
193
+ Examples
194
+ --------
195
+ >>> from networkx.algorithms import bipartite
196
+ >>> G = nx.path_graph(4)
197
+ >>> X, Y = bipartite.sets(G)
198
+ >>> list(X)
199
+ [0, 2]
200
+ >>> list(Y)
201
+ [1, 3]
202
+
203
+ See Also
204
+ --------
205
+ color
206
+
207
+ """
208
+ if G.is_directed():
209
+ is_connected = nx.is_weakly_connected
210
+ else:
211
+ is_connected = nx.is_connected
212
+ if top_nodes is not None:
213
+ X = set(top_nodes)
214
+ Y = set(G) - X
215
+ else:
216
+ if not is_connected(G):
217
+ msg = "Disconnected graph: Ambiguous solution for bipartite sets."
218
+ raise nx.AmbiguousSolution(msg)
219
+ c = color(G)
220
+ X = {n for n, is_top in c.items() if is_top}
221
+ Y = {n for n, is_top in c.items() if not is_top}
222
+ return (X, Y)
223
+
224
+
225
+ @nx._dispatchable(graphs="B")
226
+ def density(B, nodes):
227
+ """Returns density of bipartite graph B.
228
+
229
+ Parameters
230
+ ----------
231
+ B : NetworkX graph
232
+
233
+ nodes: list or container
234
+ Nodes in one node set of the bipartite graph.
235
+
236
+ Returns
237
+ -------
238
+ d : float
239
+ The bipartite density
240
+
241
+ Examples
242
+ --------
243
+ >>> from networkx.algorithms import bipartite
244
+ >>> G = nx.complete_bipartite_graph(3, 2)
245
+ >>> X = set([0, 1, 2])
246
+ >>> bipartite.density(G, X)
247
+ 1.0
248
+ >>> Y = set([3, 4])
249
+ >>> bipartite.density(G, Y)
250
+ 1.0
251
+
252
+ Notes
253
+ -----
254
+ The container of nodes passed as argument must contain all nodes
255
+ in one of the two bipartite node sets to avoid ambiguity in the
256
+ case of disconnected graphs.
257
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
258
+ for further details on how bipartite graphs are handled in NetworkX.
259
+
260
+ See Also
261
+ --------
262
+ color
263
+ """
264
+ n = len(B)
265
+ m = nx.number_of_edges(B)
266
+ nb = len(nodes)
267
+ nt = n - nb
268
+ if m == 0: # includes cases n==0 and n==1
269
+ d = 0.0
270
+ else:
271
+ if B.is_directed():
272
+ d = m / (2 * nb * nt)
273
+ else:
274
+ d = m / (nb * nt)
275
+ return d
276
+
277
+
278
+ @nx._dispatchable(graphs="B", edge_attrs="weight")
279
+ def degrees(B, nodes, weight=None):
280
+ """Returns the degrees of the two node sets in the bipartite graph B.
281
+
282
+ Parameters
283
+ ----------
284
+ B : NetworkX graph
285
+
286
+ nodes: list or container
287
+ Nodes in one node set of the bipartite graph.
288
+
289
+ weight : string or None, optional (default=None)
290
+ The edge attribute that holds the numerical value used as a weight.
291
+ If None, then each edge has weight 1.
292
+ The degree is the sum of the edge weights adjacent to the node.
293
+
294
+ Returns
295
+ -------
296
+ (degX,degY) : tuple of dictionaries
297
+ The degrees of the two bipartite sets as dictionaries keyed by node.
298
+
299
+ Examples
300
+ --------
301
+ >>> from networkx.algorithms import bipartite
302
+ >>> G = nx.complete_bipartite_graph(3, 2)
303
+ >>> Y = set([3, 4])
304
+ >>> degX, degY = bipartite.degrees(G, Y)
305
+ >>> dict(degX)
306
+ {0: 2, 1: 2, 2: 2}
307
+
308
+ Notes
309
+ -----
310
+ The container of nodes passed as argument must contain all nodes
311
+ in one of the two bipartite node sets to avoid ambiguity in the
312
+ case of disconnected graphs.
313
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
314
+ for further details on how bipartite graphs are handled in NetworkX.
315
+
316
+ See Also
317
+ --------
318
+ color, density
319
+ """
320
+ bottom = set(nodes)
321
+ top = set(B) - bottom
322
+ return (B.degree(top, weight), B.degree(bottom, weight))
lib/python3.12/site-packages/networkx/algorithms/bipartite/centrality.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import networkx as nx
2
+
3
+ __all__ = ["degree_centrality", "betweenness_centrality", "closeness_centrality"]
4
+
5
+
6
+ @nx._dispatchable(name="bipartite_degree_centrality")
7
+ def degree_centrality(G, nodes):
8
+ r"""Compute the degree centrality for nodes in a bipartite network.
9
+
10
+ The degree centrality for a node `v` is the fraction of nodes
11
+ connected to it.
12
+
13
+ Parameters
14
+ ----------
15
+ G : graph
16
+ A bipartite network
17
+
18
+ nodes : list or container
19
+ Container with all nodes in one bipartite node set.
20
+
21
+ Returns
22
+ -------
23
+ centrality : dictionary
24
+ Dictionary keyed by node with bipartite degree centrality as the value.
25
+
26
+ Examples
27
+ --------
28
+ >>> G = nx.wheel_graph(5)
29
+ >>> top_nodes = {0, 1, 2}
30
+ >>> nx.bipartite.degree_centrality(G, nodes=top_nodes)
31
+ {0: 2.0, 1: 1.5, 2: 1.5, 3: 1.0, 4: 1.0}
32
+
33
+ See Also
34
+ --------
35
+ betweenness_centrality
36
+ closeness_centrality
37
+ :func:`~networkx.algorithms.bipartite.basic.sets`
38
+ :func:`~networkx.algorithms.bipartite.basic.is_bipartite`
39
+
40
+ Notes
41
+ -----
42
+ The nodes input parameter must contain all nodes in one bipartite node set,
43
+ but the dictionary returned contains all nodes from both bipartite node
44
+ sets. See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
45
+ for further details on how bipartite graphs are handled in NetworkX.
46
+
47
+ For unipartite networks, the degree centrality values are
48
+ normalized by dividing by the maximum possible degree (which is
49
+ `n-1` where `n` is the number of nodes in G).
50
+
51
+ In the bipartite case, the maximum possible degree of a node in a
52
+ bipartite node set is the number of nodes in the opposite node set
53
+ [1]_. The degree centrality for a node `v` in the bipartite
54
+ sets `U` with `n` nodes and `V` with `m` nodes is
55
+
56
+ .. math::
57
+
58
+ d_{v} = \frac{deg(v)}{m}, \mbox{for} v \in U ,
59
+
60
+ d_{v} = \frac{deg(v)}{n}, \mbox{for} v \in V ,
61
+
62
+
63
+ where `deg(v)` is the degree of node `v`.
64
+
65
+ References
66
+ ----------
67
+ .. [1] Borgatti, S.P. and Halgin, D. In press. "Analyzing Affiliation
68
+ Networks". In Carrington, P. and Scott, J. (eds) The Sage Handbook
69
+ of Social Network Analysis. Sage Publications.
70
+ https://dx.doi.org/10.4135/9781446294413.n28
71
+ """
72
+ top = set(nodes)
73
+ bottom = set(G) - top
74
+ s = 1.0 / len(bottom)
75
+ centrality = {n: d * s for n, d in G.degree(top)}
76
+ s = 1.0 / len(top)
77
+ centrality.update({n: d * s for n, d in G.degree(bottom)})
78
+ return centrality
79
+
80
+
81
+ @nx._dispatchable(name="bipartite_betweenness_centrality")
82
+ def betweenness_centrality(G, nodes):
83
+ r"""Compute betweenness centrality for nodes in a bipartite network.
84
+
85
+ Betweenness centrality of a node `v` is the sum of the
86
+ fraction of all-pairs shortest paths that pass through `v`.
87
+
88
+ Values of betweenness are normalized by the maximum possible
89
+ value which for bipartite graphs is limited by the relative size
90
+ of the two node sets [1]_.
91
+
92
+ Let `n` be the number of nodes in the node set `U` and
93
+ `m` be the number of nodes in the node set `V`, then
94
+ nodes in `U` are normalized by dividing by
95
+
96
+ .. math::
97
+
98
+ \frac{1}{2} [m^2 (s + 1)^2 + m (s + 1)(2t - s - 1) - t (2s - t + 3)] ,
99
+
100
+ where
101
+
102
+ .. math::
103
+
104
+ s = (n - 1) \div m , t = (n - 1) \mod m ,
105
+
106
+ and nodes in `V` are normalized by dividing by
107
+
108
+ .. math::
109
+
110
+ \frac{1}{2} [n^2 (p + 1)^2 + n (p + 1)(2r - p - 1) - r (2p - r + 3)] ,
111
+
112
+ where,
113
+
114
+ .. math::
115
+
116
+ p = (m - 1) \div n , r = (m - 1) \mod n .
117
+
118
+ Parameters
119
+ ----------
120
+ G : graph
121
+ A bipartite graph
122
+
123
+ nodes : list or container
124
+ Container with all nodes in one bipartite node set.
125
+
126
+ Returns
127
+ -------
128
+ betweenness : dictionary
129
+ Dictionary keyed by node with bipartite betweenness centrality
130
+ as the value.
131
+
132
+ Examples
133
+ --------
134
+ >>> G = nx.cycle_graph(4)
135
+ >>> top_nodes = {1, 2}
136
+ >>> nx.bipartite.betweenness_centrality(G, nodes=top_nodes)
137
+ {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25}
138
+
139
+ See Also
140
+ --------
141
+ degree_centrality
142
+ closeness_centrality
143
+ :func:`~networkx.algorithms.bipartite.basic.sets`
144
+ :func:`~networkx.algorithms.bipartite.basic.is_bipartite`
145
+
146
+ Notes
147
+ -----
148
+ The nodes input parameter must contain all nodes in one bipartite node set,
149
+ but the dictionary returned contains all nodes from both node sets.
150
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
151
+ for further details on how bipartite graphs are handled in NetworkX.
152
+
153
+
154
+ References
155
+ ----------
156
+ .. [1] Borgatti, S.P. and Halgin, D. In press. "Analyzing Affiliation
157
+ Networks". In Carrington, P. and Scott, J. (eds) The Sage Handbook
158
+ of Social Network Analysis. Sage Publications.
159
+ https://dx.doi.org/10.4135/9781446294413.n28
160
+ """
161
+ top = set(nodes)
162
+ bottom = set(G) - top
163
+ n = len(top)
164
+ m = len(bottom)
165
+ s, t = divmod(n - 1, m)
166
+ bet_max_top = (
167
+ ((m**2) * ((s + 1) ** 2))
168
+ + (m * (s + 1) * (2 * t - s - 1))
169
+ - (t * ((2 * s) - t + 3))
170
+ ) / 2.0
171
+ p, r = divmod(m - 1, n)
172
+ bet_max_bot = (
173
+ ((n**2) * ((p + 1) ** 2))
174
+ + (n * (p + 1) * (2 * r - p - 1))
175
+ - (r * ((2 * p) - r + 3))
176
+ ) / 2.0
177
+ betweenness = nx.betweenness_centrality(G, normalized=False, weight=None)
178
+ for node in top:
179
+ betweenness[node] /= bet_max_top
180
+ for node in bottom:
181
+ betweenness[node] /= bet_max_bot
182
+ return betweenness
183
+
184
+
185
+ @nx._dispatchable(name="bipartite_closeness_centrality")
186
+ def closeness_centrality(G, nodes, normalized=True):
187
+ r"""Compute the closeness centrality for nodes in a bipartite network.
188
+
189
+ The closeness of a node is the distance to all other nodes in the
190
+ graph or in the case that the graph is not connected to all other nodes
191
+ in the connected component containing that node.
192
+
193
+ Parameters
194
+ ----------
195
+ G : graph
196
+ A bipartite network
197
+
198
+ nodes : list or container
199
+ Container with all nodes in one bipartite node set.
200
+
201
+ normalized : bool, optional
202
+ If True (default) normalize by connected component size.
203
+
204
+ Returns
205
+ -------
206
+ closeness : dictionary
207
+ Dictionary keyed by node with bipartite closeness centrality
208
+ as the value.
209
+
210
+ Examples
211
+ --------
212
+ >>> G = nx.wheel_graph(5)
213
+ >>> top_nodes = {0, 1, 2}
214
+ >>> nx.bipartite.closeness_centrality(G, nodes=top_nodes)
215
+ {0: 1.5, 1: 1.2, 2: 1.2, 3: 1.0, 4: 1.0}
216
+
217
+ See Also
218
+ --------
219
+ betweenness_centrality
220
+ degree_centrality
221
+ :func:`~networkx.algorithms.bipartite.basic.sets`
222
+ :func:`~networkx.algorithms.bipartite.basic.is_bipartite`
223
+
224
+ Notes
225
+ -----
226
+ The nodes input parameter must contain all nodes in one bipartite node set,
227
+ but the dictionary returned contains all nodes from both node sets.
228
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
229
+ for further details on how bipartite graphs are handled in NetworkX.
230
+
231
+
232
+ Closeness centrality is normalized by the minimum distance possible.
233
+ In the bipartite case the minimum distance for a node in one bipartite
234
+ node set is 1 from all nodes in the other node set and 2 from all
235
+ other nodes in its own set [1]_. Thus the closeness centrality
236
+ for node `v` in the two bipartite sets `U` with
237
+ `n` nodes and `V` with `m` nodes is
238
+
239
+ .. math::
240
+
241
+ c_{v} = \frac{m + 2(n - 1)}{d}, \mbox{for} v \in U,
242
+
243
+ c_{v} = \frac{n + 2(m - 1)}{d}, \mbox{for} v \in V,
244
+
245
+ where `d` is the sum of the distances from `v` to all
246
+ other nodes.
247
+
248
+ Higher values of closeness indicate higher centrality.
249
+
250
+ As in the unipartite case, setting normalized=True causes the
251
+ values to normalized further to n-1 / size(G)-1 where n is the
252
+ number of nodes in the connected part of graph containing the
253
+ node. If the graph is not completely connected, this algorithm
254
+ computes the closeness centrality for each connected part
255
+ separately.
256
+
257
+ References
258
+ ----------
259
+ .. [1] Borgatti, S.P. and Halgin, D. In press. "Analyzing Affiliation
260
+ Networks". In Carrington, P. and Scott, J. (eds) The Sage Handbook
261
+ of Social Network Analysis. Sage Publications.
262
+ https://dx.doi.org/10.4135/9781446294413.n28
263
+ """
264
+ closeness = {}
265
+ path_length = nx.single_source_shortest_path_length
266
+ top = set(nodes)
267
+ bottom = set(G) - top
268
+ n = len(top)
269
+ m = len(bottom)
270
+ for node in top:
271
+ sp = dict(path_length(G, node))
272
+ totsp = sum(sp.values())
273
+ if totsp > 0.0 and len(G) > 1:
274
+ closeness[node] = (m + 2 * (n - 1)) / totsp
275
+ if normalized:
276
+ s = (len(sp) - 1) / (len(G) - 1)
277
+ closeness[node] *= s
278
+ else:
279
+ closeness[node] = 0.0
280
+ for node in bottom:
281
+ sp = dict(path_length(G, node))
282
+ totsp = sum(sp.values())
283
+ if totsp > 0.0 and len(G) > 1:
284
+ closeness[node] = (n + 2 * (m - 1)) / totsp
285
+ if normalized:
286
+ s = (len(sp) - 1) / (len(G) - 1)
287
+ closeness[node] *= s
288
+ else:
289
+ closeness[node] = 0.0
290
+ return closeness
lib/python3.12/site-packages/networkx/algorithms/bipartite/cluster.py ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions for computing clustering of pairs"""
2
+
3
+ import itertools
4
+
5
+ import networkx as nx
6
+
7
+ __all__ = [
8
+ "clustering",
9
+ "average_clustering",
10
+ "latapy_clustering",
11
+ "robins_alexander_clustering",
12
+ ]
13
+
14
+
15
+ def cc_dot(nu, nv):
16
+ return len(nu & nv) / len(nu | nv)
17
+
18
+
19
+ def cc_max(nu, nv):
20
+ return len(nu & nv) / max(len(nu), len(nv))
21
+
22
+
23
+ def cc_min(nu, nv):
24
+ return len(nu & nv) / min(len(nu), len(nv))
25
+
26
+
27
+ modes = {"dot": cc_dot, "min": cc_min, "max": cc_max}
28
+
29
+
30
+ @nx._dispatchable
31
+ def latapy_clustering(G, nodes=None, mode="dot"):
32
+ r"""Compute a bipartite clustering coefficient for nodes.
33
+
34
+ The bipartite clustering coefficient is a measure of local density
35
+ of connections defined as [1]_:
36
+
37
+ .. math::
38
+
39
+ c_u = \frac{\sum_{v \in N(N(u))} c_{uv} }{|N(N(u))|}
40
+
41
+ where `N(N(u))` are the second order neighbors of `u` in `G` excluding `u`,
42
+ and `c_{uv}` is the pairwise clustering coefficient between nodes
43
+ `u` and `v`.
44
+
45
+ The mode selects the function for `c_{uv}` which can be:
46
+
47
+ `dot`:
48
+
49
+ .. math::
50
+
51
+ c_{uv}=\frac{|N(u)\cap N(v)|}{|N(u) \cup N(v)|}
52
+
53
+ `min`:
54
+
55
+ .. math::
56
+
57
+ c_{uv}=\frac{|N(u)\cap N(v)|}{min(|N(u)|,|N(v)|)}
58
+
59
+ `max`:
60
+
61
+ .. math::
62
+
63
+ c_{uv}=\frac{|N(u)\cap N(v)|}{max(|N(u)|,|N(v)|)}
64
+
65
+
66
+ Parameters
67
+ ----------
68
+ G : graph
69
+ A bipartite graph
70
+
71
+ nodes : list or iterable (optional)
72
+ Compute bipartite clustering for these nodes. The default
73
+ is all nodes in G.
74
+
75
+ mode : string
76
+ The pairwise bipartite clustering method to be used in the computation.
77
+ It must be "dot", "max", or "min".
78
+
79
+ Returns
80
+ -------
81
+ clustering : dictionary
82
+ A dictionary keyed by node with the clustering coefficient value.
83
+
84
+
85
+ Examples
86
+ --------
87
+ >>> from networkx.algorithms import bipartite
88
+ >>> G = nx.path_graph(4) # path graphs are bipartite
89
+ >>> c = bipartite.clustering(G)
90
+ >>> c[0]
91
+ 0.5
92
+ >>> c = bipartite.clustering(G, mode="min")
93
+ >>> c[0]
94
+ 1.0
95
+
96
+ See Also
97
+ --------
98
+ robins_alexander_clustering
99
+ average_clustering
100
+ networkx.algorithms.cluster.square_clustering
101
+
102
+ References
103
+ ----------
104
+ .. [1] Latapy, Matthieu, Clémence Magnien, and Nathalie Del Vecchio (2008).
105
+ Basic notions for the analysis of large two-mode networks.
106
+ Social Networks 30(1), 31--48.
107
+ """
108
+ if not nx.algorithms.bipartite.is_bipartite(G):
109
+ raise nx.NetworkXError("Graph is not bipartite")
110
+
111
+ try:
112
+ cc_func = modes[mode]
113
+ except KeyError as err:
114
+ raise nx.NetworkXError(
115
+ "Mode for bipartite clustering must be: dot, min or max"
116
+ ) from err
117
+
118
+ if nodes is None:
119
+ nodes = G
120
+ ccs = {}
121
+ for v in nodes:
122
+ cc = 0.0
123
+ nbrs2 = {u for nbr in G[v] for u in G[nbr]} - {v}
124
+ for u in nbrs2:
125
+ cc += cc_func(set(G[u]), set(G[v]))
126
+ if cc > 0.0: # len(nbrs2)>0
127
+ cc /= len(nbrs2)
128
+ ccs[v] = cc
129
+ return ccs
130
+
131
+
132
+ clustering = latapy_clustering
133
+
134
+
135
+ @nx._dispatchable(name="bipartite_average_clustering")
136
+ def average_clustering(G, nodes=None, mode="dot"):
137
+ r"""Compute the average bipartite clustering coefficient.
138
+
139
+ A clustering coefficient for the whole graph is the average,
140
+
141
+ .. math::
142
+
143
+ C = \frac{1}{n}\sum_{v \in G} c_v,
144
+
145
+ where `n` is the number of nodes in `G`.
146
+
147
+ Similar measures for the two bipartite sets can be defined [1]_
148
+
149
+ .. math::
150
+
151
+ C_X = \frac{1}{|X|}\sum_{v \in X} c_v,
152
+
153
+ where `X` is a bipartite set of `G`.
154
+
155
+ Parameters
156
+ ----------
157
+ G : graph
158
+ a bipartite graph
159
+
160
+ nodes : list or iterable, optional
161
+ A container of nodes to use in computing the average.
162
+ The nodes should be either the entire graph (the default) or one of the
163
+ bipartite sets.
164
+
165
+ mode : string
166
+ The pairwise bipartite clustering method.
167
+ It must be "dot", "max", or "min"
168
+
169
+ Returns
170
+ -------
171
+ clustering : float
172
+ The average bipartite clustering for the given set of nodes or the
173
+ entire graph if no nodes are specified.
174
+
175
+ Examples
176
+ --------
177
+ >>> from networkx.algorithms import bipartite
178
+ >>> G = nx.star_graph(3) # star graphs are bipartite
179
+ >>> bipartite.average_clustering(G)
180
+ 0.75
181
+ >>> X, Y = bipartite.sets(G)
182
+ >>> bipartite.average_clustering(G, X)
183
+ 0.0
184
+ >>> bipartite.average_clustering(G, Y)
185
+ 1.0
186
+
187
+ See Also
188
+ --------
189
+ clustering
190
+
191
+ Notes
192
+ -----
193
+ The container of nodes passed to this function must contain all of the nodes
194
+ in one of the bipartite sets ("top" or "bottom") in order to compute
195
+ the correct average bipartite clustering coefficients.
196
+ See :mod:`bipartite documentation <networkx.algorithms.bipartite>`
197
+ for further details on how bipartite graphs are handled in NetworkX.
198
+
199
+
200
+ References
201
+ ----------
202
+ .. [1] Latapy, Matthieu, Clémence Magnien, and Nathalie Del Vecchio (2008).
203
+ Basic notions for the analysis of large two-mode networks.
204
+ Social Networks 30(1), 31--48.
205
+ """
206
+ if nodes is None:
207
+ nodes = G
208
+ ccs = latapy_clustering(G, nodes=nodes, mode=mode)
209
+ return sum(ccs[v] for v in nodes) / len(nodes)
210
+
211
+
212
+ @nx._dispatchable
213
+ def robins_alexander_clustering(G):
214
+ r"""Compute the bipartite clustering of G.
215
+
216
+ Robins and Alexander [1]_ defined bipartite clustering coefficient as
217
+ four times the number of four cycles `C_4` divided by the number of
218
+ three paths `L_3` in a bipartite graph:
219
+
220
+ .. math::
221
+
222
+ CC_4 = \frac{4 * C_4}{L_3}
223
+
224
+ Parameters
225
+ ----------
226
+ G : graph
227
+ a bipartite graph
228
+
229
+ Returns
230
+ -------
231
+ clustering : float
232
+ The Robins and Alexander bipartite clustering for the input graph.
233
+
234
+ Examples
235
+ --------
236
+ >>> from networkx.algorithms import bipartite
237
+ >>> G = nx.davis_southern_women_graph()
238
+ >>> print(round(bipartite.robins_alexander_clustering(G), 3))
239
+ 0.468
240
+
241
+ See Also
242
+ --------
243
+ latapy_clustering
244
+ networkx.algorithms.cluster.square_clustering
245
+
246
+ References
247
+ ----------
248
+ .. [1] Robins, G. and M. Alexander (2004). Small worlds among interlocking
249
+ directors: Network structure and distance in bipartite graphs.
250
+ Computational & Mathematical Organization Theory 10(1), 69–94.
251
+
252
+ """
253
+ if G.order() < 4 or G.size() < 3:
254
+ return 0
255
+ L_3 = _threepaths(G)
256
+ if L_3 == 0:
257
+ return 0
258
+ C_4 = _four_cycles(G)
259
+ return (4.0 * C_4) / L_3
260
+
261
+
262
+ def _four_cycles(G):
263
+ # Also see `square_clustering` which counts squares in a similar way
264
+ cycles = 0
265
+ seen = set()
266
+ G_adj = G._adj
267
+ for v in G:
268
+ seen.add(v)
269
+ v_neighbors = set(G_adj[v])
270
+ if len(v_neighbors) < 2:
271
+ # Can't form a square without at least two neighbors
272
+ continue
273
+ two_hop_neighbors = set().union(*(G_adj[u] for u in v_neighbors))
274
+ two_hop_neighbors -= seen
275
+ for x in two_hop_neighbors:
276
+ p2 = len(v_neighbors.intersection(G_adj[x]))
277
+ cycles += p2 * (p2 - 1)
278
+ return cycles / 4
279
+
280
+
281
+ def _threepaths(G):
282
+ paths = 0
283
+ for v in G:
284
+ for u in G[v]:
285
+ for w in set(G[u]) - {v}:
286
+ paths += len(set(G[w]) - {v, u})
287
+ # Divide by two because we count each three path twice
288
+ # one for each possible starting point
289
+ return paths / 2
lib/python3.12/site-packages/networkx/algorithms/bipartite/covering.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions related to graph covers."""
2
+
3
+ import networkx as nx
4
+ from networkx.algorithms.bipartite.matching import hopcroft_karp_matching
5
+ from networkx.algorithms.covering import min_edge_cover as _min_edge_cover
6
+ from networkx.utils import not_implemented_for
7
+
8
+ __all__ = ["min_edge_cover"]
9
+
10
+
11
+ @not_implemented_for("directed")
12
+ @not_implemented_for("multigraph")
13
+ @nx._dispatchable(name="bipartite_min_edge_cover")
14
+ def min_edge_cover(G, matching_algorithm=None):
15
+ """Returns a set of edges which constitutes
16
+ the minimum edge cover of the graph.
17
+
18
+ The smallest edge cover can be found in polynomial time by finding
19
+ a maximum matching and extending it greedily so that all nodes
20
+ are covered.
21
+
22
+ Parameters
23
+ ----------
24
+ G : NetworkX graph
25
+ An undirected bipartite graph.
26
+
27
+ matching_algorithm : function
28
+ A function that returns a maximum cardinality matching in a
29
+ given bipartite graph. The function must take one input, the
30
+ graph ``G``, and return a dictionary mapping each node to its
31
+ mate. If not specified,
32
+ :func:`~networkx.algorithms.bipartite.matching.hopcroft_karp_matching`
33
+ will be used. Other possibilities include
34
+ :func:`~networkx.algorithms.bipartite.matching.eppstein_matching`,
35
+
36
+ Returns
37
+ -------
38
+ set
39
+ A set of the edges in a minimum edge cover of the graph, given as
40
+ pairs of nodes. It contains both the edges `(u, v)` and `(v, u)`
41
+ for given nodes `u` and `v` among the edges of minimum edge cover.
42
+
43
+ Notes
44
+ -----
45
+ An edge cover of a graph is a set of edges such that every node of
46
+ the graph is incident to at least one edge of the set.
47
+ A minimum edge cover is an edge covering of smallest cardinality.
48
+
49
+ Due to its implementation, the worst-case running time of this algorithm
50
+ is bounded by the worst-case running time of the function
51
+ ``matching_algorithm``.
52
+ """
53
+ if G.order() == 0: # Special case for the empty graph
54
+ return set()
55
+ if matching_algorithm is None:
56
+ matching_algorithm = hopcroft_karp_matching
57
+ return _min_edge_cover(G, matching_algorithm=matching_algorithm)
lib/python3.12/site-packages/networkx/algorithms/bipartite/edgelist.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ********************
3
+ Bipartite Edge Lists
4
+ ********************
5
+ Read and write NetworkX graphs as bipartite edge lists.
6
+
7
+ Format
8
+ ------
9
+ You can read or write three formats of edge lists with these functions.
10
+
11
+ Node pairs with no data::
12
+
13
+ 1 2
14
+
15
+ Python dictionary as data::
16
+
17
+ 1 2 {'weight':7, 'color':'green'}
18
+
19
+ Arbitrary data::
20
+
21
+ 1 2 7 green
22
+
23
+ For each edge (u, v) the node u is assigned to part 0 and the node v to part 1.
24
+ """
25
+
26
+ __all__ = ["generate_edgelist", "write_edgelist", "parse_edgelist", "read_edgelist"]
27
+
28
+ import networkx as nx
29
+ from networkx.utils import not_implemented_for, open_file
30
+
31
+
32
+ @open_file(1, mode="wb")
33
+ def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"):
34
+ """Write a bipartite graph as a list of edges.
35
+
36
+ Parameters
37
+ ----------
38
+ G : Graph
39
+ A NetworkX bipartite graph
40
+ path : file or string
41
+ File or filename to write. If a file is provided, it must be
42
+ opened in 'wb' mode. Filenames ending in .gz or .bz2 will be compressed.
43
+ comments : string, optional
44
+ The character used to indicate the start of a comment
45
+ delimiter : string, optional
46
+ The string used to separate values. The default is whitespace.
47
+ data : bool or list, optional
48
+ If False write no edge data.
49
+ If True write a string representation of the edge data dictionary..
50
+ If a list (or other iterable) is provided, write the keys specified
51
+ in the list.
52
+ encoding: string, optional
53
+ Specify which encoding to use when writing file.
54
+
55
+ Examples
56
+ --------
57
+ >>> G = nx.path_graph(4)
58
+ >>> G.add_nodes_from([0, 2], bipartite=0)
59
+ >>> G.add_nodes_from([1, 3], bipartite=1)
60
+ >>> nx.write_edgelist(G, "test.edgelist")
61
+ >>> fh = open("test.edgelist_open", "wb")
62
+ >>> nx.write_edgelist(G, fh)
63
+ >>> nx.write_edgelist(G, "test.edgelist.gz")
64
+ >>> nx.write_edgelist(G, "test.edgelist_nodata.gz", data=False)
65
+
66
+ >>> G = nx.Graph()
67
+ >>> G.add_edge(1, 2, weight=7, color="red")
68
+ >>> nx.write_edgelist(G, "test.edgelist_bigger_nodata", data=False)
69
+ >>> nx.write_edgelist(G, "test.edgelist_color", data=["color"])
70
+ >>> nx.write_edgelist(G, "test.edgelist_color_weight", data=["color", "weight"])
71
+
72
+ See Also
73
+ --------
74
+ write_edgelist
75
+ generate_edgelist
76
+ """
77
+ for line in generate_edgelist(G, delimiter, data):
78
+ line += "\n"
79
+ path.write(line.encode(encoding))
80
+
81
+
82
+ @not_implemented_for("directed")
83
+ def generate_edgelist(G, delimiter=" ", data=True):
84
+ """Generate a single line of the bipartite graph G in edge list format.
85
+
86
+ Parameters
87
+ ----------
88
+ G : NetworkX graph
89
+ The graph is assumed to have node attribute `part` set to 0,1 representing
90
+ the two graph parts
91
+
92
+ delimiter : string, optional
93
+ Separator for node labels
94
+
95
+ data : bool or list of keys
96
+ If False generate no edge data. If True use a dictionary
97
+ representation of edge data. If a list of keys use a list of data
98
+ values corresponding to the keys.
99
+
100
+ Returns
101
+ -------
102
+ lines : string
103
+ Lines of data in adjlist format.
104
+
105
+ Examples
106
+ --------
107
+ >>> from networkx.algorithms import bipartite
108
+ >>> G = nx.path_graph(4)
109
+ >>> G.add_nodes_from([0, 2], bipartite=0)
110
+ >>> G.add_nodes_from([1, 3], bipartite=1)
111
+ >>> G[1][2]["weight"] = 3
112
+ >>> G[2][3]["capacity"] = 12
113
+ >>> for line in bipartite.generate_edgelist(G, data=False):
114
+ ... print(line)
115
+ 0 1
116
+ 2 1
117
+ 2 3
118
+
119
+ >>> for line in bipartite.generate_edgelist(G):
120
+ ... print(line)
121
+ 0 1 {}
122
+ 2 1 {'weight': 3}
123
+ 2 3 {'capacity': 12}
124
+
125
+ >>> for line in bipartite.generate_edgelist(G, data=["weight"]):
126
+ ... print(line)
127
+ 0 1
128
+ 2 1 3
129
+ 2 3
130
+ """
131
+ try:
132
+ part0 = [n for n, d in G.nodes.items() if d["bipartite"] == 0]
133
+ except BaseException as err:
134
+ raise AttributeError("Missing node attribute `bipartite`") from err
135
+ if data is True or data is False:
136
+ for n in part0:
137
+ for edge in G.edges(n, data=data):
138
+ yield delimiter.join(map(str, edge))
139
+ else:
140
+ for n in part0:
141
+ for u, v, d in G.edges(n, data=True):
142
+ edge = [u, v]
143
+ try:
144
+ edge.extend(d[k] for k in data)
145
+ except KeyError:
146
+ pass # missing data for this edge, should warn?
147
+ yield delimiter.join(map(str, edge))
148
+
149
+
150
+ @nx._dispatchable(name="bipartite_parse_edgelist", graphs=None, returns_graph=True)
151
+ def parse_edgelist(
152
+ lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True
153
+ ):
154
+ """Parse lines of an edge list representation of a bipartite graph.
155
+
156
+ Parameters
157
+ ----------
158
+ lines : list or iterator of strings
159
+ Input data in edgelist format
160
+ comments : string, optional
161
+ Marker for comment lines
162
+ delimiter : string, optional
163
+ Separator for node labels
164
+ create_using: NetworkX graph container, optional
165
+ Use given NetworkX graph for holding nodes or edges.
166
+ nodetype : Python type, optional
167
+ Convert nodes to this type.
168
+ data : bool or list of (label,type) tuples
169
+ If False generate no edge data or if True use a dictionary
170
+ representation of edge data or a list tuples specifying dictionary
171
+ key names and types for edge data.
172
+
173
+ Returns
174
+ -------
175
+ G: NetworkX Graph
176
+ The bipartite graph corresponding to lines
177
+
178
+ Examples
179
+ --------
180
+ Edgelist with no data:
181
+
182
+ >>> from networkx.algorithms import bipartite
183
+ >>> lines = ["1 2", "2 3", "3 4"]
184
+ >>> G = bipartite.parse_edgelist(lines, nodetype=int)
185
+ >>> sorted(G.nodes())
186
+ [1, 2, 3, 4]
187
+ >>> sorted(G.nodes(data=True))
188
+ [(1, {'bipartite': 0}), (2, {'bipartite': 0}), (3, {'bipartite': 0}), (4, {'bipartite': 1})]
189
+ >>> sorted(G.edges())
190
+ [(1, 2), (2, 3), (3, 4)]
191
+
192
+ Edgelist with data in Python dictionary representation:
193
+
194
+ >>> lines = ["1 2 {'weight':3}", "2 3 {'weight':27}", "3 4 {'weight':3.0}"]
195
+ >>> G = bipartite.parse_edgelist(lines, nodetype=int)
196
+ >>> sorted(G.nodes())
197
+ [1, 2, 3, 4]
198
+ >>> sorted(G.edges(data=True))
199
+ [(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]
200
+
201
+ Edgelist with data in a list:
202
+
203
+ >>> lines = ["1 2 3", "2 3 27", "3 4 3.0"]
204
+ >>> G = bipartite.parse_edgelist(lines, nodetype=int, data=(("weight", float),))
205
+ >>> sorted(G.nodes())
206
+ [1, 2, 3, 4]
207
+ >>> sorted(G.edges(data=True))
208
+ [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]
209
+
210
+ See Also
211
+ --------
212
+ """
213
+ from ast import literal_eval
214
+
215
+ G = nx.empty_graph(0, create_using)
216
+ for line in lines:
217
+ p = line.find(comments)
218
+ if p >= 0:
219
+ line = line[:p]
220
+ if not len(line):
221
+ continue
222
+ # split line, should have 2 or more
223
+ s = line.rstrip("\n").split(delimiter)
224
+ if len(s) < 2:
225
+ continue
226
+ u = s.pop(0)
227
+ v = s.pop(0)
228
+ d = s
229
+ if nodetype is not None:
230
+ try:
231
+ u = nodetype(u)
232
+ v = nodetype(v)
233
+ except BaseException as err:
234
+ raise TypeError(
235
+ f"Failed to convert nodes {u},{v} to type {nodetype}."
236
+ ) from err
237
+
238
+ if len(d) == 0 or data is False:
239
+ # no data or data type specified
240
+ edgedata = {}
241
+ elif data is True:
242
+ # no edge types specified
243
+ try: # try to evaluate as dictionary
244
+ edgedata = dict(literal_eval(" ".join(d)))
245
+ except BaseException as err:
246
+ raise TypeError(
247
+ f"Failed to convert edge data ({d}) to dictionary."
248
+ ) from err
249
+ else:
250
+ # convert edge data to dictionary with specified keys and type
251
+ if len(d) != len(data):
252
+ raise IndexError(
253
+ f"Edge data {d} and data_keys {data} are not the same length"
254
+ )
255
+ edgedata = {}
256
+ for (edge_key, edge_type), edge_value in zip(data, d):
257
+ try:
258
+ edge_value = edge_type(edge_value)
259
+ except BaseException as err:
260
+ raise TypeError(
261
+ f"Failed to convert {edge_key} data "
262
+ f"{edge_value} to type {edge_type}."
263
+ ) from err
264
+ edgedata.update({edge_key: edge_value})
265
+ G.add_node(u, bipartite=0)
266
+ G.add_node(v, bipartite=1)
267
+ G.add_edge(u, v, **edgedata)
268
+ return G
269
+
270
+
271
+ @open_file(0, mode="rb")
272
+ @nx._dispatchable(name="bipartite_read_edgelist", graphs=None, returns_graph=True)
273
+ def read_edgelist(
274
+ path,
275
+ comments="#",
276
+ delimiter=None,
277
+ create_using=None,
278
+ nodetype=None,
279
+ data=True,
280
+ edgetype=None,
281
+ encoding="utf-8",
282
+ ):
283
+ """Read a bipartite graph from a list of edges.
284
+
285
+ Parameters
286
+ ----------
287
+ path : file or string
288
+ File or filename to read. If a file is provided, it must be
289
+ opened in 'rb' mode.
290
+ Filenames ending in .gz or .bz2 will be decompressed.
291
+ comments : string, optional
292
+ The character used to indicate the start of a comment.
293
+ delimiter : string, optional
294
+ The string used to separate values. The default is whitespace.
295
+ create_using : Graph container, optional,
296
+ Use specified container to build graph. The default is networkx.Graph,
297
+ an undirected graph.
298
+ nodetype : int, float, str, Python type, optional
299
+ Convert node data from strings to specified type
300
+ data : bool or list of (label,type) tuples
301
+ Tuples specifying dictionary key names and types for edge data
302
+ edgetype : int, float, str, Python type, optional OBSOLETE
303
+ Convert edge data from strings to specified type and use as 'weight'
304
+ encoding: string, optional
305
+ Specify which encoding to use when reading file.
306
+
307
+ Returns
308
+ -------
309
+ G : graph
310
+ A networkx Graph or other type specified with create_using
311
+
312
+ Examples
313
+ --------
314
+ >>> from networkx.algorithms import bipartite
315
+ >>> G = nx.path_graph(4)
316
+ >>> G.add_nodes_from([0, 2], bipartite=0)
317
+ >>> G.add_nodes_from([1, 3], bipartite=1)
318
+ >>> bipartite.write_edgelist(G, "test.edgelist")
319
+ >>> G = bipartite.read_edgelist("test.edgelist")
320
+
321
+ >>> fh = open("test.edgelist", "rb")
322
+ >>> G = bipartite.read_edgelist(fh)
323
+ >>> fh.close()
324
+
325
+ >>> G = bipartite.read_edgelist("test.edgelist", nodetype=int)
326
+
327
+ Edgelist with data in a list:
328
+
329
+ >>> textline = "1 2 3"
330
+ >>> fh = open("test.edgelist", "w")
331
+ >>> d = fh.write(textline)
332
+ >>> fh.close()
333
+ >>> G = bipartite.read_edgelist(
334
+ ... "test.edgelist", nodetype=int, data=(("weight", float),)
335
+ ... )
336
+ >>> list(G)
337
+ [1, 2]
338
+ >>> list(G.edges(data=True))
339
+ [(1, 2, {'weight': 3.0})]
340
+
341
+ See parse_edgelist() for more examples of formatting.
342
+
343
+ See Also
344
+ --------
345
+ parse_edgelist
346
+
347
+ Notes
348
+ -----
349
+ Since nodes must be hashable, the function nodetype must return hashable
350
+ types (e.g. int, float, str, frozenset - or tuples of those, etc.)
351
+ """
352
+ lines = (line.decode(encoding) for line in path)
353
+ return parse_edgelist(
354
+ lines,
355
+ comments=comments,
356
+ delimiter=delimiter,
357
+ create_using=create_using,
358
+ nodetype=nodetype,
359
+ data=data,
360
+ )
lib/python3.12/site-packages/networkx/algorithms/bipartite/extendability.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Provides a function for computing the extendability of a graph which is
2
+ undirected, simple, connected and bipartite and contains at least one perfect matching."""
3
+
4
+ import networkx as nx
5
+ from networkx.utils import not_implemented_for
6
+
7
+ __all__ = ["maximal_extendability"]
8
+
9
+
10
+ @not_implemented_for("directed")
11
+ @not_implemented_for("multigraph")
12
+ @nx._dispatchable
13
+ def maximal_extendability(G):
14
+ """Computes the extendability of a graph.
15
+
16
+ The extendability of a graph is defined as the maximum $k$ for which `G`
17
+ is $k$-extendable. Graph `G` is $k$-extendable if and only if `G` has a
18
+ perfect matching and every set of $k$ independent edges can be extended
19
+ to a perfect matching in `G`.
20
+
21
+ Parameters
22
+ ----------
23
+ G : NetworkX Graph
24
+ A fully-connected bipartite graph without self-loops
25
+
26
+ Returns
27
+ -------
28
+ extendability : int
29
+
30
+ Raises
31
+ ------
32
+ NetworkXError
33
+ If the graph `G` is disconnected.
34
+ If the graph `G` is not bipartite.
35
+ If the graph `G` does not contain a perfect matching.
36
+ If the residual graph of `G` is not strongly connected.
37
+
38
+ Notes
39
+ -----
40
+ Definition:
41
+ Let `G` be a simple, connected, undirected and bipartite graph with a perfect
42
+ matching M and bipartition (U,V). The residual graph of `G`, denoted by $G_M$,
43
+ is the graph obtained from G by directing the edges of M from V to U and the
44
+ edges that do not belong to M from U to V.
45
+
46
+ Lemma [1]_ :
47
+ Let M be a perfect matching of `G`. `G` is $k$-extendable if and only if its residual
48
+ graph $G_M$ is strongly connected and there are $k$ vertex-disjoint directed
49
+ paths between every vertex of U and every vertex of V.
50
+
51
+ Assuming that input graph `G` is undirected, simple, connected, bipartite and contains
52
+ a perfect matching M, this function constructs the residual graph $G_M$ of G and
53
+ returns the minimum value among the maximum vertex-disjoint directed paths between
54
+ every vertex of U and every vertex of V in $G_M$. By combining the definitions
55
+ and the lemma, this value represents the extendability of the graph `G`.
56
+
57
+ Time complexity O($n^3$ $m^2$)) where $n$ is the number of vertices
58
+ and $m$ is the number of edges.
59
+
60
+ References
61
+ ----------
62
+ .. [1] "A polynomial algorithm for the extendability problem in bipartite graphs",
63
+ J. Lakhal, L. Litzler, Information Processing Letters, 1998.
64
+ .. [2] "On n-extendible graphs", M. D. Plummer, Discrete Mathematics, 31:201–210, 1980
65
+ https://doi.org/10.1016/0012-365X(80)90037-0
66
+
67
+ """
68
+ if not nx.is_connected(G):
69
+ raise nx.NetworkXError("Graph G is not connected")
70
+
71
+ if not nx.bipartite.is_bipartite(G):
72
+ raise nx.NetworkXError("Graph G is not bipartite")
73
+
74
+ U, V = nx.bipartite.sets(G)
75
+
76
+ maximum_matching = nx.bipartite.hopcroft_karp_matching(G)
77
+
78
+ if not nx.is_perfect_matching(G, maximum_matching):
79
+ raise nx.NetworkXError("Graph G does not contain a perfect matching")
80
+
81
+ # list of edges in perfect matching, directed from V to U
82
+ pm = [(node, maximum_matching[node]) for node in V & maximum_matching.keys()]
83
+
84
+ # Direct all the edges of G, from V to U if in matching, else from U to V
85
+ directed_edges = [
86
+ (x, y) if (x in V and (x, y) in pm) or (x in U and (y, x) not in pm) else (y, x)
87
+ for x, y in G.edges
88
+ ]
89
+
90
+ # Construct the residual graph of G
91
+ residual_G = nx.DiGraph()
92
+ residual_G.add_nodes_from(G)
93
+ residual_G.add_edges_from(directed_edges)
94
+
95
+ if not nx.is_strongly_connected(residual_G):
96
+ raise nx.NetworkXError("The residual graph of G is not strongly connected")
97
+
98
+ # For node-pairs between V & U, keep min of max number of node-disjoint paths
99
+ # Variable $k$ stands for the extendability of graph G
100
+ k = float("inf")
101
+ for u in U:
102
+ for v in V:
103
+ num_paths = sum(1 for _ in nx.node_disjoint_paths(residual_G, u, v))
104
+ k = k if k < num_paths else num_paths
105
+ return k
lib/python3.12/site-packages/networkx/algorithms/bipartite/generators.py ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generators and functions for bipartite graphs.
3
+ """
4
+
5
+ import math
6
+ import numbers
7
+ from functools import reduce
8
+
9
+ import networkx as nx
10
+ from networkx.utils import nodes_or_number, py_random_state
11
+
12
+ __all__ = [
13
+ "configuration_model",
14
+ "havel_hakimi_graph",
15
+ "reverse_havel_hakimi_graph",
16
+ "alternating_havel_hakimi_graph",
17
+ "preferential_attachment_graph",
18
+ "random_graph",
19
+ "gnmk_random_graph",
20
+ "complete_bipartite_graph",
21
+ ]
22
+
23
+
24
+ @nx._dispatchable(graphs=None, returns_graph=True)
25
+ @nodes_or_number([0, 1])
26
+ def complete_bipartite_graph(n1, n2, create_using=None):
27
+ """Returns the complete bipartite graph `K_{n_1,n_2}`.
28
+
29
+ The graph is composed of two partitions with nodes 0 to (n1 - 1)
30
+ in the first and nodes n1 to (n1 + n2 - 1) in the second.
31
+ Each node in the first is connected to each node in the second.
32
+
33
+ Parameters
34
+ ----------
35
+ n1, n2 : integer or iterable container of nodes
36
+ If integers, nodes are from `range(n1)` and `range(n1, n1 + n2)`.
37
+ If a container, the elements are the nodes.
38
+ create_using : NetworkX graph instance, (default: nx.Graph)
39
+ Return graph of this type.
40
+
41
+ Notes
42
+ -----
43
+ Nodes are the integers 0 to `n1 + n2 - 1` unless either n1 or n2 are
44
+ containers of nodes. If only one of n1 or n2 are integers, that
45
+ integer is replaced by `range` of that integer.
46
+
47
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
48
+ to indicate which bipartite set the node belongs to.
49
+
50
+ This function is not imported in the main namespace.
51
+ To use it use nx.bipartite.complete_bipartite_graph
52
+ """
53
+ G = nx.empty_graph(0, create_using)
54
+ if G.is_directed():
55
+ raise nx.NetworkXError("Directed Graph not supported")
56
+
57
+ n1, top = n1
58
+ n2, bottom = n2
59
+ if isinstance(n1, numbers.Integral) and isinstance(n2, numbers.Integral):
60
+ bottom = [n1 + i for i in bottom]
61
+ G.add_nodes_from(top, bipartite=0)
62
+ G.add_nodes_from(bottom, bipartite=1)
63
+ if len(G) != len(top) + len(bottom):
64
+ raise nx.NetworkXError("Inputs n1 and n2 must contain distinct nodes")
65
+ G.add_edges_from((u, v) for u in top for v in bottom)
66
+ G.graph["name"] = f"complete_bipartite_graph({len(top)}, {len(bottom)})"
67
+ return G
68
+
69
+
70
+ @py_random_state(3)
71
+ @nx._dispatchable(name="bipartite_configuration_model", graphs=None, returns_graph=True)
72
+ def configuration_model(aseq, bseq, create_using=None, seed=None):
73
+ """Returns a random bipartite graph from two given degree sequences.
74
+
75
+ Parameters
76
+ ----------
77
+ aseq : list
78
+ Degree sequence for node set A.
79
+ bseq : list
80
+ Degree sequence for node set B.
81
+ create_using : NetworkX graph instance, optional
82
+ Return graph of this type.
83
+ seed : integer, random_state, or None (default)
84
+ Indicator of random number generation state.
85
+ See :ref:`Randomness<randomness>`.
86
+
87
+ The graph is composed of two partitions. Set A has nodes 0 to
88
+ (len(aseq) - 1) and set B has nodes len(aseq) to (len(bseq) - 1).
89
+ Nodes from set A are connected to nodes in set B by choosing
90
+ randomly from the possible free stubs, one in A and one in B.
91
+
92
+ Notes
93
+ -----
94
+ The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
95
+ If no graph type is specified use MultiGraph with parallel edges.
96
+ If you want a graph with no parallel edges use create_using=Graph()
97
+ but then the resulting degree sequences might not be exact.
98
+
99
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
100
+ to indicate which bipartite set the node belongs to.
101
+
102
+ This function is not imported in the main namespace.
103
+ To use it use nx.bipartite.configuration_model
104
+ """
105
+ G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
106
+ if G.is_directed():
107
+ raise nx.NetworkXError("Directed Graph not supported")
108
+
109
+ # length and sum of each sequence
110
+ lena = len(aseq)
111
+ lenb = len(bseq)
112
+ suma = sum(aseq)
113
+ sumb = sum(bseq)
114
+
115
+ if not suma == sumb:
116
+ raise nx.NetworkXError(
117
+ f"invalid degree sequences, sum(aseq)!=sum(bseq),{suma},{sumb}"
118
+ )
119
+
120
+ G = _add_nodes_with_bipartite_label(G, lena, lenb)
121
+
122
+ if len(aseq) == 0 or max(aseq) == 0:
123
+ return G # done if no edges
124
+
125
+ # build lists of degree-repeated vertex numbers
126
+ stubs = [[v] * aseq[v] for v in range(lena)]
127
+ astubs = [x for subseq in stubs for x in subseq]
128
+
129
+ stubs = [[v] * bseq[v - lena] for v in range(lena, lena + lenb)]
130
+ bstubs = [x for subseq in stubs for x in subseq]
131
+
132
+ # shuffle lists
133
+ seed.shuffle(astubs)
134
+ seed.shuffle(bstubs)
135
+
136
+ G.add_edges_from([astubs[i], bstubs[i]] for i in range(suma))
137
+
138
+ G.name = "bipartite_configuration_model"
139
+ return G
140
+
141
+
142
+ @nx._dispatchable(name="bipartite_havel_hakimi_graph", graphs=None, returns_graph=True)
143
+ def havel_hakimi_graph(aseq, bseq, create_using=None):
144
+ """Returns a bipartite graph from two given degree sequences using a
145
+ Havel-Hakimi style construction.
146
+
147
+ The graph is composed of two partitions. Set A has nodes 0 to
148
+ (len(aseq) - 1) and set B has nodes len(aseq) to (len(bseq) - 1).
149
+ Nodes from the set A are connected to nodes in the set B by
150
+ connecting the highest degree nodes in set A to the highest degree
151
+ nodes in set B until all stubs are connected.
152
+
153
+ Parameters
154
+ ----------
155
+ aseq : list
156
+ Degree sequence for node set A.
157
+ bseq : list
158
+ Degree sequence for node set B.
159
+ create_using : NetworkX graph instance, optional
160
+ Return graph of this type.
161
+
162
+ Notes
163
+ -----
164
+ The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
165
+ If no graph type is specified use MultiGraph with parallel edges.
166
+ If you want a graph with no parallel edges use create_using=Graph()
167
+ but then the resulting degree sequences might not be exact.
168
+
169
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
170
+ to indicate which bipartite set the node belongs to.
171
+
172
+ This function is not imported in the main namespace.
173
+ To use it use nx.bipartite.havel_hakimi_graph
174
+ """
175
+ G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
176
+ if G.is_directed():
177
+ raise nx.NetworkXError("Directed Graph not supported")
178
+
179
+ # length of the each sequence
180
+ naseq = len(aseq)
181
+ nbseq = len(bseq)
182
+
183
+ suma = sum(aseq)
184
+ sumb = sum(bseq)
185
+
186
+ if not suma == sumb:
187
+ raise nx.NetworkXError(
188
+ f"invalid degree sequences, sum(aseq)!=sum(bseq),{suma},{sumb}"
189
+ )
190
+
191
+ G = _add_nodes_with_bipartite_label(G, naseq, nbseq)
192
+
193
+ if len(aseq) == 0 or max(aseq) == 0:
194
+ return G # done if no edges
195
+
196
+ # build list of degree-repeated vertex numbers
197
+ astubs = [[aseq[v], v] for v in range(naseq)]
198
+ bstubs = [[bseq[v - naseq], v] for v in range(naseq, naseq + nbseq)]
199
+ astubs.sort()
200
+ while astubs:
201
+ (degree, u) = astubs.pop() # take of largest degree node in the a set
202
+ if degree == 0:
203
+ break # done, all are zero
204
+ # connect the source to largest degree nodes in the b set
205
+ bstubs.sort()
206
+ for target in bstubs[-degree:]:
207
+ v = target[1]
208
+ G.add_edge(u, v)
209
+ target[0] -= 1 # note this updates bstubs too.
210
+ if target[0] == 0:
211
+ bstubs.remove(target)
212
+
213
+ G.name = "bipartite_havel_hakimi_graph"
214
+ return G
215
+
216
+
217
+ @nx._dispatchable(graphs=None, returns_graph=True)
218
+ def reverse_havel_hakimi_graph(aseq, bseq, create_using=None):
219
+ """Returns a bipartite graph from two given degree sequences using a
220
+ Havel-Hakimi style construction.
221
+
222
+ The graph is composed of two partitions. Set A has nodes 0 to
223
+ (len(aseq) - 1) and set B has nodes len(aseq) to (len(bseq) - 1).
224
+ Nodes from set A are connected to nodes in the set B by connecting
225
+ the highest degree nodes in set A to the lowest degree nodes in
226
+ set B until all stubs are connected.
227
+
228
+ Parameters
229
+ ----------
230
+ aseq : list
231
+ Degree sequence for node set A.
232
+ bseq : list
233
+ Degree sequence for node set B.
234
+ create_using : NetworkX graph instance, optional
235
+ Return graph of this type.
236
+
237
+ Notes
238
+ -----
239
+ The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
240
+ If no graph type is specified use MultiGraph with parallel edges.
241
+ If you want a graph with no parallel edges use create_using=Graph()
242
+ but then the resulting degree sequences might not be exact.
243
+
244
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
245
+ to indicate which bipartite set the node belongs to.
246
+
247
+ This function is not imported in the main namespace.
248
+ To use it use nx.bipartite.reverse_havel_hakimi_graph
249
+ """
250
+ G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
251
+ if G.is_directed():
252
+ raise nx.NetworkXError("Directed Graph not supported")
253
+
254
+ # length of the each sequence
255
+ lena = len(aseq)
256
+ lenb = len(bseq)
257
+ suma = sum(aseq)
258
+ sumb = sum(bseq)
259
+
260
+ if not suma == sumb:
261
+ raise nx.NetworkXError(
262
+ f"invalid degree sequences, sum(aseq)!=sum(bseq),{suma},{sumb}"
263
+ )
264
+
265
+ G = _add_nodes_with_bipartite_label(G, lena, lenb)
266
+
267
+ if len(aseq) == 0 or max(aseq) == 0:
268
+ return G # done if no edges
269
+
270
+ # build list of degree-repeated vertex numbers
271
+ astubs = [[aseq[v], v] for v in range(lena)]
272
+ bstubs = [[bseq[v - lena], v] for v in range(lena, lena + lenb)]
273
+ astubs.sort()
274
+ bstubs.sort()
275
+ while astubs:
276
+ (degree, u) = astubs.pop() # take of largest degree node in the a set
277
+ if degree == 0:
278
+ break # done, all are zero
279
+ # connect the source to the smallest degree nodes in the b set
280
+ for target in bstubs[0:degree]:
281
+ v = target[1]
282
+ G.add_edge(u, v)
283
+ target[0] -= 1 # note this updates bstubs too.
284
+ if target[0] == 0:
285
+ bstubs.remove(target)
286
+
287
+ G.name = "bipartite_reverse_havel_hakimi_graph"
288
+ return G
289
+
290
+
291
+ @nx._dispatchable(graphs=None, returns_graph=True)
292
+ def alternating_havel_hakimi_graph(aseq, bseq, create_using=None):
293
+ """Returns a bipartite graph from two given degree sequences using
294
+ an alternating Havel-Hakimi style construction.
295
+
296
+ The graph is composed of two partitions. Set A has nodes 0 to
297
+ (len(aseq) - 1) and set B has nodes len(aseq) to (len(bseq) - 1).
298
+ Nodes from the set A are connected to nodes in the set B by
299
+ connecting the highest degree nodes in set A to alternatively the
300
+ highest and the lowest degree nodes in set B until all stubs are
301
+ connected.
302
+
303
+ Parameters
304
+ ----------
305
+ aseq : list
306
+ Degree sequence for node set A.
307
+ bseq : list
308
+ Degree sequence for node set B.
309
+ create_using : NetworkX graph instance, optional
310
+ Return graph of this type.
311
+
312
+ Notes
313
+ -----
314
+ The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
315
+ If no graph type is specified use MultiGraph with parallel edges.
316
+ If you want a graph with no parallel edges use create_using=Graph()
317
+ but then the resulting degree sequences might not be exact.
318
+
319
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
320
+ to indicate which bipartite set the node belongs to.
321
+
322
+ This function is not imported in the main namespace.
323
+ To use it use nx.bipartite.alternating_havel_hakimi_graph
324
+ """
325
+ G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
326
+ if G.is_directed():
327
+ raise nx.NetworkXError("Directed Graph not supported")
328
+
329
+ # length of the each sequence
330
+ naseq = len(aseq)
331
+ nbseq = len(bseq)
332
+ suma = sum(aseq)
333
+ sumb = sum(bseq)
334
+
335
+ if not suma == sumb:
336
+ raise nx.NetworkXError(
337
+ f"invalid degree sequences, sum(aseq)!=sum(bseq),{suma},{sumb}"
338
+ )
339
+
340
+ G = _add_nodes_with_bipartite_label(G, naseq, nbseq)
341
+
342
+ if len(aseq) == 0 or max(aseq) == 0:
343
+ return G # done if no edges
344
+ # build list of degree-repeated vertex numbers
345
+ astubs = [[aseq[v], v] for v in range(naseq)]
346
+ bstubs = [[bseq[v - naseq], v] for v in range(naseq, naseq + nbseq)]
347
+ while astubs:
348
+ astubs.sort()
349
+ (degree, u) = astubs.pop() # take of largest degree node in the a set
350
+ if degree == 0:
351
+ break # done, all are zero
352
+ bstubs.sort()
353
+ small = bstubs[0 : degree // 2] # add these low degree targets
354
+ large = bstubs[(-degree + degree // 2) :] # now high degree targets
355
+ stubs = [x for z in zip(large, small) for x in z] # combine, sorry
356
+ if len(stubs) < len(small) + len(large): # check for zip truncation
357
+ stubs.append(large.pop())
358
+ for target in stubs:
359
+ v = target[1]
360
+ G.add_edge(u, v)
361
+ target[0] -= 1 # note this updates bstubs too.
362
+ if target[0] == 0:
363
+ bstubs.remove(target)
364
+
365
+ G.name = "bipartite_alternating_havel_hakimi_graph"
366
+ return G
367
+
368
+
369
+ @py_random_state(3)
370
+ @nx._dispatchable(graphs=None, returns_graph=True)
371
+ def preferential_attachment_graph(aseq, p, create_using=None, seed=None):
372
+ """Create a bipartite graph with a preferential attachment model from
373
+ a given single degree sequence.
374
+
375
+ The graph is composed of two partitions. Set A has nodes 0 to
376
+ (len(aseq) - 1) and set B has nodes starting with node len(aseq).
377
+ The number of nodes in set B is random.
378
+
379
+ Parameters
380
+ ----------
381
+ aseq : list
382
+ Degree sequence for node set A.
383
+ p : float
384
+ Probability that a new bottom node is added.
385
+ create_using : NetworkX graph instance, optional
386
+ Return graph of this type.
387
+ seed : integer, random_state, or None (default)
388
+ Indicator of random number generation state.
389
+ See :ref:`Randomness<randomness>`.
390
+
391
+ References
392
+ ----------
393
+ .. [1] Guillaume, J.L. and Latapy, M.,
394
+ Bipartite graphs as models of complex networks.
395
+ Physica A: Statistical Mechanics and its Applications,
396
+ 2006, 371(2), pp.795-813.
397
+ .. [2] Jean-Loup Guillaume and Matthieu Latapy,
398
+ Bipartite structure of all complex networks,
399
+ Inf. Process. Lett. 90, 2004, pg. 215-221
400
+ https://doi.org/10.1016/j.ipl.2004.03.007
401
+
402
+ Notes
403
+ -----
404
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
405
+ to indicate which bipartite set the node belongs to.
406
+
407
+ This function is not imported in the main namespace.
408
+ To use it use nx.bipartite.preferential_attachment_graph
409
+ """
410
+ G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
411
+ if G.is_directed():
412
+ raise nx.NetworkXError("Directed Graph not supported")
413
+
414
+ if p > 1:
415
+ raise nx.NetworkXError(f"probability {p} > 1")
416
+
417
+ naseq = len(aseq)
418
+ G = _add_nodes_with_bipartite_label(G, naseq, 0)
419
+ vv = [[v] * aseq[v] for v in range(naseq)]
420
+ while vv:
421
+ while vv[0]:
422
+ source = vv[0][0]
423
+ vv[0].remove(source)
424
+ if seed.random() < p or len(G) == naseq:
425
+ target = len(G)
426
+ G.add_node(target, bipartite=1)
427
+ G.add_edge(source, target)
428
+ else:
429
+ bb = [[b] * G.degree(b) for b in range(naseq, len(G))]
430
+ # flatten the list of lists into a list.
431
+ bbstubs = reduce(lambda x, y: x + y, bb)
432
+ # choose preferentially a bottom node.
433
+ target = seed.choice(bbstubs)
434
+ G.add_node(target, bipartite=1)
435
+ G.add_edge(source, target)
436
+ vv.remove(vv[0])
437
+ G.name = "bipartite_preferential_attachment_model"
438
+ return G
439
+
440
+
441
+ @py_random_state(3)
442
+ @nx._dispatchable(graphs=None, returns_graph=True)
443
+ def random_graph(n, m, p, seed=None, directed=False):
444
+ """Returns a bipartite random graph.
445
+
446
+ This is a bipartite version of the binomial (Erdős-Rényi) graph.
447
+ The graph is composed of two partitions. Set A has nodes 0 to
448
+ (n - 1) and set B has nodes n to (n + m - 1).
449
+
450
+ Parameters
451
+ ----------
452
+ n : int
453
+ The number of nodes in the first bipartite set.
454
+ m : int
455
+ The number of nodes in the second bipartite set.
456
+ p : float
457
+ Probability for edge creation.
458
+ seed : integer, random_state, or None (default)
459
+ Indicator of random number generation state.
460
+ See :ref:`Randomness<randomness>`.
461
+ directed : bool, optional (default=False)
462
+ If True return a directed graph
463
+
464
+ Notes
465
+ -----
466
+ The bipartite random graph algorithm chooses each of the n*m (undirected)
467
+ or 2*nm (directed) possible edges with probability p.
468
+
469
+ This algorithm is $O(n+m)$ where $m$ is the expected number of edges.
470
+
471
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
472
+ to indicate which bipartite set the node belongs to.
473
+
474
+ This function is not imported in the main namespace.
475
+ To use it use nx.bipartite.random_graph
476
+
477
+ See Also
478
+ --------
479
+ gnp_random_graph, configuration_model
480
+
481
+ References
482
+ ----------
483
+ .. [1] Vladimir Batagelj and Ulrik Brandes,
484
+ "Efficient generation of large random networks",
485
+ Phys. Rev. E, 71, 036113, 2005.
486
+ """
487
+ G = nx.Graph()
488
+ G = _add_nodes_with_bipartite_label(G, n, m)
489
+ if directed:
490
+ G = nx.DiGraph(G)
491
+ G.name = f"fast_gnp_random_graph({n},{m},{p})"
492
+
493
+ if p <= 0:
494
+ return G
495
+ if p >= 1:
496
+ return nx.complete_bipartite_graph(n, m)
497
+
498
+ lp = math.log(1.0 - p)
499
+
500
+ v = 0
501
+ w = -1
502
+ while v < n:
503
+ lr = math.log(1.0 - seed.random())
504
+ w = w + 1 + int(lr / lp)
505
+ while w >= m and v < n:
506
+ w = w - m
507
+ v = v + 1
508
+ if v < n:
509
+ G.add_edge(v, n + w)
510
+
511
+ if directed:
512
+ # use the same algorithm to
513
+ # add edges from the "m" to "n" set
514
+ v = 0
515
+ w = -1
516
+ while v < n:
517
+ lr = math.log(1.0 - seed.random())
518
+ w = w + 1 + int(lr / lp)
519
+ while w >= m and v < n:
520
+ w = w - m
521
+ v = v + 1
522
+ if v < n:
523
+ G.add_edge(n + w, v)
524
+
525
+ return G
526
+
527
+
528
+ @py_random_state(3)
529
+ @nx._dispatchable(graphs=None, returns_graph=True)
530
+ def gnmk_random_graph(n, m, k, seed=None, directed=False):
531
+ """Returns a random bipartite graph G_{n,m,k}.
532
+
533
+ Produces a bipartite graph chosen randomly out of the set of all graphs
534
+ with n top nodes, m bottom nodes, and k edges.
535
+ The graph is composed of two sets of nodes.
536
+ Set A has nodes 0 to (n - 1) and set B has nodes n to (n + m - 1).
537
+
538
+ Parameters
539
+ ----------
540
+ n : int
541
+ The number of nodes in the first bipartite set.
542
+ m : int
543
+ The number of nodes in the second bipartite set.
544
+ k : int
545
+ The number of edges
546
+ seed : integer, random_state, or None (default)
547
+ Indicator of random number generation state.
548
+ See :ref:`Randomness<randomness>`.
549
+ directed : bool, optional (default=False)
550
+ If True return a directed graph
551
+
552
+ Examples
553
+ --------
554
+ >>> G = nx.bipartite.gnmk_random_graph(10, 20, 50)
555
+
556
+ See Also
557
+ --------
558
+ gnm_random_graph
559
+
560
+ Notes
561
+ -----
562
+ If k > m * n then a complete bipartite graph is returned.
563
+
564
+ This graph is a bipartite version of the `G_{nm}` random graph model.
565
+
566
+ The nodes are assigned the attribute 'bipartite' with the value 0 or 1
567
+ to indicate which bipartite set the node belongs to.
568
+
569
+ This function is not imported in the main namespace.
570
+ To use it use nx.bipartite.gnmk_random_graph
571
+ """
572
+ G = nx.Graph()
573
+ G = _add_nodes_with_bipartite_label(G, n, m)
574
+ if directed:
575
+ G = nx.DiGraph(G)
576
+ G.name = f"bipartite_gnm_random_graph({n},{m},{k})"
577
+ if n == 1 or m == 1:
578
+ return G
579
+ max_edges = n * m # max_edges for bipartite networks
580
+ if k >= max_edges: # Maybe we should raise an exception here
581
+ return nx.complete_bipartite_graph(n, m, create_using=G)
582
+
583
+ top = [n for n, d in G.nodes(data=True) if d["bipartite"] == 0]
584
+ bottom = list(set(G) - set(top))
585
+ edge_count = 0
586
+ while edge_count < k:
587
+ # generate random edge,u,v
588
+ u = seed.choice(top)
589
+ v = seed.choice(bottom)
590
+ if v in G[u]:
591
+ continue
592
+ else:
593
+ G.add_edge(u, v)
594
+ edge_count += 1
595
+ return G
596
+
597
+
598
+ def _add_nodes_with_bipartite_label(G, lena, lenb):
599
+ G.add_nodes_from(range(lena + lenb))
600
+ b = dict(zip(range(lena), [0] * lena))
601
+ b.update(dict(zip(range(lena, lena + lenb), [1] * lenb)))
602
+ nx.set_node_attributes(G, b, "bipartite")
603
+ return G