koichi12 commited on
Commit
2bdfed7
·
verified ·
1 Parent(s): 558d3d0

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. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/__init__.py +132 -0
  2. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__init__.py +24 -0
  3. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/clique.cpython-311.pyc +0 -0
  4. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/clustering_coefficient.cpython-311.pyc +0 -0
  5. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/dominating_set.cpython-311.pyc +0 -0
  6. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/kcomponents.cpython-311.pyc +0 -0
  7. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/traveling_salesman.cpython-311.pyc +0 -0
  8. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/clustering_coefficient.py +66 -0
  9. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/dominating_set.py +126 -0
  10. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/ramsey.py +52 -0
  11. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  12. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_approx_clust_coeff.cpython-311.pyc +0 -0
  13. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_dominating_set.cpython-311.pyc +0 -0
  14. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_kcomponents.cpython-311.pyc +0 -0
  15. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_matching.cpython-311.pyc +0 -0
  16. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +41 -0
  17. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_clique.py +113 -0
  18. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_distance_measures.py +60 -0
  19. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_maxcut.py +82 -0
  20. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_vertex_cover.py +68 -0
  21. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/asteroidal.py +170 -0
  22. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/bridges.py +205 -0
  23. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/chordal.py +440 -0
  24. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/cluster.py +605 -0
  25. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/centrality.py +171 -0
  26. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/louvain.py +373 -0
  27. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__init__.py +0 -0
  28. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  29. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/dag.py +1258 -0
  30. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/distance_regular.py +234 -0
  31. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__init__.py +11 -0
  32. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/__init__.cpython-311.pyc +0 -0
  33. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/edmondskarp.cpython-311.pyc +0 -0
  34. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/maxflow.cpython-311.pyc +0 -0
  35. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/edmondskarp.py +250 -0
  36. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/mincost.py +335 -0
  37. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/shortestaugmentingpath.py +304 -0
  38. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/test_mincost.py +476 -0
  39. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/graph_hashing.py +313 -0
  40. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/graphical.py +483 -0
  41. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/hierarchy.py +48 -0
  42. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/isolate.py +107 -0
  43. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/test_pagerank.cpython-311.pyc +0 -0
  44. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/test_hits.py +78 -0
  45. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/lowest_common_ancestors.py +268 -0
  46. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/__pycache__/contraction.cpython-311.pyc +0 -0
  47. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/tests/test_contraction.py +445 -0
  48. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/mis.py +77 -0
  49. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/moral.py +59 -0
  50. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/node_classification.py +218 -0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/__init__.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from networkx.algorithms.assortativity import *
2
+ from networkx.algorithms.asteroidal import *
3
+ from networkx.algorithms.boundary import *
4
+ from networkx.algorithms.bridges import *
5
+ from networkx.algorithms.chains import *
6
+ from networkx.algorithms.centrality import *
7
+ from networkx.algorithms.chordal import *
8
+ from networkx.algorithms.cluster import *
9
+ from networkx.algorithms.clique import *
10
+ from networkx.algorithms.communicability_alg import *
11
+ from networkx.algorithms.components import *
12
+ from networkx.algorithms.coloring import *
13
+ from networkx.algorithms.core import *
14
+ from networkx.algorithms.covering import *
15
+ from networkx.algorithms.cycles import *
16
+ from networkx.algorithms.cuts import *
17
+ from networkx.algorithms.d_separation import *
18
+ from networkx.algorithms.dag import *
19
+ from networkx.algorithms.distance_measures import *
20
+ from networkx.algorithms.distance_regular import *
21
+ from networkx.algorithms.dominance import *
22
+ from networkx.algorithms.dominating import *
23
+ from networkx.algorithms.efficiency_measures import *
24
+ from networkx.algorithms.euler import *
25
+ from networkx.algorithms.graphical import *
26
+ from networkx.algorithms.hierarchy import *
27
+ from networkx.algorithms.hybrid import *
28
+ from networkx.algorithms.link_analysis import *
29
+ from networkx.algorithms.link_prediction import *
30
+ from networkx.algorithms.lowest_common_ancestors import *
31
+ from networkx.algorithms.isolate import *
32
+ from networkx.algorithms.matching import *
33
+ from networkx.algorithms.minors import *
34
+ from networkx.algorithms.mis import *
35
+ from networkx.algorithms.moral import *
36
+ from networkx.algorithms.non_randomness import *
37
+ from networkx.algorithms.operators import *
38
+ from networkx.algorithms.planarity import *
39
+ from networkx.algorithms.planar_drawing import *
40
+ from networkx.algorithms.reciprocity import *
41
+ from networkx.algorithms.regular import *
42
+ from networkx.algorithms.richclub import *
43
+ from networkx.algorithms.shortest_paths import *
44
+ from networkx.algorithms.similarity import *
45
+ from networkx.algorithms.graph_hashing import *
46
+ from networkx.algorithms.simple_paths import *
47
+ from networkx.algorithms.smallworld import *
48
+ from networkx.algorithms.smetric import *
49
+ from networkx.algorithms.structuralholes import *
50
+ from networkx.algorithms.sparsifiers import *
51
+ from networkx.algorithms.summarization import *
52
+ from networkx.algorithms.swap import *
53
+ from networkx.algorithms.time_dependent import *
54
+ from networkx.algorithms.traversal import *
55
+ from networkx.algorithms.triads import *
56
+ from networkx.algorithms.vitality import *
57
+ from networkx.algorithms.voronoi import *
58
+ from networkx.algorithms.walks import *
59
+ from networkx.algorithms.wiener import *
60
+ from networkx.algorithms.polynomials import *
61
+
62
+ # Make certain subpackages available to the user as direct imports from
63
+ # the `networkx` namespace.
64
+ from networkx.algorithms import approximation
65
+ from networkx.algorithms import assortativity
66
+ from networkx.algorithms import bipartite
67
+ from networkx.algorithms import node_classification
68
+ from networkx.algorithms import centrality
69
+ from networkx.algorithms import chordal
70
+ from networkx.algorithms import cluster
71
+ from networkx.algorithms import clique
72
+ from networkx.algorithms import components
73
+ from networkx.algorithms import connectivity
74
+ from networkx.algorithms import community
75
+ from networkx.algorithms import coloring
76
+ from networkx.algorithms import flow
77
+ from networkx.algorithms import isomorphism
78
+ from networkx.algorithms import link_analysis
79
+ from networkx.algorithms import lowest_common_ancestors
80
+ from networkx.algorithms import operators
81
+ from networkx.algorithms import shortest_paths
82
+ from networkx.algorithms import tournament
83
+ from networkx.algorithms import traversal
84
+ from networkx.algorithms import tree
85
+
86
+ # Make certain functions from some of the previous subpackages available
87
+ # to the user as direct imports from the `networkx` namespace.
88
+ from networkx.algorithms.bipartite import complete_bipartite_graph
89
+ from networkx.algorithms.bipartite import is_bipartite
90
+ from networkx.algorithms.bipartite import projected_graph
91
+ from networkx.algorithms.connectivity import all_pairs_node_connectivity
92
+ from networkx.algorithms.connectivity import all_node_cuts
93
+ from networkx.algorithms.connectivity import average_node_connectivity
94
+ from networkx.algorithms.connectivity import edge_connectivity
95
+ from networkx.algorithms.connectivity import edge_disjoint_paths
96
+ from networkx.algorithms.connectivity import k_components
97
+ from networkx.algorithms.connectivity import k_edge_components
98
+ from networkx.algorithms.connectivity import k_edge_subgraphs
99
+ from networkx.algorithms.connectivity import k_edge_augmentation
100
+ from networkx.algorithms.connectivity import is_k_edge_connected
101
+ from networkx.algorithms.connectivity import minimum_edge_cut
102
+ from networkx.algorithms.connectivity import minimum_node_cut
103
+ from networkx.algorithms.connectivity import node_connectivity
104
+ from networkx.algorithms.connectivity import node_disjoint_paths
105
+ from networkx.algorithms.connectivity import stoer_wagner
106
+ from networkx.algorithms.flow import capacity_scaling
107
+ from networkx.algorithms.flow import cost_of_flow
108
+ from networkx.algorithms.flow import gomory_hu_tree
109
+ from networkx.algorithms.flow import max_flow_min_cost
110
+ from networkx.algorithms.flow import maximum_flow
111
+ from networkx.algorithms.flow import maximum_flow_value
112
+ from networkx.algorithms.flow import min_cost_flow
113
+ from networkx.algorithms.flow import min_cost_flow_cost
114
+ from networkx.algorithms.flow import minimum_cut
115
+ from networkx.algorithms.flow import minimum_cut_value
116
+ from networkx.algorithms.flow import network_simplex
117
+ from networkx.algorithms.isomorphism import could_be_isomorphic
118
+ from networkx.algorithms.isomorphism import fast_could_be_isomorphic
119
+ from networkx.algorithms.isomorphism import faster_could_be_isomorphic
120
+ from networkx.algorithms.isomorphism import is_isomorphic
121
+ from networkx.algorithms.isomorphism.vf2pp import *
122
+ from networkx.algorithms.tree.branchings import maximum_branching
123
+ from networkx.algorithms.tree.branchings import maximum_spanning_arborescence
124
+ from networkx.algorithms.tree.branchings import minimum_branching
125
+ from networkx.algorithms.tree.branchings import minimum_spanning_arborescence
126
+ from networkx.algorithms.tree.branchings import ArborescenceIterator
127
+ from networkx.algorithms.tree.coding import *
128
+ from networkx.algorithms.tree.decomposition import *
129
+ from networkx.algorithms.tree.mst import *
130
+ from networkx.algorithms.tree.operations import *
131
+ from networkx.algorithms.tree.recognition import *
132
+ from networkx.algorithms.tournament import is_tournament
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Approximations of graph properties and Heuristic methods for optimization.
2
+
3
+ The functions in this class are not imported into the top-level ``networkx``
4
+ namespace so the easiest way to use them is with::
5
+
6
+ >>> from networkx.algorithms import approximation
7
+
8
+ Another option is to import the specific function with
9
+ ``from networkx.algorithms.approximation import function_name``.
10
+
11
+ """
12
+ from networkx.algorithms.approximation.clustering_coefficient import *
13
+ from networkx.algorithms.approximation.clique import *
14
+ from networkx.algorithms.approximation.connectivity import *
15
+ from networkx.algorithms.approximation.distance_measures import *
16
+ from networkx.algorithms.approximation.dominating_set import *
17
+ from networkx.algorithms.approximation.kcomponents import *
18
+ from networkx.algorithms.approximation.matching import *
19
+ from networkx.algorithms.approximation.ramsey import *
20
+ from networkx.algorithms.approximation.steinertree import *
21
+ from networkx.algorithms.approximation.traveling_salesman import *
22
+ from networkx.algorithms.approximation.treewidth import *
23
+ from networkx.algorithms.approximation.vertex_cover import *
24
+ from networkx.algorithms.approximation.maxcut import *
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/clique.cpython-311.pyc ADDED
Binary file (9.8 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/clustering_coefficient.cpython-311.pyc ADDED
Binary file (3.16 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/dominating_set.cpython-311.pyc ADDED
Binary file (4.96 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/kcomponents.cpython-311.pyc ADDED
Binary file (20.5 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/traveling_salesman.cpython-311.pyc ADDED
Binary file (62.5 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/clustering_coefficient.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import networkx as nx
2
+ from networkx.utils import not_implemented_for, py_random_state
3
+
4
+ __all__ = ["average_clustering"]
5
+
6
+
7
+ @not_implemented_for("directed")
8
+ @py_random_state(2)
9
+ @nx._dispatch(name="approximate_average_clustering")
10
+ def average_clustering(G, trials=1000, seed=None):
11
+ r"""Estimates the average clustering coefficient of G.
12
+
13
+ The local clustering of each node in `G` is the fraction of triangles
14
+ that actually exist over all possible triangles in its neighborhood.
15
+ The average clustering coefficient of a graph `G` is the mean of
16
+ local clusterings.
17
+
18
+ This function finds an approximate average clustering coefficient
19
+ for G by repeating `n` times (defined in `trials`) the following
20
+ experiment: choose a node at random, choose two of its neighbors
21
+ at random, and check if they are connected. The approximate
22
+ coefficient is the fraction of triangles found over the number
23
+ of trials [1]_.
24
+
25
+ Parameters
26
+ ----------
27
+ G : NetworkX graph
28
+
29
+ trials : integer
30
+ Number of trials to perform (default 1000).
31
+
32
+ seed : integer, random_state, or None (default)
33
+ Indicator of random number generation state.
34
+ See :ref:`Randomness<randomness>`.
35
+
36
+ Returns
37
+ -------
38
+ c : float
39
+ Approximated average clustering coefficient.
40
+
41
+ Examples
42
+ --------
43
+ >>> from networkx.algorithms import approximation
44
+ >>> G = nx.erdos_renyi_graph(10, 0.2, seed=10)
45
+ >>> approximation.average_clustering(G, trials=1000, seed=10)
46
+ 0.214
47
+
48
+ References
49
+ ----------
50
+ .. [1] Schank, Thomas, and Dorothea Wagner. Approximating clustering
51
+ coefficient and transitivity. Universität Karlsruhe, Fakultät für
52
+ Informatik, 2004.
53
+ https://doi.org/10.5445/IR/1000001239
54
+
55
+ """
56
+ n = len(G)
57
+ triangles = 0
58
+ nodes = list(G)
59
+ for i in [int(seed.random() * n) for i in range(trials)]:
60
+ nbrs = list(G[nodes[i]])
61
+ if len(nbrs) < 2:
62
+ continue
63
+ u, v = seed.sample(nbrs, 2)
64
+ if u in G[v]:
65
+ triangles += 1
66
+ return triangles / trials
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/dominating_set.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions for finding node and edge dominating sets.
2
+
3
+ A `dominating set`_ for an undirected graph *G* with vertex set *V*
4
+ and edge set *E* is a subset *D* of *V* such that every vertex not in
5
+ *D* is adjacent to at least one member of *D*. An `edge dominating set`_
6
+ is a subset *F* of *E* such that every edge not in *F* is
7
+ incident to an endpoint of at least one edge in *F*.
8
+
9
+ .. _dominating set: https://en.wikipedia.org/wiki/Dominating_set
10
+ .. _edge dominating set: https://en.wikipedia.org/wiki/Edge_dominating_set
11
+
12
+ """
13
+ import networkx as nx
14
+
15
+ from ...utils import not_implemented_for
16
+ from ..matching import maximal_matching
17
+
18
+ __all__ = ["min_weighted_dominating_set", "min_edge_dominating_set"]
19
+
20
+
21
+ # TODO Why doesn't this algorithm work for directed graphs?
22
+ @not_implemented_for("directed")
23
+ @nx._dispatch(node_attrs="weight")
24
+ def min_weighted_dominating_set(G, weight=None):
25
+ r"""Returns a dominating set that approximates the minimum weight node
26
+ dominating set.
27
+
28
+ Parameters
29
+ ----------
30
+ G : NetworkX graph
31
+ Undirected graph.
32
+
33
+ weight : string
34
+ The node attribute storing the weight of an node. If provided,
35
+ the node attribute with this key must be a number for each
36
+ node. If not provided, each node is assumed to have weight one.
37
+
38
+ Returns
39
+ -------
40
+ min_weight_dominating_set : set
41
+ A set of nodes, the sum of whose weights is no more than `(\log
42
+ w(V)) w(V^*)`, where `w(V)` denotes the sum of the weights of
43
+ each node in the graph and `w(V^*)` denotes the sum of the
44
+ weights of each node in the minimum weight dominating set.
45
+
46
+ Notes
47
+ -----
48
+ This algorithm computes an approximate minimum weighted dominating
49
+ set for the graph `G`. The returned solution has weight `(\log
50
+ w(V)) w(V^*)`, where `w(V)` denotes the sum of the weights of each
51
+ node in the graph and `w(V^*)` denotes the sum of the weights of
52
+ each node in the minimum weight dominating set for the graph.
53
+
54
+ This implementation of the algorithm runs in $O(m)$ time, where $m$
55
+ is the number of edges in the graph.
56
+
57
+ References
58
+ ----------
59
+ .. [1] Vazirani, Vijay V.
60
+ *Approximation Algorithms*.
61
+ Springer Science & Business Media, 2001.
62
+
63
+ """
64
+ # The unique dominating set for the null graph is the empty set.
65
+ if len(G) == 0:
66
+ return set()
67
+
68
+ # This is the dominating set that will eventually be returned.
69
+ dom_set = set()
70
+
71
+ def _cost(node_and_neighborhood):
72
+ """Returns the cost-effectiveness of greedily choosing the given
73
+ node.
74
+
75
+ `node_and_neighborhood` is a two-tuple comprising a node and its
76
+ closed neighborhood.
77
+
78
+ """
79
+ v, neighborhood = node_and_neighborhood
80
+ return G.nodes[v].get(weight, 1) / len(neighborhood - dom_set)
81
+
82
+ # This is a set of all vertices not already covered by the
83
+ # dominating set.
84
+ vertices = set(G)
85
+ # This is a dictionary mapping each node to the closed neighborhood
86
+ # of that node.
87
+ neighborhoods = {v: {v} | set(G[v]) for v in G}
88
+
89
+ # Continue until all vertices are adjacent to some node in the
90
+ # dominating set.
91
+ while vertices:
92
+ # Find the most cost-effective node to add, along with its
93
+ # closed neighborhood.
94
+ dom_node, min_set = min(neighborhoods.items(), key=_cost)
95
+ # Add the node to the dominating set and reduce the remaining
96
+ # set of nodes to cover.
97
+ dom_set.add(dom_node)
98
+ del neighborhoods[dom_node]
99
+ vertices -= min_set
100
+
101
+ return dom_set
102
+
103
+
104
+ @nx._dispatch
105
+ def min_edge_dominating_set(G):
106
+ r"""Returns minimum cardinality edge dominating set.
107
+
108
+ Parameters
109
+ ----------
110
+ G : NetworkX graph
111
+ Undirected graph
112
+
113
+ Returns
114
+ -------
115
+ min_edge_dominating_set : set
116
+ Returns a set of dominating edges whose size is no more than 2 * OPT.
117
+
118
+ Notes
119
+ -----
120
+ The algorithm computes an approximate solution to the edge dominating set
121
+ problem. The result is no more than 2 * OPT in terms of size of the set.
122
+ Runtime of the algorithm is $O(|E|)$.
123
+ """
124
+ if not G:
125
+ raise ValueError("Expected non-empty NetworkX graph!")
126
+ return maximal_matching(G)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/ramsey.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Ramsey numbers.
3
+ """
4
+ import networkx as nx
5
+ from networkx.utils import not_implemented_for
6
+
7
+ from ...utils import arbitrary_element
8
+
9
+ __all__ = ["ramsey_R2"]
10
+
11
+
12
+ @not_implemented_for("directed")
13
+ @not_implemented_for("multigraph")
14
+ @nx._dispatch
15
+ def ramsey_R2(G):
16
+ r"""Compute the largest clique and largest independent set in `G`.
17
+
18
+ This can be used to estimate bounds for the 2-color
19
+ Ramsey number `R(2;s,t)` for `G`.
20
+
21
+ This is a recursive implementation which could run into trouble
22
+ for large recursions. Note that self-loop edges are ignored.
23
+
24
+ Parameters
25
+ ----------
26
+ G : NetworkX graph
27
+ Undirected graph
28
+
29
+ Returns
30
+ -------
31
+ max_pair : (set, set) tuple
32
+ Maximum clique, Maximum independent set.
33
+
34
+ Raises
35
+ ------
36
+ NetworkXNotImplemented
37
+ If the graph is directed or is a multigraph.
38
+ """
39
+ if not G:
40
+ return set(), set()
41
+
42
+ node = arbitrary_element(G)
43
+ nbrs = (nbr for nbr in nx.all_neighbors(G, node) if nbr != node)
44
+ nnbrs = nx.non_neighbors(G, node)
45
+ c_1, i_1 = ramsey_R2(G.subgraph(nbrs).copy())
46
+ c_2, i_2 = ramsey_R2(G.subgraph(nnbrs).copy())
47
+
48
+ c_1.add(node)
49
+ i_2.add(node)
50
+ # Choose the larger of the two cliques and the larger of the two
51
+ # independent sets, according to cardinality.
52
+ return max(c_1, c_2, key=len), max(i_1, i_2, key=len)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (240 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_approx_clust_coeff.cpython-311.pyc ADDED
Binary file (2.77 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_dominating_set.cpython-311.pyc ADDED
Binary file (4.47 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_kcomponents.cpython-311.pyc ADDED
Binary file (20.7 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_matching.cpython-311.pyc ADDED
Binary file (736 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import networkx as nx
2
+ from networkx.algorithms.approximation import average_clustering
3
+
4
+ # This approximation has to be exact in regular graphs
5
+ # with no triangles or with all possible triangles.
6
+
7
+
8
+ def test_petersen():
9
+ # Actual coefficient is 0
10
+ G = nx.petersen_graph()
11
+ assert average_clustering(G, trials=len(G) // 2) == nx.average_clustering(G)
12
+
13
+
14
+ def test_petersen_seed():
15
+ # Actual coefficient is 0
16
+ G = nx.petersen_graph()
17
+ assert average_clustering(G, trials=len(G) // 2, seed=1) == nx.average_clustering(G)
18
+
19
+
20
+ def test_tetrahedral():
21
+ # Actual coefficient is 1
22
+ G = nx.tetrahedral_graph()
23
+ assert average_clustering(G, trials=len(G) // 2) == nx.average_clustering(G)
24
+
25
+
26
+ def test_dodecahedral():
27
+ # Actual coefficient is 0
28
+ G = nx.dodecahedral_graph()
29
+ assert average_clustering(G, trials=len(G) // 2) == nx.average_clustering(G)
30
+
31
+
32
+ def test_empty():
33
+ G = nx.empty_graph(5)
34
+ assert average_clustering(G, trials=len(G) // 2) == 0
35
+
36
+
37
+ def test_complete():
38
+ G = nx.complete_graph(5)
39
+ assert average_clustering(G, trials=len(G) // 2) == 1
40
+ G = nx.complete_graph(7)
41
+ assert average_clustering(G, trials=len(G) // 2) == 1
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_clique.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.approximation.clique` module."""
2
+
3
+
4
+ import networkx as nx
5
+ from networkx.algorithms.approximation import (
6
+ clique_removal,
7
+ large_clique_size,
8
+ max_clique,
9
+ maximum_independent_set,
10
+ )
11
+
12
+
13
+ def is_independent_set(G, nodes):
14
+ """Returns True if and only if `nodes` is a clique in `G`.
15
+
16
+ `G` is a NetworkX graph. `nodes` is an iterable of nodes in
17
+ `G`.
18
+
19
+ """
20
+ return G.subgraph(nodes).number_of_edges() == 0
21
+
22
+
23
+ def is_clique(G, nodes):
24
+ """Returns True if and only if `nodes` is an independent set
25
+ in `G`.
26
+
27
+ `G` is an undirected simple graph. `nodes` is an iterable of
28
+ nodes in `G`.
29
+
30
+ """
31
+ H = G.subgraph(nodes)
32
+ n = len(H)
33
+ return H.number_of_edges() == n * (n - 1) // 2
34
+
35
+
36
+ class TestCliqueRemoval:
37
+ """Unit tests for the
38
+ :func:`~networkx.algorithms.approximation.clique_removal` function.
39
+
40
+ """
41
+
42
+ def test_trivial_graph(self):
43
+ G = nx.trivial_graph()
44
+ independent_set, cliques = clique_removal(G)
45
+ assert is_independent_set(G, independent_set)
46
+ assert all(is_clique(G, clique) for clique in cliques)
47
+ # In fact, we should only have 1-cliques, that is, singleton nodes.
48
+ assert all(len(clique) == 1 for clique in cliques)
49
+
50
+ def test_complete_graph(self):
51
+ G = nx.complete_graph(10)
52
+ independent_set, cliques = clique_removal(G)
53
+ assert is_independent_set(G, independent_set)
54
+ assert all(is_clique(G, clique) for clique in cliques)
55
+
56
+ def test_barbell_graph(self):
57
+ G = nx.barbell_graph(10, 5)
58
+ independent_set, cliques = clique_removal(G)
59
+ assert is_independent_set(G, independent_set)
60
+ assert all(is_clique(G, clique) for clique in cliques)
61
+
62
+
63
+ class TestMaxClique:
64
+ """Unit tests for the :func:`networkx.algorithms.approximation.max_clique`
65
+ function.
66
+
67
+ """
68
+
69
+ def test_null_graph(self):
70
+ G = nx.null_graph()
71
+ assert len(max_clique(G)) == 0
72
+
73
+ def test_complete_graph(self):
74
+ graph = nx.complete_graph(30)
75
+ # this should return the entire graph
76
+ mc = max_clique(graph)
77
+ assert 30 == len(mc)
78
+
79
+ def test_maximal_by_cardinality(self):
80
+ """Tests that the maximal clique is computed according to maximum
81
+ cardinality of the sets.
82
+
83
+ For more information, see pull request #1531.
84
+
85
+ """
86
+ G = nx.complete_graph(5)
87
+ G.add_edge(4, 5)
88
+ clique = max_clique(G)
89
+ assert len(clique) > 1
90
+
91
+ G = nx.lollipop_graph(30, 2)
92
+ clique = max_clique(G)
93
+ assert len(clique) > 2
94
+
95
+
96
+ def test_large_clique_size():
97
+ G = nx.complete_graph(9)
98
+ nx.add_cycle(G, [9, 10, 11])
99
+ G.add_edge(8, 9)
100
+ G.add_edge(1, 12)
101
+ G.add_node(13)
102
+
103
+ assert large_clique_size(G) == 9
104
+ G.remove_node(5)
105
+ assert large_clique_size(G) == 8
106
+ G.remove_edge(2, 3)
107
+ assert large_clique_size(G) == 7
108
+
109
+
110
+ def test_independent_set():
111
+ # smoke test
112
+ G = nx.Graph()
113
+ assert len(maximum_independent_set(G)) == 0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_distance_measures.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.approximation.distance_measures` module.
2
+ """
3
+
4
+ import pytest
5
+
6
+ import networkx as nx
7
+ from networkx.algorithms.approximation import diameter
8
+
9
+
10
+ class TestDiameter:
11
+ """Unit tests for the approximate diameter function
12
+ :func:`~networkx.algorithms.approximation.distance_measures.diameter`.
13
+ """
14
+
15
+ def test_null_graph(self):
16
+ """Test empty graph."""
17
+ G = nx.null_graph()
18
+ with pytest.raises(
19
+ nx.NetworkXError, match="Expected non-empty NetworkX graph!"
20
+ ):
21
+ diameter(G)
22
+
23
+ def test_undirected_non_connected(self):
24
+ """Test an undirected disconnected graph."""
25
+ graph = nx.path_graph(10)
26
+ graph.remove_edge(3, 4)
27
+ with pytest.raises(nx.NetworkXError, match="Graph not connected."):
28
+ diameter(graph)
29
+
30
+ def test_directed_non_strongly_connected(self):
31
+ """Test a directed non strongly connected graph."""
32
+ graph = nx.path_graph(10, create_using=nx.DiGraph())
33
+ with pytest.raises(nx.NetworkXError, match="DiGraph not strongly connected."):
34
+ diameter(graph)
35
+
36
+ def test_complete_undirected_graph(self):
37
+ """Test a complete undirected graph."""
38
+ graph = nx.complete_graph(10)
39
+ assert diameter(graph) == 1
40
+
41
+ def test_complete_directed_graph(self):
42
+ """Test a complete directed graph."""
43
+ graph = nx.complete_graph(10, create_using=nx.DiGraph())
44
+ assert diameter(graph) == 1
45
+
46
+ def test_undirected_path_graph(self):
47
+ """Test an undirected path graph with 10 nodes."""
48
+ graph = nx.path_graph(10)
49
+ assert diameter(graph) == 9
50
+
51
+ def test_directed_path_graph(self):
52
+ """Test a directed path graph with 10 nodes."""
53
+ graph = nx.path_graph(10).to_directed()
54
+ assert diameter(graph) == 9
55
+
56
+ def test_single_node(self):
57
+ """Test a graph which contains just a node."""
58
+ graph = nx.Graph()
59
+ graph.add_node(1)
60
+ assert diameter(graph) == 0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_maxcut.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ import networkx as nx
4
+ from networkx.algorithms.approximation import maxcut
5
+
6
+
7
+ def _is_valid_cut(G, set1, set2):
8
+ union = set1.union(set2)
9
+ assert union == set(G.nodes)
10
+ assert len(set1) + len(set2) == G.number_of_nodes()
11
+
12
+
13
+ def _cut_is_locally_optimal(G, cut_size, set1):
14
+ # test if cut can be locally improved
15
+ for i, node in enumerate(set1):
16
+ cut_size_without_node = nx.algorithms.cut_size(
17
+ G, set1 - {node}, weight="weight"
18
+ )
19
+ assert cut_size_without_node <= cut_size
20
+
21
+
22
+ def test_random_partitioning():
23
+ G = nx.complete_graph(5)
24
+ _, (set1, set2) = maxcut.randomized_partitioning(G, seed=5)
25
+ _is_valid_cut(G, set1, set2)
26
+
27
+
28
+ def test_random_partitioning_all_to_one():
29
+ G = nx.complete_graph(5)
30
+ _, (set1, set2) = maxcut.randomized_partitioning(G, p=1)
31
+ _is_valid_cut(G, set1, set2)
32
+ assert len(set1) == G.number_of_nodes()
33
+ assert len(set2) == 0
34
+
35
+
36
+ def test_one_exchange_basic():
37
+ G = nx.complete_graph(5)
38
+ random.seed(5)
39
+ for u, v, w in G.edges(data=True):
40
+ w["weight"] = random.randrange(-100, 100, 1) / 10
41
+
42
+ initial_cut = set(random.sample(sorted(G.nodes()), k=5))
43
+ cut_size, (set1, set2) = maxcut.one_exchange(
44
+ G, initial_cut, weight="weight", seed=5
45
+ )
46
+
47
+ _is_valid_cut(G, set1, set2)
48
+ _cut_is_locally_optimal(G, cut_size, set1)
49
+
50
+
51
+ def test_one_exchange_optimal():
52
+ # Greedy one exchange should find the optimal solution for this graph (14)
53
+ G = nx.Graph()
54
+ G.add_edge(1, 2, weight=3)
55
+ G.add_edge(1, 3, weight=3)
56
+ G.add_edge(1, 4, weight=3)
57
+ G.add_edge(1, 5, weight=3)
58
+ G.add_edge(2, 3, weight=5)
59
+
60
+ cut_size, (set1, set2) = maxcut.one_exchange(G, weight="weight", seed=5)
61
+
62
+ _is_valid_cut(G, set1, set2)
63
+ _cut_is_locally_optimal(G, cut_size, set1)
64
+ # check global optimality
65
+ assert cut_size == 14
66
+
67
+
68
+ def test_negative_weights():
69
+ G = nx.complete_graph(5)
70
+ random.seed(5)
71
+ for u, v, w in G.edges(data=True):
72
+ w["weight"] = -1 * random.random()
73
+
74
+ initial_cut = set(random.sample(sorted(G.nodes()), k=5))
75
+ cut_size, (set1, set2) = maxcut.one_exchange(G, initial_cut, weight="weight")
76
+
77
+ # make sure it is a valid cut
78
+ _is_valid_cut(G, set1, set2)
79
+ # check local optimality
80
+ _cut_is_locally_optimal(G, cut_size, set1)
81
+ # test that all nodes are in the same partition
82
+ assert len(set1) == len(G.nodes) or len(set2) == len(G.nodes)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/test_vertex_cover.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import networkx as nx
2
+ from networkx.algorithms.approximation import min_weighted_vertex_cover
3
+
4
+
5
+ def is_cover(G, node_cover):
6
+ return all({u, v} & node_cover for u, v in G.edges())
7
+
8
+
9
+ class TestMWVC:
10
+ """Unit tests for the approximate minimum weighted vertex cover
11
+ function,
12
+ :func:`~networkx.algorithms.approximation.vertex_cover.min_weighted_vertex_cover`.
13
+
14
+ """
15
+
16
+ def test_unweighted_directed(self):
17
+ # Create a star graph in which half the nodes are directed in
18
+ # and half are directed out.
19
+ G = nx.DiGraph()
20
+ G.add_edges_from((0, v) for v in range(1, 26))
21
+ G.add_edges_from((v, 0) for v in range(26, 51))
22
+ cover = min_weighted_vertex_cover(G)
23
+ assert 1 == len(cover)
24
+ assert is_cover(G, cover)
25
+
26
+ def test_unweighted_undirected(self):
27
+ # create a simple star graph
28
+ size = 50
29
+ sg = nx.star_graph(size)
30
+ cover = min_weighted_vertex_cover(sg)
31
+ assert 1 == len(cover)
32
+ assert is_cover(sg, cover)
33
+
34
+ def test_weighted(self):
35
+ wg = nx.Graph()
36
+ wg.add_node(0, weight=10)
37
+ wg.add_node(1, weight=1)
38
+ wg.add_node(2, weight=1)
39
+ wg.add_node(3, weight=1)
40
+ wg.add_node(4, weight=1)
41
+
42
+ wg.add_edge(0, 1)
43
+ wg.add_edge(0, 2)
44
+ wg.add_edge(0, 3)
45
+ wg.add_edge(0, 4)
46
+
47
+ wg.add_edge(1, 2)
48
+ wg.add_edge(2, 3)
49
+ wg.add_edge(3, 4)
50
+ wg.add_edge(4, 1)
51
+
52
+ cover = min_weighted_vertex_cover(wg, weight="weight")
53
+ csum = sum(wg.nodes[node]["weight"] for node in cover)
54
+ assert 4 == csum
55
+ assert is_cover(wg, cover)
56
+
57
+ def test_unweighted_self_loop(self):
58
+ slg = nx.Graph()
59
+ slg.add_node(0)
60
+ slg.add_node(1)
61
+ slg.add_node(2)
62
+
63
+ slg.add_edge(0, 1)
64
+ slg.add_edge(2, 2)
65
+
66
+ cover = min_weighted_vertex_cover(slg)
67
+ assert 2 == len(cover)
68
+ assert is_cover(slg, cover)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/asteroidal.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Algorithms for asteroidal triples and asteroidal numbers in graphs.
3
+
4
+ An asteroidal triple in a graph G is a set of three non-adjacent vertices
5
+ u, v and w such that there exist a path between any two of them that avoids
6
+ closed neighborhood of the third. More formally, v_j, v_k belongs to the same
7
+ connected component of G - N[v_i], where N[v_i] denotes the closed neighborhood
8
+ of v_i. A graph which does not contain any asteroidal triples is called
9
+ an AT-free graph. The class of AT-free graphs is a graph class for which
10
+ many NP-complete problems are solvable in polynomial time. Amongst them,
11
+ independent set and coloring.
12
+ """
13
+ import networkx as nx
14
+ from networkx.utils import not_implemented_for
15
+
16
+ __all__ = ["is_at_free", "find_asteroidal_triple"]
17
+
18
+
19
+ @not_implemented_for("directed")
20
+ @not_implemented_for("multigraph")
21
+ @nx._dispatch
22
+ def find_asteroidal_triple(G):
23
+ r"""Find an asteroidal triple in the given graph.
24
+
25
+ An asteroidal triple is a triple of non-adjacent vertices such that
26
+ there exists a path between any two of them which avoids the closed
27
+ neighborhood of the third. It checks all independent triples of vertices
28
+ and whether they are an asteroidal triple or not. This is done with the
29
+ help of a data structure called a component structure.
30
+ A component structure encodes information about which vertices belongs to
31
+ the same connected component when the closed neighborhood of a given vertex
32
+ is removed from the graph. The algorithm used to check is the trivial
33
+ one, outlined in [1]_, which has a runtime of
34
+ :math:`O(|V||\overline{E} + |V||E|)`, where the second term is the
35
+ creation of the component structure.
36
+
37
+ Parameters
38
+ ----------
39
+ G : NetworkX Graph
40
+ The graph to check whether is AT-free or not
41
+
42
+ Returns
43
+ -------
44
+ list or None
45
+ An asteroidal triple is returned as a list of nodes. If no asteroidal
46
+ triple exists, i.e. the graph is AT-free, then None is returned.
47
+ The returned value depends on the certificate parameter. The default
48
+ option is a bool which is True if the graph is AT-free, i.e. the
49
+ given graph contains no asteroidal triples, and False otherwise, i.e.
50
+ if the graph contains at least one asteroidal triple.
51
+
52
+ Notes
53
+ -----
54
+ The component structure and the algorithm is described in [1]_. The current
55
+ implementation implements the trivial algorithm for simple graphs.
56
+
57
+ References
58
+ ----------
59
+ .. [1] Ekkehard Köhler,
60
+ "Recognizing Graphs without asteroidal triples",
61
+ Journal of Discrete Algorithms 2, pages 439-452, 2004.
62
+ https://www.sciencedirect.com/science/article/pii/S157086670400019X
63
+ """
64
+ V = set(G.nodes)
65
+
66
+ if len(V) < 6:
67
+ # An asteroidal triple cannot exist in a graph with 5 or less vertices.
68
+ return None
69
+
70
+ component_structure = create_component_structure(G)
71
+ E_complement = set(nx.complement(G).edges)
72
+
73
+ for e in E_complement:
74
+ u = e[0]
75
+ v = e[1]
76
+ u_neighborhood = set(G[u]).union([u])
77
+ v_neighborhood = set(G[v]).union([v])
78
+ union_of_neighborhoods = u_neighborhood.union(v_neighborhood)
79
+ for w in V - union_of_neighborhoods:
80
+ # Check for each pair of vertices whether they belong to the
81
+ # same connected component when the closed neighborhood of the
82
+ # third is removed.
83
+ if (
84
+ component_structure[u][v] == component_structure[u][w]
85
+ and component_structure[v][u] == component_structure[v][w]
86
+ and component_structure[w][u] == component_structure[w][v]
87
+ ):
88
+ return [u, v, w]
89
+ return None
90
+
91
+
92
+ @not_implemented_for("directed")
93
+ @not_implemented_for("multigraph")
94
+ @nx._dispatch
95
+ def is_at_free(G):
96
+ """Check if a graph is AT-free.
97
+
98
+ The method uses the `find_asteroidal_triple` method to recognize
99
+ an AT-free graph. If no asteroidal triple is found the graph is
100
+ AT-free and True is returned. If at least one asteroidal triple is
101
+ found the graph is not AT-free and False is returned.
102
+
103
+ Parameters
104
+ ----------
105
+ G : NetworkX Graph
106
+ The graph to check whether is AT-free or not.
107
+
108
+ Returns
109
+ -------
110
+ bool
111
+ True if G is AT-free and False otherwise.
112
+
113
+ Examples
114
+ --------
115
+ >>> G = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (4, 5)])
116
+ >>> nx.is_at_free(G)
117
+ True
118
+
119
+ >>> G = nx.cycle_graph(6)
120
+ >>> nx.is_at_free(G)
121
+ False
122
+ """
123
+ return find_asteroidal_triple(G) is None
124
+
125
+
126
+ @not_implemented_for("directed")
127
+ @not_implemented_for("multigraph")
128
+ @nx._dispatch
129
+ def create_component_structure(G):
130
+ r"""Create component structure for G.
131
+
132
+ A *component structure* is an `nxn` array, denoted `c`, where `n` is
133
+ the number of vertices, where each row and column corresponds to a vertex.
134
+
135
+ .. math::
136
+ c_{uv} = \begin{cases} 0, if v \in N[u] \\
137
+ k, if v \in component k of G \setminus N[u] \end{cases}
138
+
139
+ Where `k` is an arbitrary label for each component. The structure is used
140
+ to simplify the detection of asteroidal triples.
141
+
142
+ Parameters
143
+ ----------
144
+ G : NetworkX Graph
145
+ Undirected, simple graph.
146
+
147
+ Returns
148
+ -------
149
+ component_structure : dictionary
150
+ A dictionary of dictionaries, keyed by pairs of vertices.
151
+
152
+ """
153
+ V = set(G.nodes)
154
+ component_structure = {}
155
+ for v in V:
156
+ label = 0
157
+ closed_neighborhood = set(G[v]).union({v})
158
+ row_dict = {}
159
+ for u in closed_neighborhood:
160
+ row_dict[u] = 0
161
+
162
+ G_reduced = G.subgraph(set(G.nodes) - closed_neighborhood)
163
+ for cc in nx.connected_components(G_reduced):
164
+ label += 1
165
+ for u in cc:
166
+ row_dict[u] = label
167
+
168
+ component_structure[v] = row_dict
169
+
170
+ return component_structure
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/bridges.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Bridge-finding algorithms."""
2
+ from itertools import chain
3
+
4
+ import networkx as nx
5
+ from networkx.utils import not_implemented_for
6
+
7
+ __all__ = ["bridges", "has_bridges", "local_bridges"]
8
+
9
+
10
+ @not_implemented_for("directed")
11
+ @nx._dispatch
12
+ def bridges(G, root=None):
13
+ """Generate all bridges in a graph.
14
+
15
+ A *bridge* in a graph is an edge whose removal causes the number of
16
+ connected components of the graph to increase. Equivalently, a bridge is an
17
+ edge that does not belong to any cycle. Bridges are also known as cut-edges,
18
+ isthmuses, or cut arcs.
19
+
20
+ Parameters
21
+ ----------
22
+ G : undirected graph
23
+
24
+ root : node (optional)
25
+ A node in the graph `G`. If specified, only the bridges in the
26
+ connected component containing this node will be returned.
27
+
28
+ Yields
29
+ ------
30
+ e : edge
31
+ An edge in the graph whose removal disconnects the graph (or
32
+ causes the number of connected components to increase).
33
+
34
+ Raises
35
+ ------
36
+ NodeNotFound
37
+ If `root` is not in the graph `G`.
38
+
39
+ NetworkXNotImplemented
40
+ If `G` is a directed graph.
41
+
42
+ Examples
43
+ --------
44
+ The barbell graph with parameter zero has a single bridge:
45
+
46
+ >>> G = nx.barbell_graph(10, 0)
47
+ >>> list(nx.bridges(G))
48
+ [(9, 10)]
49
+
50
+ Notes
51
+ -----
52
+ This is an implementation of the algorithm described in [1]_. An edge is a
53
+ bridge if and only if it is not contained in any chain. Chains are found
54
+ using the :func:`networkx.chain_decomposition` function.
55
+
56
+ The algorithm described in [1]_ requires a simple graph. If the provided
57
+ graph is a multigraph, we convert it to a simple graph and verify that any
58
+ bridges discovered by the chain decomposition algorithm are not multi-edges.
59
+
60
+ Ignoring polylogarithmic factors, the worst-case time complexity is the
61
+ same as the :func:`networkx.chain_decomposition` function,
62
+ $O(m + n)$, where $n$ is the number of nodes in the graph and $m$ is
63
+ the number of edges.
64
+
65
+ References
66
+ ----------
67
+ .. [1] https://en.wikipedia.org/wiki/Bridge_%28graph_theory%29#Bridge-Finding_with_Chain_Decompositions
68
+ """
69
+ multigraph = G.is_multigraph()
70
+ H = nx.Graph(G) if multigraph else G
71
+ chains = nx.chain_decomposition(H, root=root)
72
+ chain_edges = set(chain.from_iterable(chains))
73
+ H_copy = H.copy()
74
+ if root is not None:
75
+ H = H.subgraph(nx.node_connected_component(H, root)).copy()
76
+ for u, v in H.edges():
77
+ if (u, v) not in chain_edges and (v, u) not in chain_edges:
78
+ if multigraph and len(G[u][v]) > 1:
79
+ continue
80
+ yield u, v
81
+
82
+
83
+ @not_implemented_for("directed")
84
+ @nx._dispatch
85
+ def has_bridges(G, root=None):
86
+ """Decide whether a graph has any bridges.
87
+
88
+ A *bridge* in a graph is an edge whose removal causes the number of
89
+ connected components of the graph to increase.
90
+
91
+ Parameters
92
+ ----------
93
+ G : undirected graph
94
+
95
+ root : node (optional)
96
+ A node in the graph `G`. If specified, only the bridges in the
97
+ connected component containing this node will be considered.
98
+
99
+ Returns
100
+ -------
101
+ bool
102
+ Whether the graph (or the connected component containing `root`)
103
+ has any bridges.
104
+
105
+ Raises
106
+ ------
107
+ NodeNotFound
108
+ If `root` is not in the graph `G`.
109
+
110
+ NetworkXNotImplemented
111
+ If `G` is a directed graph.
112
+
113
+ Examples
114
+ --------
115
+ The barbell graph with parameter zero has a single bridge::
116
+
117
+ >>> G = nx.barbell_graph(10, 0)
118
+ >>> nx.has_bridges(G)
119
+ True
120
+
121
+ On the other hand, the cycle graph has no bridges::
122
+
123
+ >>> G = nx.cycle_graph(5)
124
+ >>> nx.has_bridges(G)
125
+ False
126
+
127
+ Notes
128
+ -----
129
+ This implementation uses the :func:`networkx.bridges` function, so
130
+ it shares its worst-case time complexity, $O(m + n)$, ignoring
131
+ polylogarithmic factors, where $n$ is the number of nodes in the
132
+ graph and $m$ is the number of edges.
133
+
134
+ """
135
+ try:
136
+ next(bridges(G, root=root))
137
+ except StopIteration:
138
+ return False
139
+ else:
140
+ return True
141
+
142
+
143
+ @not_implemented_for("multigraph")
144
+ @not_implemented_for("directed")
145
+ @nx._dispatch(edge_attrs="weight")
146
+ def local_bridges(G, with_span=True, weight=None):
147
+ """Iterate over local bridges of `G` optionally computing the span
148
+
149
+ A *local bridge* is an edge whose endpoints have no common neighbors.
150
+ That is, the edge is not part of a triangle in the graph.
151
+
152
+ The *span* of a *local bridge* is the shortest path length between
153
+ the endpoints if the local bridge is removed.
154
+
155
+ Parameters
156
+ ----------
157
+ G : undirected graph
158
+
159
+ with_span : bool
160
+ If True, yield a 3-tuple `(u, v, span)`
161
+
162
+ weight : function, string or None (default: None)
163
+ If function, used to compute edge weights for the span.
164
+ If string, the edge data attribute used in calculating span.
165
+ If None, all edges have weight 1.
166
+
167
+ Yields
168
+ ------
169
+ e : edge
170
+ The local bridges as an edge 2-tuple of nodes `(u, v)` or
171
+ as a 3-tuple `(u, v, span)` when `with_span is True`.
172
+
173
+ Raises
174
+ ------
175
+ NetworkXNotImplemented
176
+ If `G` is a directed graph or multigraph.
177
+
178
+ Examples
179
+ --------
180
+ A cycle graph has every edge a local bridge with span N-1.
181
+
182
+ >>> G = nx.cycle_graph(9)
183
+ >>> (0, 8, 8) in set(nx.local_bridges(G))
184
+ True
185
+ """
186
+ if with_span is not True:
187
+ for u, v in G.edges:
188
+ if not (set(G[u]) & set(G[v])):
189
+ yield u, v
190
+ else:
191
+ wt = nx.weighted._weight_function(G, weight)
192
+ for u, v in G.edges:
193
+ if not (set(G[u]) & set(G[v])):
194
+ enodes = {u, v}
195
+
196
+ def hide_edge(n, nbr, d):
197
+ if n not in enodes or nbr not in enodes:
198
+ return wt(n, nbr, d)
199
+ return None
200
+
201
+ try:
202
+ span = nx.shortest_path_length(G, u, v, weight=hide_edge)
203
+ yield u, v, span
204
+ except nx.NetworkXNoPath:
205
+ yield u, v, float("inf")
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/chordal.py ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Algorithms for chordal graphs.
3
+
4
+ A graph is chordal if every cycle of length at least 4 has a chord
5
+ (an edge joining two nodes not adjacent in the cycle).
6
+ https://en.wikipedia.org/wiki/Chordal_graph
7
+ """
8
+ import sys
9
+
10
+ import networkx as nx
11
+ from networkx.algorithms.components import connected_components
12
+ from networkx.utils import arbitrary_element, not_implemented_for
13
+
14
+ __all__ = [
15
+ "is_chordal",
16
+ "find_induced_nodes",
17
+ "chordal_graph_cliques",
18
+ "chordal_graph_treewidth",
19
+ "NetworkXTreewidthBoundExceeded",
20
+ "complete_to_chordal_graph",
21
+ ]
22
+
23
+
24
+ class NetworkXTreewidthBoundExceeded(nx.NetworkXException):
25
+ """Exception raised when a treewidth bound has been provided and it has
26
+ been exceeded"""
27
+
28
+
29
+ @not_implemented_for("directed")
30
+ @not_implemented_for("multigraph")
31
+ @nx._dispatch
32
+ def is_chordal(G):
33
+ """Checks whether G is a chordal graph.
34
+
35
+ A graph is chordal if every cycle of length at least 4 has a chord
36
+ (an edge joining two nodes not adjacent in the cycle).
37
+
38
+ Parameters
39
+ ----------
40
+ G : graph
41
+ A NetworkX graph.
42
+
43
+ Returns
44
+ -------
45
+ chordal : bool
46
+ True if G is a chordal graph and False otherwise.
47
+
48
+ Raises
49
+ ------
50
+ NetworkXNotImplemented
51
+ The algorithm does not support DiGraph, MultiGraph and MultiDiGraph.
52
+
53
+ Examples
54
+ --------
55
+ >>> e = [
56
+ ... (1, 2),
57
+ ... (1, 3),
58
+ ... (2, 3),
59
+ ... (2, 4),
60
+ ... (3, 4),
61
+ ... (3, 5),
62
+ ... (3, 6),
63
+ ... (4, 5),
64
+ ... (4, 6),
65
+ ... (5, 6),
66
+ ... ]
67
+ >>> G = nx.Graph(e)
68
+ >>> nx.is_chordal(G)
69
+ True
70
+
71
+ Notes
72
+ -----
73
+ The routine tries to go through every node following maximum cardinality
74
+ search. It returns False when it finds that the separator for any node
75
+ is not a clique. Based on the algorithms in [1]_.
76
+
77
+ Self loops are ignored.
78
+
79
+ References
80
+ ----------
81
+ .. [1] R. E. Tarjan and M. Yannakakis, Simple linear-time algorithms
82
+ to test chordality of graphs, test acyclicity of hypergraphs, and
83
+ selectively reduce acyclic hypergraphs, SIAM J. Comput., 13 (1984),
84
+ pp. 566–579.
85
+ """
86
+ if len(G.nodes) <= 3:
87
+ return True
88
+ return len(_find_chordality_breaker(G)) == 0
89
+
90
+
91
+ @nx._dispatch
92
+ def find_induced_nodes(G, s, t, treewidth_bound=sys.maxsize):
93
+ """Returns the set of induced nodes in the path from s to t.
94
+
95
+ Parameters
96
+ ----------
97
+ G : graph
98
+ A chordal NetworkX graph
99
+ s : node
100
+ Source node to look for induced nodes
101
+ t : node
102
+ Destination node to look for induced nodes
103
+ treewidth_bound: float
104
+ Maximum treewidth acceptable for the graph H. The search
105
+ for induced nodes will end as soon as the treewidth_bound is exceeded.
106
+
107
+ Returns
108
+ -------
109
+ induced_nodes : Set of nodes
110
+ The set of induced nodes in the path from s to t in G
111
+
112
+ Raises
113
+ ------
114
+ NetworkXError
115
+ The algorithm does not support DiGraph, MultiGraph and MultiDiGraph.
116
+ If the input graph is an instance of one of these classes, a
117
+ :exc:`NetworkXError` is raised.
118
+ The algorithm can only be applied to chordal graphs. If the input
119
+ graph is found to be non-chordal, a :exc:`NetworkXError` is raised.
120
+
121
+ Examples
122
+ --------
123
+ >>> G = nx.Graph()
124
+ >>> G = nx.generators.classic.path_graph(10)
125
+ >>> induced_nodes = nx.find_induced_nodes(G, 1, 9, 2)
126
+ >>> sorted(induced_nodes)
127
+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
128
+
129
+ Notes
130
+ -----
131
+ G must be a chordal graph and (s,t) an edge that is not in G.
132
+
133
+ If a treewidth_bound is provided, the search for induced nodes will end
134
+ as soon as the treewidth_bound is exceeded.
135
+
136
+ The algorithm is inspired by Algorithm 4 in [1]_.
137
+ A formal definition of induced node can also be found on that reference.
138
+
139
+ Self Loops are ignored
140
+
141
+ References
142
+ ----------
143
+ .. [1] Learning Bounded Treewidth Bayesian Networks.
144
+ Gal Elidan, Stephen Gould; JMLR, 9(Dec):2699--2731, 2008.
145
+ http://jmlr.csail.mit.edu/papers/volume9/elidan08a/elidan08a.pdf
146
+ """
147
+ if not is_chordal(G):
148
+ raise nx.NetworkXError("Input graph is not chordal.")
149
+
150
+ H = nx.Graph(G)
151
+ H.add_edge(s, t)
152
+ induced_nodes = set()
153
+ triplet = _find_chordality_breaker(H, s, treewidth_bound)
154
+ while triplet:
155
+ (u, v, w) = triplet
156
+ induced_nodes.update(triplet)
157
+ for n in triplet:
158
+ if n != s:
159
+ H.add_edge(s, n)
160
+ triplet = _find_chordality_breaker(H, s, treewidth_bound)
161
+ if induced_nodes:
162
+ # Add t and the second node in the induced path from s to t.
163
+ induced_nodes.add(t)
164
+ for u in G[s]:
165
+ if len(induced_nodes & set(G[u])) == 2:
166
+ induced_nodes.add(u)
167
+ break
168
+ return induced_nodes
169
+
170
+
171
+ @nx._dispatch
172
+ def chordal_graph_cliques(G):
173
+ """Returns all maximal cliques of a chordal graph.
174
+
175
+ The algorithm breaks the graph in connected components and performs a
176
+ maximum cardinality search in each component to get the cliques.
177
+
178
+ Parameters
179
+ ----------
180
+ G : graph
181
+ A NetworkX graph
182
+
183
+ Yields
184
+ ------
185
+ frozenset of nodes
186
+ Maximal cliques, each of which is a frozenset of
187
+ nodes in `G`. The order of cliques is arbitrary.
188
+
189
+ Raises
190
+ ------
191
+ NetworkXError
192
+ The algorithm does not support DiGraph, MultiGraph and MultiDiGraph.
193
+ The algorithm can only be applied to chordal graphs. If the input
194
+ graph is found to be non-chordal, a :exc:`NetworkXError` is raised.
195
+
196
+ Examples
197
+ --------
198
+ >>> e = [
199
+ ... (1, 2),
200
+ ... (1, 3),
201
+ ... (2, 3),
202
+ ... (2, 4),
203
+ ... (3, 4),
204
+ ... (3, 5),
205
+ ... (3, 6),
206
+ ... (4, 5),
207
+ ... (4, 6),
208
+ ... (5, 6),
209
+ ... (7, 8),
210
+ ... ]
211
+ >>> G = nx.Graph(e)
212
+ >>> G.add_node(9)
213
+ >>> cliques = [c for c in chordal_graph_cliques(G)]
214
+ >>> cliques[0]
215
+ frozenset({1, 2, 3})
216
+ """
217
+ for C in (G.subgraph(c).copy() for c in connected_components(G)):
218
+ if C.number_of_nodes() == 1:
219
+ if nx.number_of_selfloops(C) > 0:
220
+ raise nx.NetworkXError("Input graph is not chordal.")
221
+ yield frozenset(C.nodes())
222
+ else:
223
+ unnumbered = set(C.nodes())
224
+ v = arbitrary_element(C)
225
+ unnumbered.remove(v)
226
+ numbered = {v}
227
+ clique_wanna_be = {v}
228
+ while unnumbered:
229
+ v = _max_cardinality_node(C, unnumbered, numbered)
230
+ unnumbered.remove(v)
231
+ numbered.add(v)
232
+ new_clique_wanna_be = set(C.neighbors(v)) & numbered
233
+ sg = C.subgraph(clique_wanna_be)
234
+ if _is_complete_graph(sg):
235
+ new_clique_wanna_be.add(v)
236
+ if not new_clique_wanna_be >= clique_wanna_be:
237
+ yield frozenset(clique_wanna_be)
238
+ clique_wanna_be = new_clique_wanna_be
239
+ else:
240
+ raise nx.NetworkXError("Input graph is not chordal.")
241
+ yield frozenset(clique_wanna_be)
242
+
243
+
244
+ @nx._dispatch
245
+ def chordal_graph_treewidth(G):
246
+ """Returns the treewidth of the chordal graph G.
247
+
248
+ Parameters
249
+ ----------
250
+ G : graph
251
+ A NetworkX graph
252
+
253
+ Returns
254
+ -------
255
+ treewidth : int
256
+ The size of the largest clique in the graph minus one.
257
+
258
+ Raises
259
+ ------
260
+ NetworkXError
261
+ The algorithm does not support DiGraph, MultiGraph and MultiDiGraph.
262
+ The algorithm can only be applied to chordal graphs. If the input
263
+ graph is found to be non-chordal, a :exc:`NetworkXError` is raised.
264
+
265
+ Examples
266
+ --------
267
+ >>> e = [
268
+ ... (1, 2),
269
+ ... (1, 3),
270
+ ... (2, 3),
271
+ ... (2, 4),
272
+ ... (3, 4),
273
+ ... (3, 5),
274
+ ... (3, 6),
275
+ ... (4, 5),
276
+ ... (4, 6),
277
+ ... (5, 6),
278
+ ... (7, 8),
279
+ ... ]
280
+ >>> G = nx.Graph(e)
281
+ >>> G.add_node(9)
282
+ >>> nx.chordal_graph_treewidth(G)
283
+ 3
284
+
285
+ References
286
+ ----------
287
+ .. [1] https://en.wikipedia.org/wiki/Tree_decomposition#Treewidth
288
+ """
289
+ if not is_chordal(G):
290
+ raise nx.NetworkXError("Input graph is not chordal.")
291
+
292
+ max_clique = -1
293
+ for clique in nx.chordal_graph_cliques(G):
294
+ max_clique = max(max_clique, len(clique))
295
+ return max_clique - 1
296
+
297
+
298
+ def _is_complete_graph(G):
299
+ """Returns True if G is a complete graph."""
300
+ if nx.number_of_selfloops(G) > 0:
301
+ raise nx.NetworkXError("Self loop found in _is_complete_graph()")
302
+ n = G.number_of_nodes()
303
+ if n < 2:
304
+ return True
305
+ e = G.number_of_edges()
306
+ max_edges = (n * (n - 1)) / 2
307
+ return e == max_edges
308
+
309
+
310
+ def _find_missing_edge(G):
311
+ """Given a non-complete graph G, returns a missing edge."""
312
+ nodes = set(G)
313
+ for u in G:
314
+ missing = nodes - set(list(G[u].keys()) + [u])
315
+ if missing:
316
+ return (u, missing.pop())
317
+
318
+
319
+ def _max_cardinality_node(G, choices, wanna_connect):
320
+ """Returns a the node in choices that has more connections in G
321
+ to nodes in wanna_connect.
322
+ """
323
+ max_number = -1
324
+ for x in choices:
325
+ number = len([y for y in G[x] if y in wanna_connect])
326
+ if number > max_number:
327
+ max_number = number
328
+ max_cardinality_node = x
329
+ return max_cardinality_node
330
+
331
+
332
+ def _find_chordality_breaker(G, s=None, treewidth_bound=sys.maxsize):
333
+ """Given a graph G, starts a max cardinality search
334
+ (starting from s if s is given and from an arbitrary node otherwise)
335
+ trying to find a non-chordal cycle.
336
+
337
+ If it does find one, it returns (u,v,w) where u,v,w are the three
338
+ nodes that together with s are involved in the cycle.
339
+
340
+ It ignores any self loops.
341
+ """
342
+ unnumbered = set(G)
343
+ if s is None:
344
+ s = arbitrary_element(G)
345
+ unnumbered.remove(s)
346
+ numbered = {s}
347
+ current_treewidth = -1
348
+ while unnumbered: # and current_treewidth <= treewidth_bound:
349
+ v = _max_cardinality_node(G, unnumbered, numbered)
350
+ unnumbered.remove(v)
351
+ numbered.add(v)
352
+ clique_wanna_be = set(G[v]) & numbered
353
+ sg = G.subgraph(clique_wanna_be)
354
+ if _is_complete_graph(sg):
355
+ # The graph seems to be chordal by now. We update the treewidth
356
+ current_treewidth = max(current_treewidth, len(clique_wanna_be))
357
+ if current_treewidth > treewidth_bound:
358
+ raise nx.NetworkXTreewidthBoundExceeded(
359
+ f"treewidth_bound exceeded: {current_treewidth}"
360
+ )
361
+ else:
362
+ # sg is not a clique,
363
+ # look for an edge that is not included in sg
364
+ (u, w) = _find_missing_edge(sg)
365
+ return (u, v, w)
366
+ return ()
367
+
368
+
369
+ @not_implemented_for("directed")
370
+ @nx._dispatch
371
+ def complete_to_chordal_graph(G):
372
+ """Return a copy of G completed to a chordal graph
373
+
374
+ Adds edges to a copy of G to create a chordal graph. A graph G=(V,E) is
375
+ called chordal if for each cycle with length bigger than 3, there exist
376
+ two non-adjacent nodes connected by an edge (called a chord).
377
+
378
+ Parameters
379
+ ----------
380
+ G : NetworkX graph
381
+ Undirected graph
382
+
383
+ Returns
384
+ -------
385
+ H : NetworkX graph
386
+ The chordal enhancement of G
387
+ alpha : Dictionary
388
+ The elimination ordering of nodes of G
389
+
390
+ Notes
391
+ -----
392
+ There are different approaches to calculate the chordal
393
+ enhancement of a graph. The algorithm used here is called
394
+ MCS-M and gives at least minimal (local) triangulation of graph. Note
395
+ that this triangulation is not necessarily a global minimum.
396
+
397
+ https://en.wikipedia.org/wiki/Chordal_graph
398
+
399
+ References
400
+ ----------
401
+ .. [1] Berry, Anne & Blair, Jean & Heggernes, Pinar & Peyton, Barry. (2004)
402
+ Maximum Cardinality Search for Computing Minimal Triangulations of
403
+ Graphs. Algorithmica. 39. 287-298. 10.1007/s00453-004-1084-3.
404
+
405
+ Examples
406
+ --------
407
+ >>> from networkx.algorithms.chordal import complete_to_chordal_graph
408
+ >>> G = nx.wheel_graph(10)
409
+ >>> H, alpha = complete_to_chordal_graph(G)
410
+ """
411
+ H = G.copy()
412
+ alpha = {node: 0 for node in H}
413
+ if nx.is_chordal(H):
414
+ return H, alpha
415
+ chords = set()
416
+ weight = {node: 0 for node in H.nodes()}
417
+ unnumbered_nodes = list(H.nodes())
418
+ for i in range(len(H.nodes()), 0, -1):
419
+ # get the node in unnumbered_nodes with the maximum weight
420
+ z = max(unnumbered_nodes, key=lambda node: weight[node])
421
+ unnumbered_nodes.remove(z)
422
+ alpha[z] = i
423
+ update_nodes = []
424
+ for y in unnumbered_nodes:
425
+ if G.has_edge(y, z):
426
+ update_nodes.append(y)
427
+ else:
428
+ # y_weight will be bigger than node weights between y and z
429
+ y_weight = weight[y]
430
+ lower_nodes = [
431
+ node for node in unnumbered_nodes if weight[node] < y_weight
432
+ ]
433
+ if nx.has_path(H.subgraph(lower_nodes + [z, y]), y, z):
434
+ update_nodes.append(y)
435
+ chords.add((z, y))
436
+ # during calculation of paths the weights should not be updated
437
+ for node in update_nodes:
438
+ weight[node] += 1
439
+ H.add_edges_from(chords)
440
+ return H, alpha
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/cluster.py ADDED
@@ -0,0 +1,605 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Algorithms to characterize the number of triangles in a graph."""
2
+
3
+ from collections import Counter
4
+ from itertools import chain, combinations
5
+
6
+ import networkx as nx
7
+ from networkx.utils import not_implemented_for
8
+
9
+ __all__ = [
10
+ "triangles",
11
+ "average_clustering",
12
+ "clustering",
13
+ "transitivity",
14
+ "square_clustering",
15
+ "generalized_degree",
16
+ ]
17
+
18
+
19
+ @not_implemented_for("directed")
20
+ @nx._dispatch
21
+ def triangles(G, nodes=None):
22
+ """Compute the number of triangles.
23
+
24
+ Finds the number of triangles that include a node as one vertex.
25
+
26
+ Parameters
27
+ ----------
28
+ G : graph
29
+ A networkx graph
30
+
31
+ nodes : node, iterable of nodes, or None (default=None)
32
+ If a singleton node, return the number of triangles for that node.
33
+ If an iterable, compute the number of triangles for each of those nodes.
34
+ If `None` (the default) compute the number of triangles for all nodes in `G`.
35
+
36
+ Returns
37
+ -------
38
+ out : dict or int
39
+ If `nodes` is a container of nodes, returns number of triangles keyed by node (dict).
40
+ If `nodes` is a specific node, returns number of triangles for the node (int).
41
+
42
+ Examples
43
+ --------
44
+ >>> G = nx.complete_graph(5)
45
+ >>> print(nx.triangles(G, 0))
46
+ 6
47
+ >>> print(nx.triangles(G))
48
+ {0: 6, 1: 6, 2: 6, 3: 6, 4: 6}
49
+ >>> print(list(nx.triangles(G, [0, 1]).values()))
50
+ [6, 6]
51
+
52
+ Notes
53
+ -----
54
+ Self loops are ignored.
55
+
56
+ """
57
+ if nodes is not None:
58
+ # If `nodes` represents a single node, return only its number of triangles
59
+ if nodes in G:
60
+ return next(_triangles_and_degree_iter(G, nodes))[2] // 2
61
+
62
+ # if `nodes` is a container of nodes, then return a
63
+ # dictionary mapping node to number of triangles.
64
+ return {v: t // 2 for v, d, t, _ in _triangles_and_degree_iter(G, nodes)}
65
+
66
+ # if nodes is None, then compute triangles for the complete graph
67
+
68
+ # dict used to avoid visiting the same nodes twice
69
+ # this allows calculating/counting each triangle only once
70
+ later_neighbors = {}
71
+
72
+ # iterate over the nodes in a graph
73
+ for node, neighbors in G.adjacency():
74
+ later_neighbors[node] = {
75
+ n for n in neighbors if n not in later_neighbors and n != node
76
+ }
77
+
78
+ # instantiate Counter for each node to include isolated nodes
79
+ # add 1 to the count if a nodes neighbor's neighbor is also a neighbor
80
+ triangle_counts = Counter(dict.fromkeys(G, 0))
81
+ for node1, neighbors in later_neighbors.items():
82
+ for node2 in neighbors:
83
+ third_nodes = neighbors & later_neighbors[node2]
84
+ m = len(third_nodes)
85
+ triangle_counts[node1] += m
86
+ triangle_counts[node2] += m
87
+ triangle_counts.update(third_nodes)
88
+
89
+ return dict(triangle_counts)
90
+
91
+
92
+ @not_implemented_for("multigraph")
93
+ def _triangles_and_degree_iter(G, nodes=None):
94
+ """Return an iterator of (node, degree, triangles, generalized degree).
95
+
96
+ This double counts triangles so you may want to divide by 2.
97
+ See degree(), triangles() and generalized_degree() for definitions
98
+ and details.
99
+
100
+ """
101
+ if nodes is None:
102
+ nodes_nbrs = G.adj.items()
103
+ else:
104
+ nodes_nbrs = ((n, G[n]) for n in G.nbunch_iter(nodes))
105
+
106
+ for v, v_nbrs in nodes_nbrs:
107
+ vs = set(v_nbrs) - {v}
108
+ gen_degree = Counter(len(vs & (set(G[w]) - {w})) for w in vs)
109
+ ntriangles = sum(k * val for k, val in gen_degree.items())
110
+ yield (v, len(vs), ntriangles, gen_degree)
111
+
112
+
113
+ @not_implemented_for("multigraph")
114
+ def _weighted_triangles_and_degree_iter(G, nodes=None, weight="weight"):
115
+ """Return an iterator of (node, degree, weighted_triangles).
116
+
117
+ Used for weighted clustering.
118
+ Note: this returns the geometric average weight of edges in the triangle.
119
+ Also, each triangle is counted twice (each direction).
120
+ So you may want to divide by 2.
121
+
122
+ """
123
+ import numpy as np
124
+
125
+ if weight is None or G.number_of_edges() == 0:
126
+ max_weight = 1
127
+ else:
128
+ max_weight = max(d.get(weight, 1) for u, v, d in G.edges(data=True))
129
+ if nodes is None:
130
+ nodes_nbrs = G.adj.items()
131
+ else:
132
+ nodes_nbrs = ((n, G[n]) for n in G.nbunch_iter(nodes))
133
+
134
+ def wt(u, v):
135
+ return G[u][v].get(weight, 1) / max_weight
136
+
137
+ for i, nbrs in nodes_nbrs:
138
+ inbrs = set(nbrs) - {i}
139
+ weighted_triangles = 0
140
+ seen = set()
141
+ for j in inbrs:
142
+ seen.add(j)
143
+ # This avoids counting twice -- we double at the end.
144
+ jnbrs = set(G[j]) - seen
145
+ # Only compute the edge weight once, before the inner inner
146
+ # loop.
147
+ wij = wt(i, j)
148
+ weighted_triangles += sum(
149
+ np.cbrt([(wij * wt(j, k) * wt(k, i)) for k in inbrs & jnbrs])
150
+ )
151
+ yield (i, len(inbrs), 2 * weighted_triangles)
152
+
153
+
154
+ @not_implemented_for("multigraph")
155
+ def _directed_triangles_and_degree_iter(G, nodes=None):
156
+ """Return an iterator of
157
+ (node, total_degree, reciprocal_degree, directed_triangles).
158
+
159
+ Used for directed clustering.
160
+ Note that unlike `_triangles_and_degree_iter()`, this function counts
161
+ directed triangles so does not count triangles twice.
162
+
163
+ """
164
+ nodes_nbrs = ((n, G._pred[n], G._succ[n]) for n in G.nbunch_iter(nodes))
165
+
166
+ for i, preds, succs in nodes_nbrs:
167
+ ipreds = set(preds) - {i}
168
+ isuccs = set(succs) - {i}
169
+
170
+ directed_triangles = 0
171
+ for j in chain(ipreds, isuccs):
172
+ jpreds = set(G._pred[j]) - {j}
173
+ jsuccs = set(G._succ[j]) - {j}
174
+ directed_triangles += sum(
175
+ 1
176
+ for k in chain(
177
+ (ipreds & jpreds),
178
+ (ipreds & jsuccs),
179
+ (isuccs & jpreds),
180
+ (isuccs & jsuccs),
181
+ )
182
+ )
183
+ dtotal = len(ipreds) + len(isuccs)
184
+ dbidirectional = len(ipreds & isuccs)
185
+ yield (i, dtotal, dbidirectional, directed_triangles)
186
+
187
+
188
+ @not_implemented_for("multigraph")
189
+ def _directed_weighted_triangles_and_degree_iter(G, nodes=None, weight="weight"):
190
+ """Return an iterator of
191
+ (node, total_degree, reciprocal_degree, directed_weighted_triangles).
192
+
193
+ Used for directed weighted clustering.
194
+ Note that unlike `_weighted_triangles_and_degree_iter()`, this function counts
195
+ directed triangles so does not count triangles twice.
196
+
197
+ """
198
+ import numpy as np
199
+
200
+ if weight is None or G.number_of_edges() == 0:
201
+ max_weight = 1
202
+ else:
203
+ max_weight = max(d.get(weight, 1) for u, v, d in G.edges(data=True))
204
+
205
+ nodes_nbrs = ((n, G._pred[n], G._succ[n]) for n in G.nbunch_iter(nodes))
206
+
207
+ def wt(u, v):
208
+ return G[u][v].get(weight, 1) / max_weight
209
+
210
+ for i, preds, succs in nodes_nbrs:
211
+ ipreds = set(preds) - {i}
212
+ isuccs = set(succs) - {i}
213
+
214
+ directed_triangles = 0
215
+ for j in ipreds:
216
+ jpreds = set(G._pred[j]) - {j}
217
+ jsuccs = set(G._succ[j]) - {j}
218
+ directed_triangles += sum(
219
+ np.cbrt([(wt(j, i) * wt(k, i) * wt(k, j)) for k in ipreds & jpreds])
220
+ )
221
+ directed_triangles += sum(
222
+ np.cbrt([(wt(j, i) * wt(k, i) * wt(j, k)) for k in ipreds & jsuccs])
223
+ )
224
+ directed_triangles += sum(
225
+ np.cbrt([(wt(j, i) * wt(i, k) * wt(k, j)) for k in isuccs & jpreds])
226
+ )
227
+ directed_triangles += sum(
228
+ np.cbrt([(wt(j, i) * wt(i, k) * wt(j, k)) for k in isuccs & jsuccs])
229
+ )
230
+
231
+ for j in isuccs:
232
+ jpreds = set(G._pred[j]) - {j}
233
+ jsuccs = set(G._succ[j]) - {j}
234
+ directed_triangles += sum(
235
+ np.cbrt([(wt(i, j) * wt(k, i) * wt(k, j)) for k in ipreds & jpreds])
236
+ )
237
+ directed_triangles += sum(
238
+ np.cbrt([(wt(i, j) * wt(k, i) * wt(j, k)) for k in ipreds & jsuccs])
239
+ )
240
+ directed_triangles += sum(
241
+ np.cbrt([(wt(i, j) * wt(i, k) * wt(k, j)) for k in isuccs & jpreds])
242
+ )
243
+ directed_triangles += sum(
244
+ np.cbrt([(wt(i, j) * wt(i, k) * wt(j, k)) for k in isuccs & jsuccs])
245
+ )
246
+
247
+ dtotal = len(ipreds) + len(isuccs)
248
+ dbidirectional = len(ipreds & isuccs)
249
+ yield (i, dtotal, dbidirectional, directed_triangles)
250
+
251
+
252
+ @nx._dispatch(edge_attrs="weight")
253
+ def average_clustering(G, nodes=None, weight=None, count_zeros=True):
254
+ r"""Compute the average clustering coefficient for the graph G.
255
+
256
+ The clustering coefficient for the graph is the average,
257
+
258
+ .. math::
259
+
260
+ C = \frac{1}{n}\sum_{v \in G} c_v,
261
+
262
+ where :math:`n` is the number of nodes in `G`.
263
+
264
+ Parameters
265
+ ----------
266
+ G : graph
267
+
268
+ nodes : container of nodes, optional (default=all nodes in G)
269
+ Compute average clustering for nodes in this container.
270
+
271
+ weight : string or None, optional (default=None)
272
+ The edge attribute that holds the numerical value used as a weight.
273
+ If None, then each edge has weight 1.
274
+
275
+ count_zeros : bool
276
+ If False include only the nodes with nonzero clustering in the average.
277
+
278
+ Returns
279
+ -------
280
+ avg : float
281
+ Average clustering
282
+
283
+ Examples
284
+ --------
285
+ >>> G = nx.complete_graph(5)
286
+ >>> print(nx.average_clustering(G))
287
+ 1.0
288
+
289
+ Notes
290
+ -----
291
+ This is a space saving routine; it might be faster
292
+ to use the clustering function to get a list and then take the average.
293
+
294
+ Self loops are ignored.
295
+
296
+ References
297
+ ----------
298
+ .. [1] Generalizations of the clustering coefficient to weighted
299
+ complex networks by J. Saramäki, M. Kivelä, J.-P. Onnela,
300
+ K. Kaski, and J. Kertész, Physical Review E, 75 027105 (2007).
301
+ http://jponnela.com/web_documents/a9.pdf
302
+ .. [2] Marcus Kaiser, Mean clustering coefficients: the role of isolated
303
+ nodes and leafs on clustering measures for small-world networks.
304
+ https://arxiv.org/abs/0802.2512
305
+ """
306
+ c = clustering(G, nodes, weight=weight).values()
307
+ if not count_zeros:
308
+ c = [v for v in c if abs(v) > 0]
309
+ return sum(c) / len(c)
310
+
311
+
312
+ @nx._dispatch(edge_attrs="weight")
313
+ def clustering(G, nodes=None, weight=None):
314
+ r"""Compute the clustering coefficient for nodes.
315
+
316
+ For unweighted graphs, the clustering of a node :math:`u`
317
+ is the fraction of possible triangles through that node that exist,
318
+
319
+ .. math::
320
+
321
+ c_u = \frac{2 T(u)}{deg(u)(deg(u)-1)},
322
+
323
+ where :math:`T(u)` is the number of triangles through node :math:`u` and
324
+ :math:`deg(u)` is the degree of :math:`u`.
325
+
326
+ For weighted graphs, there are several ways to define clustering [1]_.
327
+ the one used here is defined
328
+ as the geometric average of the subgraph edge weights [2]_,
329
+
330
+ .. math::
331
+
332
+ c_u = \frac{1}{deg(u)(deg(u)-1))}
333
+ \sum_{vw} (\hat{w}_{uv} \hat{w}_{uw} \hat{w}_{vw})^{1/3}.
334
+
335
+ The edge weights :math:`\hat{w}_{uv}` are normalized by the maximum weight
336
+ in the network :math:`\hat{w}_{uv} = w_{uv}/\max(w)`.
337
+
338
+ The value of :math:`c_u` is assigned to 0 if :math:`deg(u) < 2`.
339
+
340
+ Additionally, this weighted definition has been generalized to support negative edge weights [3]_.
341
+
342
+ For directed graphs, the clustering is similarly defined as the fraction
343
+ of all possible directed triangles or geometric average of the subgraph
344
+ edge weights for unweighted and weighted directed graph respectively [4]_.
345
+
346
+ .. math::
347
+
348
+ c_u = \frac{T(u)}{2(deg^{tot}(u)(deg^{tot}(u)-1) - 2deg^{\leftrightarrow}(u))},
349
+
350
+ where :math:`T(u)` is the number of directed triangles through node
351
+ :math:`u`, :math:`deg^{tot}(u)` is the sum of in degree and out degree of
352
+ :math:`u` and :math:`deg^{\leftrightarrow}(u)` is the reciprocal degree of
353
+ :math:`u`.
354
+
355
+
356
+ Parameters
357
+ ----------
358
+ G : graph
359
+
360
+ nodes : node, iterable of nodes, or None (default=None)
361
+ If a singleton node, return the number of triangles for that node.
362
+ If an iterable, compute the number of triangles for each of those nodes.
363
+ If `None` (the default) compute the number of triangles for all nodes in `G`.
364
+
365
+ weight : string or None, optional (default=None)
366
+ The edge attribute that holds the numerical value used as a weight.
367
+ If None, then each edge has weight 1.
368
+
369
+ Returns
370
+ -------
371
+ out : float, or dictionary
372
+ Clustering coefficient at specified nodes
373
+
374
+ Examples
375
+ --------
376
+ >>> G = nx.complete_graph(5)
377
+ >>> print(nx.clustering(G, 0))
378
+ 1.0
379
+ >>> print(nx.clustering(G))
380
+ {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}
381
+
382
+ Notes
383
+ -----
384
+ Self loops are ignored.
385
+
386
+ References
387
+ ----------
388
+ .. [1] Generalizations of the clustering coefficient to weighted
389
+ complex networks by J. Saramäki, M. Kivelä, J.-P. Onnela,
390
+ K. Kaski, and J. Kertész, Physical Review E, 75 027105 (2007).
391
+ http://jponnela.com/web_documents/a9.pdf
392
+ .. [2] Intensity and coherence of motifs in weighted complex
393
+ networks by J. P. Onnela, J. Saramäki, J. Kertész, and K. Kaski,
394
+ Physical Review E, 71(6), 065103 (2005).
395
+ .. [3] Generalization of Clustering Coefficients to Signed Correlation Networks
396
+ by G. Costantini and M. Perugini, PloS one, 9(2), e88669 (2014).
397
+ .. [4] Clustering in complex directed networks by G. Fagiolo,
398
+ Physical Review E, 76(2), 026107 (2007).
399
+ """
400
+ if G.is_directed():
401
+ if weight is not None:
402
+ td_iter = _directed_weighted_triangles_and_degree_iter(G, nodes, weight)
403
+ clusterc = {
404
+ v: 0 if t == 0 else t / ((dt * (dt - 1) - 2 * db) * 2)
405
+ for v, dt, db, t in td_iter
406
+ }
407
+ else:
408
+ td_iter = _directed_triangles_and_degree_iter(G, nodes)
409
+ clusterc = {
410
+ v: 0 if t == 0 else t / ((dt * (dt - 1) - 2 * db) * 2)
411
+ for v, dt, db, t in td_iter
412
+ }
413
+ else:
414
+ # The formula 2*T/(d*(d-1)) from docs is t/(d*(d-1)) here b/c t==2*T
415
+ if weight is not None:
416
+ td_iter = _weighted_triangles_and_degree_iter(G, nodes, weight)
417
+ clusterc = {v: 0 if t == 0 else t / (d * (d - 1)) for v, d, t in td_iter}
418
+ else:
419
+ td_iter = _triangles_and_degree_iter(G, nodes)
420
+ clusterc = {v: 0 if t == 0 else t / (d * (d - 1)) for v, d, t, _ in td_iter}
421
+ if nodes in G:
422
+ # Return the value of the sole entry in the dictionary.
423
+ return clusterc[nodes]
424
+ return clusterc
425
+
426
+
427
+ @nx._dispatch
428
+ def transitivity(G):
429
+ r"""Compute graph transitivity, the fraction of all possible triangles
430
+ present in G.
431
+
432
+ Possible triangles are identified by the number of "triads"
433
+ (two edges with a shared vertex).
434
+
435
+ The transitivity is
436
+
437
+ .. math::
438
+
439
+ T = 3\frac{\#triangles}{\#triads}.
440
+
441
+ Parameters
442
+ ----------
443
+ G : graph
444
+
445
+ Returns
446
+ -------
447
+ out : float
448
+ Transitivity
449
+
450
+ Examples
451
+ --------
452
+ >>> G = nx.complete_graph(5)
453
+ >>> print(nx.transitivity(G))
454
+ 1.0
455
+ """
456
+ triangles_contri = [
457
+ (t, d * (d - 1)) for v, d, t, _ in _triangles_and_degree_iter(G)
458
+ ]
459
+ # If the graph is empty
460
+ if len(triangles_contri) == 0:
461
+ return 0
462
+ triangles, contri = map(sum, zip(*triangles_contri))
463
+ return 0 if triangles == 0 else triangles / contri
464
+
465
+
466
+ @nx._dispatch
467
+ def square_clustering(G, nodes=None):
468
+ r"""Compute the squares clustering coefficient for nodes.
469
+
470
+ For each node return the fraction of possible squares that exist at
471
+ the node [1]_
472
+
473
+ .. math::
474
+ C_4(v) = \frac{ \sum_{u=1}^{k_v}
475
+ \sum_{w=u+1}^{k_v} q_v(u,w) }{ \sum_{u=1}^{k_v}
476
+ \sum_{w=u+1}^{k_v} [a_v(u,w) + q_v(u,w)]},
477
+
478
+ where :math:`q_v(u,w)` are the number of common neighbors of :math:`u` and
479
+ :math:`w` other than :math:`v` (ie squares), and :math:`a_v(u,w) = (k_u -
480
+ (1+q_v(u,w)+\theta_{uv})) + (k_w - (1+q_v(u,w)+\theta_{uw}))`, where
481
+ :math:`\theta_{uw} = 1` if :math:`u` and :math:`w` are connected and 0
482
+ otherwise. [2]_
483
+
484
+ Parameters
485
+ ----------
486
+ G : graph
487
+
488
+ nodes : container of nodes, optional (default=all nodes in G)
489
+ Compute clustering for nodes in this container.
490
+
491
+ Returns
492
+ -------
493
+ c4 : dictionary
494
+ A dictionary keyed by node with the square clustering coefficient value.
495
+
496
+ Examples
497
+ --------
498
+ >>> G = nx.complete_graph(5)
499
+ >>> print(nx.square_clustering(G, 0))
500
+ 1.0
501
+ >>> print(nx.square_clustering(G))
502
+ {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}
503
+
504
+ Notes
505
+ -----
506
+ While :math:`C_3(v)` (triangle clustering) gives the probability that
507
+ two neighbors of node v are connected with each other, :math:`C_4(v)` is
508
+ the probability that two neighbors of node v share a common
509
+ neighbor different from v. This algorithm can be applied to both
510
+ bipartite and unipartite networks.
511
+
512
+ References
513
+ ----------
514
+ .. [1] Pedro G. Lind, Marta C. González, and Hans J. Herrmann. 2005
515
+ Cycles and clustering in bipartite networks.
516
+ Physical Review E (72) 056127.
517
+ .. [2] Zhang, Peng et al. Clustering Coefficient and Community Structure of
518
+ Bipartite Networks. Physica A: Statistical Mechanics and its Applications 387.27 (2008): 6869–6875.
519
+ https://arxiv.org/abs/0710.0117v1
520
+ """
521
+ if nodes is None:
522
+ node_iter = G
523
+ else:
524
+ node_iter = G.nbunch_iter(nodes)
525
+ clustering = {}
526
+ for v in node_iter:
527
+ clustering[v] = 0
528
+ potential = 0
529
+ for u, w in combinations(G[v], 2):
530
+ squares = len((set(G[u]) & set(G[w])) - {v})
531
+ clustering[v] += squares
532
+ degm = squares + 1
533
+ if w in G[u]:
534
+ degm += 1
535
+ potential += (len(G[u]) - degm) + (len(G[w]) - degm) + squares
536
+ if potential > 0:
537
+ clustering[v] /= potential
538
+ if nodes in G:
539
+ # Return the value of the sole entry in the dictionary.
540
+ return clustering[nodes]
541
+ return clustering
542
+
543
+
544
+ @not_implemented_for("directed")
545
+ @nx._dispatch
546
+ def generalized_degree(G, nodes=None):
547
+ r"""Compute the generalized degree for nodes.
548
+
549
+ For each node, the generalized degree shows how many edges of given
550
+ triangle multiplicity the node is connected to. The triangle multiplicity
551
+ of an edge is the number of triangles an edge participates in. The
552
+ generalized degree of node :math:`i` can be written as a vector
553
+ :math:`\mathbf{k}_i=(k_i^{(0)}, \dotsc, k_i^{(N-2)})` where
554
+ :math:`k_i^{(j)}` is the number of edges attached to node :math:`i` that
555
+ participate in :math:`j` triangles.
556
+
557
+ Parameters
558
+ ----------
559
+ G : graph
560
+
561
+ nodes : container of nodes, optional (default=all nodes in G)
562
+ Compute the generalized degree for nodes in this container.
563
+
564
+ Returns
565
+ -------
566
+ out : Counter, or dictionary of Counters
567
+ Generalized degree of specified nodes. The Counter is keyed by edge
568
+ triangle multiplicity.
569
+
570
+ Examples
571
+ --------
572
+ >>> G = nx.complete_graph(5)
573
+ >>> print(nx.generalized_degree(G, 0))
574
+ Counter({3: 4})
575
+ >>> print(nx.generalized_degree(G))
576
+ {0: Counter({3: 4}), 1: Counter({3: 4}), 2: Counter({3: 4}), 3: Counter({3: 4}), 4: Counter({3: 4})}
577
+
578
+ To recover the number of triangles attached to a node:
579
+
580
+ >>> k1 = nx.generalized_degree(G, 0)
581
+ >>> sum([k * v for k, v in k1.items()]) / 2 == nx.triangles(G, 0)
582
+ True
583
+
584
+ Notes
585
+ -----
586
+ In a network of N nodes, the highest triangle multiplicity an edge can have
587
+ is N-2.
588
+
589
+ The return value does not include a `zero` entry if no edges of a
590
+ particular triangle multiplicity are present.
591
+
592
+ The number of triangles node :math:`i` is attached to can be recovered from
593
+ the generalized degree :math:`\mathbf{k}_i=(k_i^{(0)}, \dotsc,
594
+ k_i^{(N-2)})` by :math:`(k_i^{(1)}+2k_i^{(2)}+\dotsc +(N-2)k_i^{(N-2)})/2`.
595
+
596
+ References
597
+ ----------
598
+ .. [1] Networks with arbitrary edge multiplicities by V. Zlatić,
599
+ D. Garlaschelli and G. Caldarelli, EPL (Europhysics Letters),
600
+ Volume 97, Number 2 (2012).
601
+ https://iopscience.iop.org/article/10.1209/0295-5075/97/28005
602
+ """
603
+ if nodes in G:
604
+ return next(_triangles_and_degree_iter(G, nodes))[3]
605
+ return {v: gd for v, d, t, gd in _triangles_and_degree_iter(G, nodes)}
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/centrality.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions for computing communities based on centrality notions."""
2
+
3
+ import networkx as nx
4
+
5
+ __all__ = ["girvan_newman"]
6
+
7
+
8
+ @nx._dispatch(preserve_edge_attrs="most_valuable_edge")
9
+ def girvan_newman(G, most_valuable_edge=None):
10
+ """Finds communities in a graph using the Girvan–Newman method.
11
+
12
+ Parameters
13
+ ----------
14
+ G : NetworkX graph
15
+
16
+ most_valuable_edge : function
17
+ Function that takes a graph as input and outputs an edge. The
18
+ edge returned by this function will be recomputed and removed at
19
+ each iteration of the algorithm.
20
+
21
+ If not specified, the edge with the highest
22
+ :func:`networkx.edge_betweenness_centrality` will be used.
23
+
24
+ Returns
25
+ -------
26
+ iterator
27
+ Iterator over tuples of sets of nodes in `G`. Each set of node
28
+ is a community, each tuple is a sequence of communities at a
29
+ particular level of the algorithm.
30
+
31
+ Examples
32
+ --------
33
+ To get the first pair of communities::
34
+
35
+ >>> G = nx.path_graph(10)
36
+ >>> comp = nx.community.girvan_newman(G)
37
+ >>> tuple(sorted(c) for c in next(comp))
38
+ ([0, 1, 2, 3, 4], [5, 6, 7, 8, 9])
39
+
40
+ To get only the first *k* tuples of communities, use
41
+ :func:`itertools.islice`::
42
+
43
+ >>> import itertools
44
+ >>> G = nx.path_graph(8)
45
+ >>> k = 2
46
+ >>> comp = nx.community.girvan_newman(G)
47
+ >>> for communities in itertools.islice(comp, k):
48
+ ... print(tuple(sorted(c) for c in communities))
49
+ ...
50
+ ([0, 1, 2, 3], [4, 5, 6, 7])
51
+ ([0, 1], [2, 3], [4, 5, 6, 7])
52
+
53
+ To stop getting tuples of communities once the number of communities
54
+ is greater than *k*, use :func:`itertools.takewhile`::
55
+
56
+ >>> import itertools
57
+ >>> G = nx.path_graph(8)
58
+ >>> k = 4
59
+ >>> comp = nx.community.girvan_newman(G)
60
+ >>> limited = itertools.takewhile(lambda c: len(c) <= k, comp)
61
+ >>> for communities in limited:
62
+ ... print(tuple(sorted(c) for c in communities))
63
+ ...
64
+ ([0, 1, 2, 3], [4, 5, 6, 7])
65
+ ([0, 1], [2, 3], [4, 5, 6, 7])
66
+ ([0, 1], [2, 3], [4, 5], [6, 7])
67
+
68
+ To just choose an edge to remove based on the weight::
69
+
70
+ >>> from operator import itemgetter
71
+ >>> G = nx.path_graph(10)
72
+ >>> edges = G.edges()
73
+ >>> nx.set_edge_attributes(G, {(u, v): v for u, v in edges}, "weight")
74
+ >>> def heaviest(G):
75
+ ... u, v, w = max(G.edges(data="weight"), key=itemgetter(2))
76
+ ... return (u, v)
77
+ ...
78
+ >>> comp = nx.community.girvan_newman(G, most_valuable_edge=heaviest)
79
+ >>> tuple(sorted(c) for c in next(comp))
80
+ ([0, 1, 2, 3, 4, 5, 6, 7, 8], [9])
81
+
82
+ To utilize edge weights when choosing an edge with, for example, the
83
+ highest betweenness centrality::
84
+
85
+ >>> from networkx import edge_betweenness_centrality as betweenness
86
+ >>> def most_central_edge(G):
87
+ ... centrality = betweenness(G, weight="weight")
88
+ ... return max(centrality, key=centrality.get)
89
+ ...
90
+ >>> G = nx.path_graph(10)
91
+ >>> comp = nx.community.girvan_newman(G, most_valuable_edge=most_central_edge)
92
+ >>> tuple(sorted(c) for c in next(comp))
93
+ ([0, 1, 2, 3, 4], [5, 6, 7, 8, 9])
94
+
95
+ To specify a different ranking algorithm for edges, use the
96
+ `most_valuable_edge` keyword argument::
97
+
98
+ >>> from networkx import edge_betweenness_centrality
99
+ >>> from random import random
100
+ >>> def most_central_edge(G):
101
+ ... centrality = edge_betweenness_centrality(G)
102
+ ... max_cent = max(centrality.values())
103
+ ... # Scale the centrality values so they are between 0 and 1,
104
+ ... # and add some random noise.
105
+ ... centrality = {e: c / max_cent for e, c in centrality.items()}
106
+ ... # Add some random noise.
107
+ ... centrality = {e: c + random() for e, c in centrality.items()}
108
+ ... return max(centrality, key=centrality.get)
109
+ ...
110
+ >>> G = nx.path_graph(10)
111
+ >>> comp = nx.community.girvan_newman(G, most_valuable_edge=most_central_edge)
112
+
113
+ Notes
114
+ -----
115
+ The Girvan–Newman algorithm detects communities by progressively
116
+ removing edges from the original graph. The algorithm removes the
117
+ "most valuable" edge, traditionally the edge with the highest
118
+ betweenness centrality, at each step. As the graph breaks down into
119
+ pieces, the tightly knit community structure is exposed and the
120
+ result can be depicted as a dendrogram.
121
+
122
+ """
123
+ # If the graph is already empty, simply return its connected
124
+ # components.
125
+ if G.number_of_edges() == 0:
126
+ yield tuple(nx.connected_components(G))
127
+ return
128
+ # If no function is provided for computing the most valuable edge,
129
+ # use the edge betweenness centrality.
130
+ if most_valuable_edge is None:
131
+
132
+ def most_valuable_edge(G):
133
+ """Returns the edge with the highest betweenness centrality
134
+ in the graph `G`.
135
+
136
+ """
137
+ # We have guaranteed that the graph is non-empty, so this
138
+ # dictionary will never be empty.
139
+ betweenness = nx.edge_betweenness_centrality(G)
140
+ return max(betweenness, key=betweenness.get)
141
+
142
+ # The copy of G here must include the edge weight data.
143
+ g = G.copy().to_undirected()
144
+ # Self-loops must be removed because their removal has no effect on
145
+ # the connected components of the graph.
146
+ g.remove_edges_from(nx.selfloop_edges(g))
147
+ while g.number_of_edges() > 0:
148
+ yield _without_most_central_edges(g, most_valuable_edge)
149
+
150
+
151
+ def _without_most_central_edges(G, most_valuable_edge):
152
+ """Returns the connected components of the graph that results from
153
+ repeatedly removing the most "valuable" edge in the graph.
154
+
155
+ `G` must be a non-empty graph. This function modifies the graph `G`
156
+ in-place; that is, it removes edges on the graph `G`.
157
+
158
+ `most_valuable_edge` is a function that takes the graph `G` as input
159
+ (or a subgraph with one or more edges of `G` removed) and returns an
160
+ edge. That edge will be removed and this process will be repeated
161
+ until the number of connected components in the graph increases.
162
+
163
+ """
164
+ original_num_components = nx.number_connected_components(G)
165
+ num_new_components = original_num_components
166
+ while num_new_components <= original_num_components:
167
+ edge = most_valuable_edge(G)
168
+ G.remove_edge(*edge)
169
+ new_components = tuple(nx.connected_components(G))
170
+ num_new_components = len(new_components)
171
+ return new_components
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/louvain.py ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Function for detecting communities based on Louvain Community Detection
2
+ Algorithm"""
3
+
4
+ from collections import defaultdict, deque
5
+
6
+ import networkx as nx
7
+ from networkx.algorithms.community import modularity
8
+ from networkx.utils import py_random_state
9
+
10
+ __all__ = ["louvain_communities", "louvain_partitions"]
11
+
12
+
13
+ @py_random_state("seed")
14
+ @nx._dispatch(edge_attrs="weight")
15
+ def louvain_communities(
16
+ G, weight="weight", resolution=1, threshold=0.0000001, seed=None
17
+ ):
18
+ r"""Find the best partition of a graph using the Louvain Community Detection
19
+ Algorithm.
20
+
21
+ Louvain Community Detection Algorithm is a simple method to extract the community
22
+ structure of a network. This is a heuristic method based on modularity optimization. [1]_
23
+
24
+ The algorithm works in 2 steps. On the first step it assigns every node to be
25
+ in its own community and then for each node it tries to find the maximum positive
26
+ modularity gain by moving each node to all of its neighbor communities. If no positive
27
+ gain is achieved the node remains in its original community.
28
+
29
+ The modularity gain obtained by moving an isolated node $i$ into a community $C$ can
30
+ easily be calculated by the following formula (combining [1]_ [2]_ and some algebra):
31
+
32
+ .. math::
33
+ \Delta Q = \frac{k_{i,in}}{2m} - \gamma\frac{ \Sigma_{tot} \cdot k_i}{2m^2}
34
+
35
+ where $m$ is the size of the graph, $k_{i,in}$ is the sum of the weights of the links
36
+ from $i$ to nodes in $C$, $k_i$ is the sum of the weights of the links incident to node $i$,
37
+ $\Sigma_{tot}$ is the sum of the weights of the links incident to nodes in $C$ and $\gamma$
38
+ is the resolution parameter.
39
+
40
+ For the directed case the modularity gain can be computed using this formula according to [3]_
41
+
42
+ .. math::
43
+ \Delta Q = \frac{k_{i,in}}{m}
44
+ - \gamma\frac{k_i^{out} \cdot\Sigma_{tot}^{in} + k_i^{in} \cdot \Sigma_{tot}^{out}}{m^2}
45
+
46
+ where $k_i^{out}$, $k_i^{in}$ are the outer and inner weighted degrees of node $i$ and
47
+ $\Sigma_{tot}^{in}$, $\Sigma_{tot}^{out}$ are the sum of in-going and out-going links incident
48
+ to nodes in $C$.
49
+
50
+ The first phase continues until no individual move can improve the modularity.
51
+
52
+ The second phase consists in building a new network whose nodes are now the communities
53
+ found in the first phase. To do so, the weights of the links between the new nodes are given by
54
+ the sum of the weight of the links between nodes in the corresponding two communities. Once this
55
+ phase is complete it is possible to reapply the first phase creating bigger communities with
56
+ increased modularity.
57
+
58
+ The above two phases are executed until no modularity gain is achieved (or is less than
59
+ the `threshold`).
60
+
61
+ Be careful with self-loops in the input graph. These are treated as
62
+ previously reduced communities -- as if the process had been started
63
+ in the middle of the algorithm. Large self-loop edge weights thus
64
+ represent strong communities and in practice may be hard to add
65
+ other nodes to. If your input graph edge weights for self-loops
66
+ do not represent already reduced communities you may want to remove
67
+ the self-loops before inputting that graph.
68
+
69
+ Parameters
70
+ ----------
71
+ G : NetworkX graph
72
+ weight : string or None, optional (default="weight")
73
+ The name of an edge attribute that holds the numerical value
74
+ used as a weight. If None then each edge has weight 1.
75
+ resolution : float, optional (default=1)
76
+ If resolution is less than 1, the algorithm favors larger communities.
77
+ Greater than 1 favors smaller communities
78
+ threshold : float, optional (default=0.0000001)
79
+ Modularity gain threshold for each level. If the gain of modularity
80
+ between 2 levels of the algorithm is less than the given threshold
81
+ then the algorithm stops and returns the resulting communities.
82
+ seed : integer, random_state, or None (default)
83
+ Indicator of random number generation state.
84
+ See :ref:`Randomness<randomness>`.
85
+
86
+ Returns
87
+ -------
88
+ list
89
+ A list of sets (partition of `G`). Each set represents one community and contains
90
+ all the nodes that constitute it.
91
+
92
+ Examples
93
+ --------
94
+ >>> import networkx as nx
95
+ >>> G = nx.petersen_graph()
96
+ >>> nx.community.louvain_communities(G, seed=123)
97
+ [{0, 4, 5, 7, 9}, {1, 2, 3, 6, 8}]
98
+
99
+ Notes
100
+ -----
101
+ The order in which the nodes are considered can affect the final output. In the algorithm
102
+ the ordering happens using a random shuffle.
103
+
104
+ References
105
+ ----------
106
+ .. [1] Blondel, V.D. et al. Fast unfolding of communities in
107
+ large networks. J. Stat. Mech 10008, 1-12(2008). https://doi.org/10.1088/1742-5468/2008/10/P10008
108
+ .. [2] Traag, V.A., Waltman, L. & van Eck, N.J. From Louvain to Leiden: guaranteeing
109
+ well-connected communities. Sci Rep 9, 5233 (2019). https://doi.org/10.1038/s41598-019-41695-z
110
+ .. [3] Nicolas Dugué, Anthony Perez. Directed Louvain : maximizing modularity in directed networks.
111
+ [Research Report] Université d’Orléans. 2015. hal-01231784. https://hal.archives-ouvertes.fr/hal-01231784
112
+
113
+ See Also
114
+ --------
115
+ louvain_partitions
116
+ """
117
+
118
+ d = louvain_partitions(G, weight, resolution, threshold, seed)
119
+ q = deque(d, maxlen=1)
120
+ return q.pop()
121
+
122
+
123
+ @py_random_state("seed")
124
+ @nx._dispatch(edge_attrs="weight")
125
+ def louvain_partitions(
126
+ G, weight="weight", resolution=1, threshold=0.0000001, seed=None
127
+ ):
128
+ """Yields partitions for each level of the Louvain Community Detection Algorithm
129
+
130
+ Louvain Community Detection Algorithm is a simple method to extract the community
131
+ structure of a network. This is a heuristic method based on modularity optimization. [1]_
132
+
133
+ The partitions at each level (step of the algorithm) form a dendrogram of communities.
134
+ A dendrogram is a diagram representing a tree and each level represents
135
+ a partition of the G graph. The top level contains the smallest communities
136
+ and as you traverse to the bottom of the tree the communities get bigger
137
+ and the overall modularity increases making the partition better.
138
+
139
+ Each level is generated by executing the two phases of the Louvain Community
140
+ Detection Algorithm.
141
+
142
+ Be careful with self-loops in the input graph. These are treated as
143
+ previously reduced communities -- as if the process had been started
144
+ in the middle of the algorithm. Large self-loop edge weights thus
145
+ represent strong communities and in practice may be hard to add
146
+ other nodes to. If your input graph edge weights for self-loops
147
+ do not represent already reduced communities you may want to remove
148
+ the self-loops before inputting that graph.
149
+
150
+ Parameters
151
+ ----------
152
+ G : NetworkX graph
153
+ weight : string or None, optional (default="weight")
154
+ The name of an edge attribute that holds the numerical value
155
+ used as a weight. If None then each edge has weight 1.
156
+ resolution : float, optional (default=1)
157
+ If resolution is less than 1, the algorithm favors larger communities.
158
+ Greater than 1 favors smaller communities
159
+ threshold : float, optional (default=0.0000001)
160
+ Modularity gain threshold for each level. If the gain of modularity
161
+ between 2 levels of the algorithm is less than the given threshold
162
+ then the algorithm stops and returns the resulting communities.
163
+ seed : integer, random_state, or None (default)
164
+ Indicator of random number generation state.
165
+ See :ref:`Randomness<randomness>`.
166
+
167
+ Yields
168
+ ------
169
+ list
170
+ A list of sets (partition of `G`). Each set represents one community and contains
171
+ all the nodes that constitute it.
172
+
173
+ References
174
+ ----------
175
+ .. [1] Blondel, V.D. et al. Fast unfolding of communities in
176
+ large networks. J. Stat. Mech 10008, 1-12(2008)
177
+
178
+ See Also
179
+ --------
180
+ louvain_communities
181
+ """
182
+
183
+ partition = [{u} for u in G.nodes()]
184
+ if nx.is_empty(G):
185
+ yield partition
186
+ return
187
+ mod = modularity(G, partition, resolution=resolution, weight=weight)
188
+ is_directed = G.is_directed()
189
+ if G.is_multigraph():
190
+ graph = _convert_multigraph(G, weight, is_directed)
191
+ else:
192
+ graph = G.__class__()
193
+ graph.add_nodes_from(G)
194
+ graph.add_weighted_edges_from(G.edges(data=weight, default=1))
195
+
196
+ m = graph.size(weight="weight")
197
+ partition, inner_partition, improvement = _one_level(
198
+ graph, m, partition, resolution, is_directed, seed
199
+ )
200
+ improvement = True
201
+ while improvement:
202
+ # gh-5901 protect the sets in the yielded list from further manipulation here
203
+ yield [s.copy() for s in partition]
204
+ new_mod = modularity(
205
+ graph, inner_partition, resolution=resolution, weight="weight"
206
+ )
207
+ if new_mod - mod <= threshold:
208
+ return
209
+ mod = new_mod
210
+ graph = _gen_graph(graph, inner_partition)
211
+ partition, inner_partition, improvement = _one_level(
212
+ graph, m, partition, resolution, is_directed, seed
213
+ )
214
+
215
+
216
+ def _one_level(G, m, partition, resolution=1, is_directed=False, seed=None):
217
+ """Calculate one level of the Louvain partitions tree
218
+
219
+ Parameters
220
+ ----------
221
+ G : NetworkX Graph/DiGraph
222
+ The graph from which to detect communities
223
+ m : number
224
+ The size of the graph `G`.
225
+ partition : list of sets of nodes
226
+ A valid partition of the graph `G`
227
+ resolution : positive number
228
+ The resolution parameter for computing the modularity of a partition
229
+ is_directed : bool
230
+ True if `G` is a directed graph.
231
+ seed : integer, random_state, or None (default)
232
+ Indicator of random number generation state.
233
+ See :ref:`Randomness<randomness>`.
234
+
235
+ """
236
+ node2com = {u: i for i, u in enumerate(G.nodes())}
237
+ inner_partition = [{u} for u in G.nodes()]
238
+ if is_directed:
239
+ in_degrees = dict(G.in_degree(weight="weight"))
240
+ out_degrees = dict(G.out_degree(weight="weight"))
241
+ Stot_in = list(in_degrees.values())
242
+ Stot_out = list(out_degrees.values())
243
+ # Calculate weights for both in and out neighbours without considering self-loops
244
+ nbrs = {}
245
+ for u in G:
246
+ nbrs[u] = defaultdict(float)
247
+ for _, n, wt in G.out_edges(u, data="weight"):
248
+ if u != n:
249
+ nbrs[u][n] += wt
250
+ for n, _, wt in G.in_edges(u, data="weight"):
251
+ if u != n:
252
+ nbrs[u][n] += wt
253
+ else:
254
+ degrees = dict(G.degree(weight="weight"))
255
+ Stot = list(degrees.values())
256
+ nbrs = {u: {v: data["weight"] for v, data in G[u].items() if v != u} for u in G}
257
+ rand_nodes = list(G.nodes)
258
+ seed.shuffle(rand_nodes)
259
+ nb_moves = 1
260
+ improvement = False
261
+ while nb_moves > 0:
262
+ nb_moves = 0
263
+ for u in rand_nodes:
264
+ best_mod = 0
265
+ best_com = node2com[u]
266
+ weights2com = _neighbor_weights(nbrs[u], node2com)
267
+ if is_directed:
268
+ in_degree = in_degrees[u]
269
+ out_degree = out_degrees[u]
270
+ Stot_in[best_com] -= in_degree
271
+ Stot_out[best_com] -= out_degree
272
+ remove_cost = (
273
+ -weights2com[best_com] / m
274
+ + resolution
275
+ * (out_degree * Stot_in[best_com] + in_degree * Stot_out[best_com])
276
+ / m**2
277
+ )
278
+ else:
279
+ degree = degrees[u]
280
+ Stot[best_com] -= degree
281
+ remove_cost = -weights2com[best_com] / m + resolution * (
282
+ Stot[best_com] * degree
283
+ ) / (2 * m**2)
284
+ for nbr_com, wt in weights2com.items():
285
+ if is_directed:
286
+ gain = (
287
+ remove_cost
288
+ + wt / m
289
+ - resolution
290
+ * (
291
+ out_degree * Stot_in[nbr_com]
292
+ + in_degree * Stot_out[nbr_com]
293
+ )
294
+ / m**2
295
+ )
296
+ else:
297
+ gain = (
298
+ remove_cost
299
+ + wt / m
300
+ - resolution * (Stot[nbr_com] * degree) / (2 * m**2)
301
+ )
302
+ if gain > best_mod:
303
+ best_mod = gain
304
+ best_com = nbr_com
305
+ if is_directed:
306
+ Stot_in[best_com] += in_degree
307
+ Stot_out[best_com] += out_degree
308
+ else:
309
+ Stot[best_com] += degree
310
+ if best_com != node2com[u]:
311
+ com = G.nodes[u].get("nodes", {u})
312
+ partition[node2com[u]].difference_update(com)
313
+ inner_partition[node2com[u]].remove(u)
314
+ partition[best_com].update(com)
315
+ inner_partition[best_com].add(u)
316
+ improvement = True
317
+ nb_moves += 1
318
+ node2com[u] = best_com
319
+ partition = list(filter(len, partition))
320
+ inner_partition = list(filter(len, inner_partition))
321
+ return partition, inner_partition, improvement
322
+
323
+
324
+ def _neighbor_weights(nbrs, node2com):
325
+ """Calculate weights between node and its neighbor communities.
326
+
327
+ Parameters
328
+ ----------
329
+ nbrs : dictionary
330
+ Dictionary with nodes' neighbours as keys and their edge weight as value.
331
+ node2com : dictionary
332
+ Dictionary with all graph's nodes as keys and their community index as value.
333
+
334
+ """
335
+ weights = defaultdict(float)
336
+ for nbr, wt in nbrs.items():
337
+ weights[node2com[nbr]] += wt
338
+ return weights
339
+
340
+
341
+ def _gen_graph(G, partition):
342
+ """Generate a new graph based on the partitions of a given graph"""
343
+ H = G.__class__()
344
+ node2com = {}
345
+ for i, part in enumerate(partition):
346
+ nodes = set()
347
+ for node in part:
348
+ node2com[node] = i
349
+ nodes.update(G.nodes[node].get("nodes", {node}))
350
+ H.add_node(i, nodes=nodes)
351
+
352
+ for node1, node2, wt in G.edges(data=True):
353
+ wt = wt["weight"]
354
+ com1 = node2com[node1]
355
+ com2 = node2com[node2]
356
+ temp = H.get_edge_data(com1, com2, {"weight": 0})["weight"]
357
+ H.add_edge(com1, com2, weight=wt + temp)
358
+ return H
359
+
360
+
361
+ def _convert_multigraph(G, weight, is_directed):
362
+ """Convert a Multigraph to normal Graph"""
363
+ if is_directed:
364
+ H = nx.DiGraph()
365
+ else:
366
+ H = nx.Graph()
367
+ H.add_nodes_from(G)
368
+ for u, v, wt in G.edges(data=weight, default=1):
369
+ if H.has_edge(u, v):
370
+ H[u][v]["weight"] += wt
371
+ else:
372
+ H.add_edge(u, v, weight=wt)
373
+ return H
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__init__.py ADDED
File without changes
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (236 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/dag.py ADDED
@@ -0,0 +1,1258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Algorithms for directed acyclic graphs (DAGs).
2
+
3
+ Note that most of these functions are only guaranteed to work for DAGs.
4
+ In general, these functions do not check for acyclic-ness, so it is up
5
+ to the user to check for that.
6
+ """
7
+
8
+ import heapq
9
+ from collections import deque
10
+ from functools import partial
11
+ from itertools import chain, combinations, product, starmap
12
+ from math import gcd
13
+
14
+ import networkx as nx
15
+ from networkx.utils import arbitrary_element, not_implemented_for, pairwise
16
+
17
+ __all__ = [
18
+ "descendants",
19
+ "ancestors",
20
+ "topological_sort",
21
+ "lexicographical_topological_sort",
22
+ "all_topological_sorts",
23
+ "topological_generations",
24
+ "is_directed_acyclic_graph",
25
+ "is_aperiodic",
26
+ "transitive_closure",
27
+ "transitive_closure_dag",
28
+ "transitive_reduction",
29
+ "antichains",
30
+ "dag_longest_path",
31
+ "dag_longest_path_length",
32
+ "dag_to_branching",
33
+ "compute_v_structures",
34
+ ]
35
+
36
+ chaini = chain.from_iterable
37
+
38
+
39
+ @nx._dispatch
40
+ def descendants(G, source):
41
+ """Returns all nodes reachable from `source` in `G`.
42
+
43
+ Parameters
44
+ ----------
45
+ G : NetworkX Graph
46
+ source : node in `G`
47
+
48
+ Returns
49
+ -------
50
+ set()
51
+ The descendants of `source` in `G`
52
+
53
+ Raises
54
+ ------
55
+ NetworkXError
56
+ If node `source` is not in `G`.
57
+
58
+ Examples
59
+ --------
60
+ >>> DG = nx.path_graph(5, create_using=nx.DiGraph)
61
+ >>> sorted(nx.descendants(DG, 2))
62
+ [3, 4]
63
+
64
+ The `source` node is not a descendant of itself, but can be included manually:
65
+
66
+ >>> sorted(nx.descendants(DG, 2) | {2})
67
+ [2, 3, 4]
68
+
69
+ See also
70
+ --------
71
+ ancestors
72
+ """
73
+ return {child for parent, child in nx.bfs_edges(G, source)}
74
+
75
+
76
+ @nx._dispatch
77
+ def ancestors(G, source):
78
+ """Returns all nodes having a path to `source` in `G`.
79
+
80
+ Parameters
81
+ ----------
82
+ G : NetworkX Graph
83
+ source : node in `G`
84
+
85
+ Returns
86
+ -------
87
+ set()
88
+ The ancestors of `source` in `G`
89
+
90
+ Raises
91
+ ------
92
+ NetworkXError
93
+ If node `source` is not in `G`.
94
+
95
+ Examples
96
+ --------
97
+ >>> DG = nx.path_graph(5, create_using=nx.DiGraph)
98
+ >>> sorted(nx.ancestors(DG, 2))
99
+ [0, 1]
100
+
101
+ The `source` node is not an ancestor of itself, but can be included manually:
102
+
103
+ >>> sorted(nx.ancestors(DG, 2) | {2})
104
+ [0, 1, 2]
105
+
106
+ See also
107
+ --------
108
+ descendants
109
+ """
110
+ return {child for parent, child in nx.bfs_edges(G, source, reverse=True)}
111
+
112
+
113
+ @nx._dispatch
114
+ def has_cycle(G):
115
+ """Decides whether the directed graph has a cycle."""
116
+ try:
117
+ # Feed the entire iterator into a zero-length deque.
118
+ deque(topological_sort(G), maxlen=0)
119
+ except nx.NetworkXUnfeasible:
120
+ return True
121
+ else:
122
+ return False
123
+
124
+
125
+ @nx._dispatch
126
+ def is_directed_acyclic_graph(G):
127
+ """Returns True if the graph `G` is a directed acyclic graph (DAG) or
128
+ False if not.
129
+
130
+ Parameters
131
+ ----------
132
+ G : NetworkX graph
133
+
134
+ Returns
135
+ -------
136
+ bool
137
+ True if `G` is a DAG, False otherwise
138
+
139
+ Examples
140
+ --------
141
+ Undirected graph::
142
+
143
+ >>> G = nx.Graph([(1, 2), (2, 3)])
144
+ >>> nx.is_directed_acyclic_graph(G)
145
+ False
146
+
147
+ Directed graph with cycle::
148
+
149
+ >>> G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
150
+ >>> nx.is_directed_acyclic_graph(G)
151
+ False
152
+
153
+ Directed acyclic graph::
154
+
155
+ >>> G = nx.DiGraph([(1, 2), (2, 3)])
156
+ >>> nx.is_directed_acyclic_graph(G)
157
+ True
158
+
159
+ See also
160
+ --------
161
+ topological_sort
162
+ """
163
+ return G.is_directed() and not has_cycle(G)
164
+
165
+
166
+ @nx._dispatch
167
+ def topological_generations(G):
168
+ """Stratifies a DAG into generations.
169
+
170
+ A topological generation is node collection in which ancestors of a node in each
171
+ generation are guaranteed to be in a previous generation, and any descendants of
172
+ a node are guaranteed to be in a following generation. Nodes are guaranteed to
173
+ be in the earliest possible generation that they can belong to.
174
+
175
+ Parameters
176
+ ----------
177
+ G : NetworkX digraph
178
+ A directed acyclic graph (DAG)
179
+
180
+ Yields
181
+ ------
182
+ sets of nodes
183
+ Yields sets of nodes representing each generation.
184
+
185
+ Raises
186
+ ------
187
+ NetworkXError
188
+ Generations are defined for directed graphs only. If the graph
189
+ `G` is undirected, a :exc:`NetworkXError` is raised.
190
+
191
+ NetworkXUnfeasible
192
+ If `G` is not a directed acyclic graph (DAG) no topological generations
193
+ exist and a :exc:`NetworkXUnfeasible` exception is raised. This can also
194
+ be raised if `G` is changed while the returned iterator is being processed
195
+
196
+ RuntimeError
197
+ If `G` is changed while the returned iterator is being processed.
198
+
199
+ Examples
200
+ --------
201
+ >>> DG = nx.DiGraph([(2, 1), (3, 1)])
202
+ >>> [sorted(generation) for generation in nx.topological_generations(DG)]
203
+ [[2, 3], [1]]
204
+
205
+ Notes
206
+ -----
207
+ The generation in which a node resides can also be determined by taking the
208
+ max-path-distance from the node to the farthest leaf node. That value can
209
+ be obtained with this function using `enumerate(topological_generations(G))`.
210
+
211
+ See also
212
+ --------
213
+ topological_sort
214
+ """
215
+ if not G.is_directed():
216
+ raise nx.NetworkXError("Topological sort not defined on undirected graphs.")
217
+
218
+ multigraph = G.is_multigraph()
219
+ indegree_map = {v: d for v, d in G.in_degree() if d > 0}
220
+ zero_indegree = [v for v, d in G.in_degree() if d == 0]
221
+
222
+ while zero_indegree:
223
+ this_generation = zero_indegree
224
+ zero_indegree = []
225
+ for node in this_generation:
226
+ if node not in G:
227
+ raise RuntimeError("Graph changed during iteration")
228
+ for child in G.neighbors(node):
229
+ try:
230
+ indegree_map[child] -= len(G[node][child]) if multigraph else 1
231
+ except KeyError as err:
232
+ raise RuntimeError("Graph changed during iteration") from err
233
+ if indegree_map[child] == 0:
234
+ zero_indegree.append(child)
235
+ del indegree_map[child]
236
+ yield this_generation
237
+
238
+ if indegree_map:
239
+ raise nx.NetworkXUnfeasible(
240
+ "Graph contains a cycle or graph changed during iteration"
241
+ )
242
+
243
+
244
+ @nx._dispatch
245
+ def topological_sort(G):
246
+ """Returns a generator of nodes in topologically sorted order.
247
+
248
+ A topological sort is a nonunique permutation of the nodes of a
249
+ directed graph such that an edge from u to v implies that u
250
+ appears before v in the topological sort order. This ordering is
251
+ valid only if the graph has no directed cycles.
252
+
253
+ Parameters
254
+ ----------
255
+ G : NetworkX digraph
256
+ A directed acyclic graph (DAG)
257
+
258
+ Yields
259
+ ------
260
+ nodes
261
+ Yields the nodes in topological sorted order.
262
+
263
+ Raises
264
+ ------
265
+ NetworkXError
266
+ Topological sort is defined for directed graphs only. If the graph `G`
267
+ is undirected, a :exc:`NetworkXError` is raised.
268
+
269
+ NetworkXUnfeasible
270
+ If `G` is not a directed acyclic graph (DAG) no topological sort exists
271
+ and a :exc:`NetworkXUnfeasible` exception is raised. This can also be
272
+ raised if `G` is changed while the returned iterator is being processed
273
+
274
+ RuntimeError
275
+ If `G` is changed while the returned iterator is being processed.
276
+
277
+ Examples
278
+ --------
279
+ To get the reverse order of the topological sort:
280
+
281
+ >>> DG = nx.DiGraph([(1, 2), (2, 3)])
282
+ >>> list(reversed(list(nx.topological_sort(DG))))
283
+ [3, 2, 1]
284
+
285
+ If your DiGraph naturally has the edges representing tasks/inputs
286
+ and nodes representing people/processes that initiate tasks, then
287
+ topological_sort is not quite what you need. You will have to change
288
+ the tasks to nodes with dependence reflected by edges. The result is
289
+ a kind of topological sort of the edges. This can be done
290
+ with :func:`networkx.line_graph` as follows:
291
+
292
+ >>> list(nx.topological_sort(nx.line_graph(DG)))
293
+ [(1, 2), (2, 3)]
294
+
295
+ Notes
296
+ -----
297
+ This algorithm is based on a description and proof in
298
+ "Introduction to Algorithms: A Creative Approach" [1]_ .
299
+
300
+ See also
301
+ --------
302
+ is_directed_acyclic_graph, lexicographical_topological_sort
303
+
304
+ References
305
+ ----------
306
+ .. [1] Manber, U. (1989).
307
+ *Introduction to Algorithms - A Creative Approach.* Addison-Wesley.
308
+ """
309
+ for generation in nx.topological_generations(G):
310
+ yield from generation
311
+
312
+
313
+ @nx._dispatch
314
+ def lexicographical_topological_sort(G, key=None):
315
+ """Generate the nodes in the unique lexicographical topological sort order.
316
+
317
+ Generates a unique ordering of nodes by first sorting topologically (for which there are often
318
+ multiple valid orderings) and then additionally by sorting lexicographically.
319
+
320
+ A topological sort arranges the nodes of a directed graph so that the
321
+ upstream node of each directed edge precedes the downstream node.
322
+ It is always possible to find a solution for directed graphs that have no cycles.
323
+ There may be more than one valid solution.
324
+
325
+ Lexicographical sorting is just sorting alphabetically. It is used here to break ties in the
326
+ topological sort and to determine a single, unique ordering. This can be useful in comparing
327
+ sort results.
328
+
329
+ The lexicographical order can be customized by providing a function to the `key=` parameter.
330
+ The definition of the key function is the same as used in python's built-in `sort()`.
331
+ The function takes a single argument and returns a key to use for sorting purposes.
332
+
333
+ Lexicographical sorting can fail if the node names are un-sortable. See the example below.
334
+ The solution is to provide a function to the `key=` argument that returns sortable keys.
335
+
336
+
337
+ Parameters
338
+ ----------
339
+ G : NetworkX digraph
340
+ A directed acyclic graph (DAG)
341
+
342
+ key : function, optional
343
+ A function of one argument that converts a node name to a comparison key.
344
+ It defines and resolves ambiguities in the sort order. Defaults to the identity function.
345
+
346
+ Yields
347
+ ------
348
+ nodes
349
+ Yields the nodes of G in lexicographical topological sort order.
350
+
351
+ Raises
352
+ ------
353
+ NetworkXError
354
+ Topological sort is defined for directed graphs only. If the graph `G`
355
+ is undirected, a :exc:`NetworkXError` is raised.
356
+
357
+ NetworkXUnfeasible
358
+ If `G` is not a directed acyclic graph (DAG) no topological sort exists
359
+ and a :exc:`NetworkXUnfeasible` exception is raised. This can also be
360
+ raised if `G` is changed while the returned iterator is being processed
361
+
362
+ RuntimeError
363
+ If `G` is changed while the returned iterator is being processed.
364
+
365
+ TypeError
366
+ Results from un-sortable node names.
367
+ Consider using `key=` parameter to resolve ambiguities in the sort order.
368
+
369
+ Examples
370
+ --------
371
+ >>> DG = nx.DiGraph([(2, 1), (2, 5), (1, 3), (1, 4), (5, 4)])
372
+ >>> list(nx.lexicographical_topological_sort(DG))
373
+ [2, 1, 3, 5, 4]
374
+ >>> list(nx.lexicographical_topological_sort(DG, key=lambda x: -x))
375
+ [2, 5, 1, 4, 3]
376
+
377
+ The sort will fail for any graph with integer and string nodes. Comparison of integer to strings
378
+ is not defined in python. Is 3 greater or less than 'red'?
379
+
380
+ >>> DG = nx.DiGraph([(1, 'red'), (3, 'red'), (1, 'green'), (2, 'blue')])
381
+ >>> list(nx.lexicographical_topological_sort(DG))
382
+ Traceback (most recent call last):
383
+ ...
384
+ TypeError: '<' not supported between instances of 'str' and 'int'
385
+ ...
386
+
387
+ Incomparable nodes can be resolved using a `key` function. This example function
388
+ allows comparison of integers and strings by returning a tuple where the first
389
+ element is True for `str`, False otherwise. The second element is the node name.
390
+ This groups the strings and integers separately so they can be compared only among themselves.
391
+
392
+ >>> key = lambda node: (isinstance(node, str), node)
393
+ >>> list(nx.lexicographical_topological_sort(DG, key=key))
394
+ [1, 2, 3, 'blue', 'green', 'red']
395
+
396
+ Notes
397
+ -----
398
+ This algorithm is based on a description and proof in
399
+ "Introduction to Algorithms: A Creative Approach" [1]_ .
400
+
401
+ See also
402
+ --------
403
+ topological_sort
404
+
405
+ References
406
+ ----------
407
+ .. [1] Manber, U. (1989).
408
+ *Introduction to Algorithms - A Creative Approach.* Addison-Wesley.
409
+ """
410
+ if not G.is_directed():
411
+ msg = "Topological sort not defined on undirected graphs."
412
+ raise nx.NetworkXError(msg)
413
+
414
+ if key is None:
415
+
416
+ def key(node):
417
+ return node
418
+
419
+ nodeid_map = {n: i for i, n in enumerate(G)}
420
+
421
+ def create_tuple(node):
422
+ return key(node), nodeid_map[node], node
423
+
424
+ indegree_map = {v: d for v, d in G.in_degree() if d > 0}
425
+ # These nodes have zero indegree and ready to be returned.
426
+ zero_indegree = [create_tuple(v) for v, d in G.in_degree() if d == 0]
427
+ heapq.heapify(zero_indegree)
428
+
429
+ while zero_indegree:
430
+ _, _, node = heapq.heappop(zero_indegree)
431
+
432
+ if node not in G:
433
+ raise RuntimeError("Graph changed during iteration")
434
+ for _, child in G.edges(node):
435
+ try:
436
+ indegree_map[child] -= 1
437
+ except KeyError as err:
438
+ raise RuntimeError("Graph changed during iteration") from err
439
+ if indegree_map[child] == 0:
440
+ try:
441
+ heapq.heappush(zero_indegree, create_tuple(child))
442
+ except TypeError as err:
443
+ raise TypeError(
444
+ f"{err}\nConsider using `key=` parameter to resolve ambiguities in the sort order."
445
+ )
446
+ del indegree_map[child]
447
+
448
+ yield node
449
+
450
+ if indegree_map:
451
+ msg = "Graph contains a cycle or graph changed during iteration"
452
+ raise nx.NetworkXUnfeasible(msg)
453
+
454
+
455
+ @not_implemented_for("undirected")
456
+ @nx._dispatch
457
+ def all_topological_sorts(G):
458
+ """Returns a generator of _all_ topological sorts of the directed graph G.
459
+
460
+ A topological sort is a nonunique permutation of the nodes such that an
461
+ edge from u to v implies that u appears before v in the topological sort
462
+ order.
463
+
464
+ Parameters
465
+ ----------
466
+ G : NetworkX DiGraph
467
+ A directed graph
468
+
469
+ Yields
470
+ ------
471
+ topological_sort_order : list
472
+ a list of nodes in `G`, representing one of the topological sort orders
473
+
474
+ Raises
475
+ ------
476
+ NetworkXNotImplemented
477
+ If `G` is not directed
478
+ NetworkXUnfeasible
479
+ If `G` is not acyclic
480
+
481
+ Examples
482
+ --------
483
+ To enumerate all topological sorts of directed graph:
484
+
485
+ >>> DG = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
486
+ >>> list(nx.all_topological_sorts(DG))
487
+ [[1, 2, 4, 3], [1, 2, 3, 4]]
488
+
489
+ Notes
490
+ -----
491
+ Implements an iterative version of the algorithm given in [1].
492
+
493
+ References
494
+ ----------
495
+ .. [1] Knuth, Donald E., Szwarcfiter, Jayme L. (1974).
496
+ "A Structured Program to Generate All Topological Sorting Arrangements"
497
+ Information Processing Letters, Volume 2, Issue 6, 1974, Pages 153-157,
498
+ ISSN 0020-0190,
499
+ https://doi.org/10.1016/0020-0190(74)90001-5.
500
+ Elsevier (North-Holland), Amsterdam
501
+ """
502
+ if not G.is_directed():
503
+ raise nx.NetworkXError("Topological sort not defined on undirected graphs.")
504
+
505
+ # the names of count and D are chosen to match the global variables in [1]
506
+ # number of edges originating in a vertex v
507
+ count = dict(G.in_degree())
508
+ # vertices with indegree 0
509
+ D = deque([v for v, d in G.in_degree() if d == 0])
510
+ # stack of first value chosen at a position k in the topological sort
511
+ bases = []
512
+ current_sort = []
513
+
514
+ # do-while construct
515
+ while True:
516
+ assert all(count[v] == 0 for v in D)
517
+
518
+ if len(current_sort) == len(G):
519
+ yield list(current_sort)
520
+
521
+ # clean-up stack
522
+ while len(current_sort) > 0:
523
+ assert len(bases) == len(current_sort)
524
+ q = current_sort.pop()
525
+
526
+ # "restores" all edges (q, x)
527
+ # NOTE: it is important to iterate over edges instead
528
+ # of successors, so count is updated correctly in multigraphs
529
+ for _, j in G.out_edges(q):
530
+ count[j] += 1
531
+ assert count[j] >= 0
532
+ # remove entries from D
533
+ while len(D) > 0 and count[D[-1]] > 0:
534
+ D.pop()
535
+
536
+ # corresponds to a circular shift of the values in D
537
+ # if the first value chosen (the base) is in the first
538
+ # position of D again, we are done and need to consider the
539
+ # previous condition
540
+ D.appendleft(q)
541
+ if D[-1] == bases[-1]:
542
+ # all possible values have been chosen at current position
543
+ # remove corresponding marker
544
+ bases.pop()
545
+ else:
546
+ # there are still elements that have not been fixed
547
+ # at the current position in the topological sort
548
+ # stop removing elements, escape inner loop
549
+ break
550
+
551
+ else:
552
+ if len(D) == 0:
553
+ raise nx.NetworkXUnfeasible("Graph contains a cycle.")
554
+
555
+ # choose next node
556
+ q = D.pop()
557
+ # "erase" all edges (q, x)
558
+ # NOTE: it is important to iterate over edges instead
559
+ # of successors, so count is updated correctly in multigraphs
560
+ for _, j in G.out_edges(q):
561
+ count[j] -= 1
562
+ assert count[j] >= 0
563
+ if count[j] == 0:
564
+ D.append(j)
565
+ current_sort.append(q)
566
+
567
+ # base for current position might _not_ be fixed yet
568
+ if len(bases) < len(current_sort):
569
+ bases.append(q)
570
+
571
+ if len(bases) == 0:
572
+ break
573
+
574
+
575
+ @nx._dispatch
576
+ def is_aperiodic(G):
577
+ """Returns True if `G` is aperiodic.
578
+
579
+ A directed graph is aperiodic if there is no integer k > 1 that
580
+ divides the length of every cycle in the graph.
581
+
582
+ Parameters
583
+ ----------
584
+ G : NetworkX DiGraph
585
+ A directed graph
586
+
587
+ Returns
588
+ -------
589
+ bool
590
+ True if the graph is aperiodic False otherwise
591
+
592
+ Raises
593
+ ------
594
+ NetworkXError
595
+ If `G` is not directed
596
+
597
+ Examples
598
+ --------
599
+ A graph consisting of one cycle, the length of which is 2. Therefore ``k = 2``
600
+ divides the length of every cycle in the graph and thus the graph
601
+ is *not aperiodic*::
602
+
603
+ >>> DG = nx.DiGraph([(1, 2), (2, 1)])
604
+ >>> nx.is_aperiodic(DG)
605
+ False
606
+
607
+ A graph consisting of two cycles: one of length 2 and the other of length 3.
608
+ The cycle lengths are coprime, so there is no single value of k where ``k > 1``
609
+ that divides each cycle length and therefore the graph is *aperiodic*::
610
+
611
+ >>> DG = nx.DiGraph([(1, 2), (2, 3), (3, 1), (1, 4), (4, 1)])
612
+ >>> nx.is_aperiodic(DG)
613
+ True
614
+
615
+ A graph consisting of two cycles: one of length 2 and the other of length 4.
616
+ The lengths of the cycles share a common factor ``k = 2``, and therefore
617
+ the graph is *not aperiodic*::
618
+
619
+ >>> DG = nx.DiGraph([(1, 2), (2, 1), (3, 4), (4, 5), (5, 6), (6, 3)])
620
+ >>> nx.is_aperiodic(DG)
621
+ False
622
+
623
+ An acyclic graph, therefore the graph is *not aperiodic*::
624
+
625
+ >>> DG = nx.DiGraph([(1, 2), (2, 3)])
626
+ >>> nx.is_aperiodic(DG)
627
+ False
628
+
629
+ Notes
630
+ -----
631
+ This uses the method outlined in [1]_, which runs in $O(m)$ time
632
+ given $m$ edges in `G`. Note that a graph is not aperiodic if it is
633
+ acyclic as every integer trivial divides length 0 cycles.
634
+
635
+ References
636
+ ----------
637
+ .. [1] Jarvis, J. P.; Shier, D. R. (1996),
638
+ "Graph-theoretic analysis of finite Markov chains,"
639
+ in Shier, D. R.; Wallenius, K. T., Applied Mathematical Modeling:
640
+ A Multidisciplinary Approach, CRC Press.
641
+ """
642
+ if not G.is_directed():
643
+ raise nx.NetworkXError("is_aperiodic not defined for undirected graphs")
644
+
645
+ s = arbitrary_element(G)
646
+ levels = {s: 0}
647
+ this_level = [s]
648
+ g = 0
649
+ lev = 1
650
+ while this_level:
651
+ next_level = []
652
+ for u in this_level:
653
+ for v in G[u]:
654
+ if v in levels: # Non-Tree Edge
655
+ g = gcd(g, levels[u] - levels[v] + 1)
656
+ else: # Tree Edge
657
+ next_level.append(v)
658
+ levels[v] = lev
659
+ this_level = next_level
660
+ lev += 1
661
+ if len(levels) == len(G): # All nodes in tree
662
+ return g == 1
663
+ else:
664
+ return g == 1 and nx.is_aperiodic(G.subgraph(set(G) - set(levels)))
665
+
666
+
667
+ @nx._dispatch(preserve_all_attrs=True)
668
+ def transitive_closure(G, reflexive=False):
669
+ """Returns transitive closure of a graph
670
+
671
+ The transitive closure of G = (V,E) is a graph G+ = (V,E+) such that
672
+ for all v, w in V there is an edge (v, w) in E+ if and only if there
673
+ is a path from v to w in G.
674
+
675
+ Handling of paths from v to v has some flexibility within this definition.
676
+ A reflexive transitive closure creates a self-loop for the path
677
+ from v to v of length 0. The usual transitive closure creates a
678
+ self-loop only if a cycle exists (a path from v to v with length > 0).
679
+ We also allow an option for no self-loops.
680
+
681
+ Parameters
682
+ ----------
683
+ G : NetworkX Graph
684
+ A directed/undirected graph/multigraph.
685
+ reflexive : Bool or None, optional (default: False)
686
+ Determines when cycles create self-loops in the Transitive Closure.
687
+ If True, trivial cycles (length 0) create self-loops. The result
688
+ is a reflexive transitive closure of G.
689
+ If False (the default) non-trivial cycles create self-loops.
690
+ If None, self-loops are not created.
691
+
692
+ Returns
693
+ -------
694
+ NetworkX graph
695
+ The transitive closure of `G`
696
+
697
+ Raises
698
+ ------
699
+ NetworkXError
700
+ If `reflexive` not in `{None, True, False}`
701
+
702
+ Examples
703
+ --------
704
+ The treatment of trivial (i.e. length 0) cycles is controlled by the
705
+ `reflexive` parameter.
706
+
707
+ Trivial (i.e. length 0) cycles do not create self-loops when
708
+ ``reflexive=False`` (the default)::
709
+
710
+ >>> DG = nx.DiGraph([(1, 2), (2, 3)])
711
+ >>> TC = nx.transitive_closure(DG, reflexive=False)
712
+ >>> TC.edges()
713
+ OutEdgeView([(1, 2), (1, 3), (2, 3)])
714
+
715
+ However, nontrivial (i.e. length greater than 0) cycles create self-loops
716
+ when ``reflexive=False`` (the default)::
717
+
718
+ >>> DG = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
719
+ >>> TC = nx.transitive_closure(DG, reflexive=False)
720
+ >>> TC.edges()
721
+ OutEdgeView([(1, 2), (1, 3), (1, 1), (2, 3), (2, 1), (2, 2), (3, 1), (3, 2), (3, 3)])
722
+
723
+ Trivial cycles (length 0) create self-loops when ``reflexive=True``::
724
+
725
+ >>> DG = nx.DiGraph([(1, 2), (2, 3)])
726
+ >>> TC = nx.transitive_closure(DG, reflexive=True)
727
+ >>> TC.edges()
728
+ OutEdgeView([(1, 2), (1, 1), (1, 3), (2, 3), (2, 2), (3, 3)])
729
+
730
+ And the third option is not to create self-loops at all when ``reflexive=None``::
731
+
732
+ >>> DG = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
733
+ >>> TC = nx.transitive_closure(DG, reflexive=None)
734
+ >>> TC.edges()
735
+ OutEdgeView([(1, 2), (1, 3), (2, 3), (2, 1), (3, 1), (3, 2)])
736
+
737
+ References
738
+ ----------
739
+ .. [1] https://www.ics.uci.edu/~eppstein/PADS/PartialOrder.py
740
+ """
741
+ TC = G.copy()
742
+
743
+ if reflexive not in {None, True, False}:
744
+ raise nx.NetworkXError("Incorrect value for the parameter `reflexive`")
745
+
746
+ for v in G:
747
+ if reflexive is None:
748
+ TC.add_edges_from((v, u) for u in nx.descendants(G, v) if u not in TC[v])
749
+ elif reflexive is True:
750
+ TC.add_edges_from(
751
+ (v, u) for u in nx.descendants(G, v) | {v} if u not in TC[v]
752
+ )
753
+ elif reflexive is False:
754
+ TC.add_edges_from((v, e[1]) for e in nx.edge_bfs(G, v) if e[1] not in TC[v])
755
+
756
+ return TC
757
+
758
+
759
+ @not_implemented_for("undirected")
760
+ @nx._dispatch(preserve_all_attrs=True)
761
+ def transitive_closure_dag(G, topo_order=None):
762
+ """Returns the transitive closure of a directed acyclic graph.
763
+
764
+ This function is faster than the function `transitive_closure`, but fails
765
+ if the graph has a cycle.
766
+
767
+ The transitive closure of G = (V,E) is a graph G+ = (V,E+) such that
768
+ for all v, w in V there is an edge (v, w) in E+ if and only if there
769
+ is a non-null path from v to w in G.
770
+
771
+ Parameters
772
+ ----------
773
+ G : NetworkX DiGraph
774
+ A directed acyclic graph (DAG)
775
+
776
+ topo_order: list or tuple, optional
777
+ A topological order for G (if None, the function will compute one)
778
+
779
+ Returns
780
+ -------
781
+ NetworkX DiGraph
782
+ The transitive closure of `G`
783
+
784
+ Raises
785
+ ------
786
+ NetworkXNotImplemented
787
+ If `G` is not directed
788
+ NetworkXUnfeasible
789
+ If `G` has a cycle
790
+
791
+ Examples
792
+ --------
793
+ >>> DG = nx.DiGraph([(1, 2), (2, 3)])
794
+ >>> TC = nx.transitive_closure_dag(DG)
795
+ >>> TC.edges()
796
+ OutEdgeView([(1, 2), (1, 3), (2, 3)])
797
+
798
+ Notes
799
+ -----
800
+ This algorithm is probably simple enough to be well-known but I didn't find
801
+ a mention in the literature.
802
+ """
803
+ if topo_order is None:
804
+ topo_order = list(topological_sort(G))
805
+
806
+ TC = G.copy()
807
+
808
+ # idea: traverse vertices following a reverse topological order, connecting
809
+ # each vertex to its descendants at distance 2 as we go
810
+ for v in reversed(topo_order):
811
+ TC.add_edges_from((v, u) for u in nx.descendants_at_distance(TC, v, 2))
812
+
813
+ return TC
814
+
815
+
816
+ @not_implemented_for("undirected")
817
+ @nx._dispatch
818
+ def transitive_reduction(G):
819
+ """Returns transitive reduction of a directed graph
820
+
821
+ The transitive reduction of G = (V,E) is a graph G- = (V,E-) such that
822
+ for all v,w in V there is an edge (v,w) in E- if and only if (v,w) is
823
+ in E and there is no path from v to w in G with length greater than 1.
824
+
825
+ Parameters
826
+ ----------
827
+ G : NetworkX DiGraph
828
+ A directed acyclic graph (DAG)
829
+
830
+ Returns
831
+ -------
832
+ NetworkX DiGraph
833
+ The transitive reduction of `G`
834
+
835
+ Raises
836
+ ------
837
+ NetworkXError
838
+ If `G` is not a directed acyclic graph (DAG) transitive reduction is
839
+ not uniquely defined and a :exc:`NetworkXError` exception is raised.
840
+
841
+ Examples
842
+ --------
843
+ To perform transitive reduction on a DiGraph:
844
+
845
+ >>> DG = nx.DiGraph([(1, 2), (2, 3), (1, 3)])
846
+ >>> TR = nx.transitive_reduction(DG)
847
+ >>> list(TR.edges)
848
+ [(1, 2), (2, 3)]
849
+
850
+ To avoid unnecessary data copies, this implementation does not return a
851
+ DiGraph with node/edge data.
852
+ To perform transitive reduction on a DiGraph and transfer node/edge data:
853
+
854
+ >>> DG = nx.DiGraph()
855
+ >>> DG.add_edges_from([(1, 2), (2, 3), (1, 3)], color='red')
856
+ >>> TR = nx.transitive_reduction(DG)
857
+ >>> TR.add_nodes_from(DG.nodes(data=True))
858
+ >>> TR.add_edges_from((u, v, DG.edges[u, v]) for u, v in TR.edges)
859
+ >>> list(TR.edges(data=True))
860
+ [(1, 2, {'color': 'red'}), (2, 3, {'color': 'red'})]
861
+
862
+ References
863
+ ----------
864
+ https://en.wikipedia.org/wiki/Transitive_reduction
865
+
866
+ """
867
+ if not is_directed_acyclic_graph(G):
868
+ msg = "Directed Acyclic Graph required for transitive_reduction"
869
+ raise nx.NetworkXError(msg)
870
+ TR = nx.DiGraph()
871
+ TR.add_nodes_from(G.nodes())
872
+ descendants = {}
873
+ # count before removing set stored in descendants
874
+ check_count = dict(G.in_degree)
875
+ for u in G:
876
+ u_nbrs = set(G[u])
877
+ for v in G[u]:
878
+ if v in u_nbrs:
879
+ if v not in descendants:
880
+ descendants[v] = {y for x, y in nx.dfs_edges(G, v)}
881
+ u_nbrs -= descendants[v]
882
+ check_count[v] -= 1
883
+ if check_count[v] == 0:
884
+ del descendants[v]
885
+ TR.add_edges_from((u, v) for v in u_nbrs)
886
+ return TR
887
+
888
+
889
+ @not_implemented_for("undirected")
890
+ @nx._dispatch
891
+ def antichains(G, topo_order=None):
892
+ """Generates antichains from a directed acyclic graph (DAG).
893
+
894
+ An antichain is a subset of a partially ordered set such that any
895
+ two elements in the subset are incomparable.
896
+
897
+ Parameters
898
+ ----------
899
+ G : NetworkX DiGraph
900
+ A directed acyclic graph (DAG)
901
+
902
+ topo_order: list or tuple, optional
903
+ A topological order for G (if None, the function will compute one)
904
+
905
+ Yields
906
+ ------
907
+ antichain : list
908
+ a list of nodes in `G` representing an antichain
909
+
910
+ Raises
911
+ ------
912
+ NetworkXNotImplemented
913
+ If `G` is not directed
914
+
915
+ NetworkXUnfeasible
916
+ If `G` contains a cycle
917
+
918
+ Examples
919
+ --------
920
+ >>> DG = nx.DiGraph([(1, 2), (1, 3)])
921
+ >>> list(nx.antichains(DG))
922
+ [[], [3], [2], [2, 3], [1]]
923
+
924
+ Notes
925
+ -----
926
+ This function was originally developed by Peter Jipsen and Franco Saliola
927
+ for the SAGE project. It's included in NetworkX with permission from the
928
+ authors. Original SAGE code at:
929
+
930
+ https://github.com/sagemath/sage/blob/master/src/sage/combinat/posets/hasse_diagram.py
931
+
932
+ References
933
+ ----------
934
+ .. [1] Free Lattices, by R. Freese, J. Jezek and J. B. Nation,
935
+ AMS, Vol 42, 1995, p. 226.
936
+ """
937
+ if topo_order is None:
938
+ topo_order = list(nx.topological_sort(G))
939
+
940
+ TC = nx.transitive_closure_dag(G, topo_order)
941
+ antichains_stacks = [([], list(reversed(topo_order)))]
942
+
943
+ while antichains_stacks:
944
+ (antichain, stack) = antichains_stacks.pop()
945
+ # Invariant:
946
+ # - the elements of antichain are independent
947
+ # - the elements of stack are independent from those of antichain
948
+ yield antichain
949
+ while stack:
950
+ x = stack.pop()
951
+ new_antichain = antichain + [x]
952
+ new_stack = [t for t in stack if not ((t in TC[x]) or (x in TC[t]))]
953
+ antichains_stacks.append((new_antichain, new_stack))
954
+
955
+
956
+ @not_implemented_for("undirected")
957
+ @nx._dispatch(edge_attrs={"weight": "default_weight"})
958
+ def dag_longest_path(G, weight="weight", default_weight=1, topo_order=None):
959
+ """Returns the longest path in a directed acyclic graph (DAG).
960
+
961
+ If `G` has edges with `weight` attribute the edge data are used as
962
+ weight values.
963
+
964
+ Parameters
965
+ ----------
966
+ G : NetworkX DiGraph
967
+ A directed acyclic graph (DAG)
968
+
969
+ weight : str, optional
970
+ Edge data key to use for weight
971
+
972
+ default_weight : int, optional
973
+ The weight of edges that do not have a weight attribute
974
+
975
+ topo_order: list or tuple, optional
976
+ A topological order for `G` (if None, the function will compute one)
977
+
978
+ Returns
979
+ -------
980
+ list
981
+ Longest path
982
+
983
+ Raises
984
+ ------
985
+ NetworkXNotImplemented
986
+ If `G` is not directed
987
+
988
+ Examples
989
+ --------
990
+ >>> DG = nx.DiGraph([(0, 1, {'cost':1}), (1, 2, {'cost':1}), (0, 2, {'cost':42})])
991
+ >>> list(nx.all_simple_paths(DG, 0, 2))
992
+ [[0, 1, 2], [0, 2]]
993
+ >>> nx.dag_longest_path(DG)
994
+ [0, 1, 2]
995
+ >>> nx.dag_longest_path(DG, weight="cost")
996
+ [0, 2]
997
+
998
+ In the case where multiple valid topological orderings exist, `topo_order`
999
+ can be used to specify a specific ordering:
1000
+
1001
+ >>> DG = nx.DiGraph([(0, 1), (0, 2)])
1002
+ >>> sorted(nx.all_topological_sorts(DG)) # Valid topological orderings
1003
+ [[0, 1, 2], [0, 2, 1]]
1004
+ >>> nx.dag_longest_path(DG, topo_order=[0, 1, 2])
1005
+ [0, 1]
1006
+ >>> nx.dag_longest_path(DG, topo_order=[0, 2, 1])
1007
+ [0, 2]
1008
+
1009
+ See also
1010
+ --------
1011
+ dag_longest_path_length
1012
+
1013
+ """
1014
+ if not G:
1015
+ return []
1016
+
1017
+ if topo_order is None:
1018
+ topo_order = nx.topological_sort(G)
1019
+
1020
+ dist = {} # stores {v : (length, u)}
1021
+ for v in topo_order:
1022
+ us = [
1023
+ (
1024
+ dist[u][0]
1025
+ + (
1026
+ max(data.values(), key=lambda x: x.get(weight, default_weight))
1027
+ if G.is_multigraph()
1028
+ else data
1029
+ ).get(weight, default_weight),
1030
+ u,
1031
+ )
1032
+ for u, data in G.pred[v].items()
1033
+ ]
1034
+
1035
+ # Use the best predecessor if there is one and its distance is
1036
+ # non-negative, otherwise terminate.
1037
+ maxu = max(us, key=lambda x: x[0]) if us else (0, v)
1038
+ dist[v] = maxu if maxu[0] >= 0 else (0, v)
1039
+
1040
+ u = None
1041
+ v = max(dist, key=lambda x: dist[x][0])
1042
+ path = []
1043
+ while u != v:
1044
+ path.append(v)
1045
+ u = v
1046
+ v = dist[v][1]
1047
+
1048
+ path.reverse()
1049
+ return path
1050
+
1051
+
1052
+ @not_implemented_for("undirected")
1053
+ @nx._dispatch(edge_attrs={"weight": "default_weight"})
1054
+ def dag_longest_path_length(G, weight="weight", default_weight=1):
1055
+ """Returns the longest path length in a DAG
1056
+
1057
+ Parameters
1058
+ ----------
1059
+ G : NetworkX DiGraph
1060
+ A directed acyclic graph (DAG)
1061
+
1062
+ weight : string, optional
1063
+ Edge data key to use for weight
1064
+
1065
+ default_weight : int, optional
1066
+ The weight of edges that do not have a weight attribute
1067
+
1068
+ Returns
1069
+ -------
1070
+ int
1071
+ Longest path length
1072
+
1073
+ Raises
1074
+ ------
1075
+ NetworkXNotImplemented
1076
+ If `G` is not directed
1077
+
1078
+ Examples
1079
+ --------
1080
+ >>> DG = nx.DiGraph([(0, 1, {'cost':1}), (1, 2, {'cost':1}), (0, 2, {'cost':42})])
1081
+ >>> list(nx.all_simple_paths(DG, 0, 2))
1082
+ [[0, 1, 2], [0, 2]]
1083
+ >>> nx.dag_longest_path_length(DG)
1084
+ 2
1085
+ >>> nx.dag_longest_path_length(DG, weight="cost")
1086
+ 42
1087
+
1088
+ See also
1089
+ --------
1090
+ dag_longest_path
1091
+ """
1092
+ path = nx.dag_longest_path(G, weight, default_weight)
1093
+ path_length = 0
1094
+ if G.is_multigraph():
1095
+ for u, v in pairwise(path):
1096
+ i = max(G[u][v], key=lambda x: G[u][v][x].get(weight, default_weight))
1097
+ path_length += G[u][v][i].get(weight, default_weight)
1098
+ else:
1099
+ for u, v in pairwise(path):
1100
+ path_length += G[u][v].get(weight, default_weight)
1101
+
1102
+ return path_length
1103
+
1104
+
1105
+ @nx._dispatch
1106
+ def root_to_leaf_paths(G):
1107
+ """Yields root-to-leaf paths in a directed acyclic graph.
1108
+
1109
+ `G` must be a directed acyclic graph. If not, the behavior of this
1110
+ function is undefined. A "root" in this graph is a node of in-degree
1111
+ zero and a "leaf" a node of out-degree zero.
1112
+
1113
+ When invoked, this function iterates over each path from any root to
1114
+ any leaf. A path is a list of nodes.
1115
+
1116
+ """
1117
+ roots = (v for v, d in G.in_degree() if d == 0)
1118
+ leaves = (v for v, d in G.out_degree() if d == 0)
1119
+ all_paths = partial(nx.all_simple_paths, G)
1120
+ # TODO In Python 3, this would be better as `yield from ...`.
1121
+ return chaini(starmap(all_paths, product(roots, leaves)))
1122
+
1123
+
1124
+ @not_implemented_for("multigraph")
1125
+ @not_implemented_for("undirected")
1126
+ @nx._dispatch
1127
+ def dag_to_branching(G):
1128
+ """Returns a branching representing all (overlapping) paths from
1129
+ root nodes to leaf nodes in the given directed acyclic graph.
1130
+
1131
+ As described in :mod:`networkx.algorithms.tree.recognition`, a
1132
+ *branching* is a directed forest in which each node has at most one
1133
+ parent. In other words, a branching is a disjoint union of
1134
+ *arborescences*. For this function, each node of in-degree zero in
1135
+ `G` becomes a root of one of the arborescences, and there will be
1136
+ one leaf node for each distinct path from that root to a leaf node
1137
+ in `G`.
1138
+
1139
+ Each node `v` in `G` with *k* parents becomes *k* distinct nodes in
1140
+ the returned branching, one for each parent, and the sub-DAG rooted
1141
+ at `v` is duplicated for each copy. The algorithm then recurses on
1142
+ the children of each copy of `v`.
1143
+
1144
+ Parameters
1145
+ ----------
1146
+ G : NetworkX graph
1147
+ A directed acyclic graph.
1148
+
1149
+ Returns
1150
+ -------
1151
+ DiGraph
1152
+ The branching in which there is a bijection between root-to-leaf
1153
+ paths in `G` (in which multiple paths may share the same leaf)
1154
+ and root-to-leaf paths in the branching (in which there is a
1155
+ unique path from a root to a leaf).
1156
+
1157
+ Each node has an attribute 'source' whose value is the original
1158
+ node to which this node corresponds. No other graph, node, or
1159
+ edge attributes are copied into this new graph.
1160
+
1161
+ Raises
1162
+ ------
1163
+ NetworkXNotImplemented
1164
+ If `G` is not directed, or if `G` is a multigraph.
1165
+
1166
+ HasACycle
1167
+ If `G` is not acyclic.
1168
+
1169
+ Examples
1170
+ --------
1171
+ To examine which nodes in the returned branching were produced by
1172
+ which original node in the directed acyclic graph, we can collect
1173
+ the mapping from source node to new nodes into a dictionary. For
1174
+ example, consider the directed diamond graph::
1175
+
1176
+ >>> from collections import defaultdict
1177
+ >>> from operator import itemgetter
1178
+ >>>
1179
+ >>> G = nx.DiGraph(nx.utils.pairwise("abd"))
1180
+ >>> G.add_edges_from(nx.utils.pairwise("acd"))
1181
+ >>> B = nx.dag_to_branching(G)
1182
+ >>>
1183
+ >>> sources = defaultdict(set)
1184
+ >>> for v, source in B.nodes(data="source"):
1185
+ ... sources[source].add(v)
1186
+ >>> len(sources["a"])
1187
+ 1
1188
+ >>> len(sources["d"])
1189
+ 2
1190
+
1191
+ To copy node attributes from the original graph to the new graph,
1192
+ you can use a dictionary like the one constructed in the above
1193
+ example::
1194
+
1195
+ >>> for source, nodes in sources.items():
1196
+ ... for v in nodes:
1197
+ ... B.nodes[v].update(G.nodes[source])
1198
+
1199
+ Notes
1200
+ -----
1201
+ This function is not idempotent in the sense that the node labels in
1202
+ the returned branching may be uniquely generated each time the
1203
+ function is invoked. In fact, the node labels may not be integers;
1204
+ in order to relabel the nodes to be more readable, you can use the
1205
+ :func:`networkx.convert_node_labels_to_integers` function.
1206
+
1207
+ The current implementation of this function uses
1208
+ :func:`networkx.prefix_tree`, so it is subject to the limitations of
1209
+ that function.
1210
+
1211
+ """
1212
+ if has_cycle(G):
1213
+ msg = "dag_to_branching is only defined for acyclic graphs"
1214
+ raise nx.HasACycle(msg)
1215
+ paths = root_to_leaf_paths(G)
1216
+ B = nx.prefix_tree(paths)
1217
+ # Remove the synthetic `root`(0) and `NIL`(-1) nodes from the tree
1218
+ B.remove_node(0)
1219
+ B.remove_node(-1)
1220
+ return B
1221
+
1222
+
1223
+ @not_implemented_for("undirected")
1224
+ @nx._dispatch
1225
+ def compute_v_structures(G):
1226
+ """Iterate through the graph to compute all v-structures.
1227
+
1228
+ V-structures are triples in the directed graph where
1229
+ two parent nodes point to the same child and the two parent nodes
1230
+ are not adjacent.
1231
+
1232
+ Parameters
1233
+ ----------
1234
+ G : graph
1235
+ A networkx DiGraph.
1236
+
1237
+ Returns
1238
+ -------
1239
+ vstructs : iterator of tuples
1240
+ The v structures within the graph. Each v structure is a 3-tuple with the
1241
+ parent, collider, and other parent.
1242
+
1243
+ Examples
1244
+ --------
1245
+ >>> G = nx.DiGraph()
1246
+ >>> G.add_edges_from([(1, 2), (0, 5), (3, 1), (2, 4), (3, 1), (4, 5), (1, 5)])
1247
+ >>> sorted(nx.compute_v_structures(G))
1248
+ [(0, 5, 1), (0, 5, 4), (1, 5, 4)]
1249
+
1250
+ Notes
1251
+ -----
1252
+ https://en.wikipedia.org/wiki/Collider_(statistics)
1253
+ """
1254
+ for collider, preds in G.pred.items():
1255
+ for common_parents in combinations(preds, r=2):
1256
+ # ensure that the colliders are the same
1257
+ common_parents = sorted(common_parents)
1258
+ yield (common_parents[0], collider, common_parents[1])
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/distance_regular.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ =======================
3
+ Distance-regular graphs
4
+ =======================
5
+ """
6
+
7
+ import networkx as nx
8
+ from networkx.utils import not_implemented_for
9
+
10
+ from .distance_measures import diameter
11
+
12
+ __all__ = [
13
+ "is_distance_regular",
14
+ "is_strongly_regular",
15
+ "intersection_array",
16
+ "global_parameters",
17
+ ]
18
+
19
+
20
+ @nx._dispatch
21
+ def is_distance_regular(G):
22
+ """Returns True if the graph is distance regular, False otherwise.
23
+
24
+ A connected graph G is distance-regular if for any nodes x,y
25
+ and any integers i,j=0,1,...,d (where d is the graph
26
+ diameter), the number of vertices at distance i from x and
27
+ distance j from y depends only on i,j and the graph distance
28
+ between x and y, independently of the choice of x and y.
29
+
30
+ Parameters
31
+ ----------
32
+ G: Networkx graph (undirected)
33
+
34
+ Returns
35
+ -------
36
+ bool
37
+ True if the graph is Distance Regular, False otherwise
38
+
39
+ Examples
40
+ --------
41
+ >>> G = nx.hypercube_graph(6)
42
+ >>> nx.is_distance_regular(G)
43
+ True
44
+
45
+ See Also
46
+ --------
47
+ intersection_array, global_parameters
48
+
49
+ Notes
50
+ -----
51
+ For undirected and simple graphs only
52
+
53
+ References
54
+ ----------
55
+ .. [1] Brouwer, A. E.; Cohen, A. M.; and Neumaier, A.
56
+ Distance-Regular Graphs. New York: Springer-Verlag, 1989.
57
+ .. [2] Weisstein, Eric W. "Distance-Regular Graph."
58
+ http://mathworld.wolfram.com/Distance-RegularGraph.html
59
+
60
+ """
61
+ try:
62
+ intersection_array(G)
63
+ return True
64
+ except nx.NetworkXError:
65
+ return False
66
+
67
+
68
+ def global_parameters(b, c):
69
+ """Returns global parameters for a given intersection array.
70
+
71
+ Given a distance-regular graph G with integers b_i, c_i,i = 0,....,d
72
+ such that for any 2 vertices x,y in G at a distance i=d(x,y), there
73
+ are exactly c_i neighbors of y at a distance of i-1 from x and b_i
74
+ neighbors of y at a distance of i+1 from x.
75
+
76
+ Thus, a distance regular graph has the global parameters,
77
+ [[c_0,a_0,b_0],[c_1,a_1,b_1],......,[c_d,a_d,b_d]] for the
78
+ intersection array [b_0,b_1,.....b_{d-1};c_1,c_2,.....c_d]
79
+ where a_i+b_i+c_i=k , k= degree of every vertex.
80
+
81
+ Parameters
82
+ ----------
83
+ b : list
84
+
85
+ c : list
86
+
87
+ Returns
88
+ -------
89
+ iterable
90
+ An iterable over three tuples.
91
+
92
+ Examples
93
+ --------
94
+ >>> G = nx.dodecahedral_graph()
95
+ >>> b, c = nx.intersection_array(G)
96
+ >>> list(nx.global_parameters(b, c))
97
+ [(0, 0, 3), (1, 0, 2), (1, 1, 1), (1, 1, 1), (2, 0, 1), (3, 0, 0)]
98
+
99
+ References
100
+ ----------
101
+ .. [1] Weisstein, Eric W. "Global Parameters."
102
+ From MathWorld--A Wolfram Web Resource.
103
+ http://mathworld.wolfram.com/GlobalParameters.html
104
+
105
+ See Also
106
+ --------
107
+ intersection_array
108
+ """
109
+ return ((y, b[0] - x - y, x) for x, y in zip(b + [0], [0] + c))
110
+
111
+
112
+ @not_implemented_for("directed", "multigraph")
113
+ @nx._dispatch
114
+ def intersection_array(G):
115
+ """Returns the intersection array of a distance-regular graph.
116
+
117
+ Given a distance-regular graph G with integers b_i, c_i,i = 0,....,d
118
+ such that for any 2 vertices x,y in G at a distance i=d(x,y), there
119
+ are exactly c_i neighbors of y at a distance of i-1 from x and b_i
120
+ neighbors of y at a distance of i+1 from x.
121
+
122
+ A distance regular graph's intersection array is given by,
123
+ [b_0,b_1,.....b_{d-1};c_1,c_2,.....c_d]
124
+
125
+ Parameters
126
+ ----------
127
+ G: Networkx graph (undirected)
128
+
129
+ Returns
130
+ -------
131
+ b,c: tuple of lists
132
+
133
+ Examples
134
+ --------
135
+ >>> G = nx.icosahedral_graph()
136
+ >>> nx.intersection_array(G)
137
+ ([5, 2, 1], [1, 2, 5])
138
+
139
+ References
140
+ ----------
141
+ .. [1] Weisstein, Eric W. "Intersection Array."
142
+ From MathWorld--A Wolfram Web Resource.
143
+ http://mathworld.wolfram.com/IntersectionArray.html
144
+
145
+ See Also
146
+ --------
147
+ global_parameters
148
+ """
149
+ # test for regular graph (all degrees must be equal)
150
+ degree = iter(G.degree())
151
+ (_, k) = next(degree)
152
+ for _, knext in degree:
153
+ if knext != k:
154
+ raise nx.NetworkXError("Graph is not distance regular.")
155
+ k = knext
156
+ path_length = dict(nx.all_pairs_shortest_path_length(G))
157
+ diameter = max(max(path_length[n].values()) for n in path_length)
158
+ bint = {} # 'b' intersection array
159
+ cint = {} # 'c' intersection array
160
+ for u in G:
161
+ for v in G:
162
+ try:
163
+ i = path_length[u][v]
164
+ except KeyError as err: # graph must be connected
165
+ raise nx.NetworkXError("Graph is not distance regular.") from err
166
+ # number of neighbors of v at a distance of i-1 from u
167
+ c = len([n for n in G[v] if path_length[n][u] == i - 1])
168
+ # number of neighbors of v at a distance of i+1 from u
169
+ b = len([n for n in G[v] if path_length[n][u] == i + 1])
170
+ # b,c are independent of u and v
171
+ if cint.get(i, c) != c or bint.get(i, b) != b:
172
+ raise nx.NetworkXError("Graph is not distance regular")
173
+ bint[i] = b
174
+ cint[i] = c
175
+ return (
176
+ [bint.get(j, 0) for j in range(diameter)],
177
+ [cint.get(j + 1, 0) for j in range(diameter)],
178
+ )
179
+
180
+
181
+ # TODO There is a definition for directed strongly regular graphs.
182
+ @not_implemented_for("directed", "multigraph")
183
+ @nx._dispatch
184
+ def is_strongly_regular(G):
185
+ """Returns True if and only if the given graph is strongly
186
+ regular.
187
+
188
+ An undirected graph is *strongly regular* if
189
+
190
+ * it is regular,
191
+ * each pair of adjacent vertices has the same number of neighbors in
192
+ common,
193
+ * each pair of nonadjacent vertices has the same number of neighbors
194
+ in common.
195
+
196
+ Each strongly regular graph is a distance-regular graph.
197
+ Conversely, if a distance-regular graph has diameter two, then it is
198
+ a strongly regular graph. For more information on distance-regular
199
+ graphs, see :func:`is_distance_regular`.
200
+
201
+ Parameters
202
+ ----------
203
+ G : NetworkX graph
204
+ An undirected graph.
205
+
206
+ Returns
207
+ -------
208
+ bool
209
+ Whether `G` is strongly regular.
210
+
211
+ Examples
212
+ --------
213
+
214
+ The cycle graph on five vertices is strongly regular. It is
215
+ two-regular, each pair of adjacent vertices has no shared neighbors,
216
+ and each pair of nonadjacent vertices has one shared neighbor::
217
+
218
+ >>> G = nx.cycle_graph(5)
219
+ >>> nx.is_strongly_regular(G)
220
+ True
221
+
222
+ """
223
+ # Here is an alternate implementation based directly on the
224
+ # definition of strongly regular graphs:
225
+ #
226
+ # return (all_equal(G.degree().values())
227
+ # and all_equal(len(common_neighbors(G, u, v))
228
+ # for u, v in G.edges())
229
+ # and all_equal(len(common_neighbors(G, u, v))
230
+ # for u, v in non_edges(G)))
231
+ #
232
+ # We instead use the fact that a distance-regular graph of diameter
233
+ # two is strongly regular.
234
+ return is_distance_regular(G) and diameter(G) == 2
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .maxflow import *
2
+ from .mincost import *
3
+ from .boykovkolmogorov import *
4
+ from .dinitz_alg import *
5
+ from .edmondskarp import *
6
+ from .gomory_hu import *
7
+ from .preflowpush import *
8
+ from .shortestaugmentingpath import *
9
+ from .capacityscaling import *
10
+ from .networksimplex import *
11
+ from .utils import build_flow_dict, build_residual_network
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (670 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/edmondskarp.cpython-311.pyc ADDED
Binary file (9.77 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/maxflow.cpython-311.pyc ADDED
Binary file (23.7 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/edmondskarp.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Edmonds-Karp algorithm for maximum flow problems.
3
+ """
4
+
5
+ import networkx as nx
6
+ from networkx.algorithms.flow.utils import build_residual_network
7
+
8
+ __all__ = ["edmonds_karp"]
9
+
10
+
11
+ @nx._dispatch(
12
+ graphs="R",
13
+ preserve_edge_attrs={"R": {"capacity": float("inf"), "flow": 0}},
14
+ preserve_graph_attrs=True,
15
+ )
16
+ def edmonds_karp_core(R, s, t, cutoff):
17
+ """Implementation of the Edmonds-Karp algorithm."""
18
+ R_nodes = R.nodes
19
+ R_pred = R.pred
20
+ R_succ = R.succ
21
+
22
+ inf = R.graph["inf"]
23
+
24
+ def augment(path):
25
+ """Augment flow along a path from s to t."""
26
+ # Determine the path residual capacity.
27
+ flow = inf
28
+ it = iter(path)
29
+ u = next(it)
30
+ for v in it:
31
+ attr = R_succ[u][v]
32
+ flow = min(flow, attr["capacity"] - attr["flow"])
33
+ u = v
34
+ if flow * 2 > inf:
35
+ raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.")
36
+ # Augment flow along the path.
37
+ it = iter(path)
38
+ u = next(it)
39
+ for v in it:
40
+ R_succ[u][v]["flow"] += flow
41
+ R_succ[v][u]["flow"] -= flow
42
+ u = v
43
+ return flow
44
+
45
+ def bidirectional_bfs():
46
+ """Bidirectional breadth-first search for an augmenting path."""
47
+ pred = {s: None}
48
+ q_s = [s]
49
+ succ = {t: None}
50
+ q_t = [t]
51
+ while True:
52
+ q = []
53
+ if len(q_s) <= len(q_t):
54
+ for u in q_s:
55
+ for v, attr in R_succ[u].items():
56
+ if v not in pred and attr["flow"] < attr["capacity"]:
57
+ pred[v] = u
58
+ if v in succ:
59
+ return v, pred, succ
60
+ q.append(v)
61
+ if not q:
62
+ return None, None, None
63
+ q_s = q
64
+ else:
65
+ for u in q_t:
66
+ for v, attr in R_pred[u].items():
67
+ if v not in succ and attr["flow"] < attr["capacity"]:
68
+ succ[v] = u
69
+ if v in pred:
70
+ return v, pred, succ
71
+ q.append(v)
72
+ if not q:
73
+ return None, None, None
74
+ q_t = q
75
+
76
+ # Look for shortest augmenting paths using breadth-first search.
77
+ flow_value = 0
78
+ while flow_value < cutoff:
79
+ v, pred, succ = bidirectional_bfs()
80
+ if pred is None:
81
+ break
82
+ path = [v]
83
+ # Trace a path from s to v.
84
+ u = v
85
+ while u != s:
86
+ u = pred[u]
87
+ path.append(u)
88
+ path.reverse()
89
+ # Trace a path from v to t.
90
+ u = v
91
+ while u != t:
92
+ u = succ[u]
93
+ path.append(u)
94
+ flow_value += augment(path)
95
+
96
+ return flow_value
97
+
98
+
99
+ def edmonds_karp_impl(G, s, t, capacity, residual, cutoff):
100
+ """Implementation of the Edmonds-Karp algorithm."""
101
+ if s not in G:
102
+ raise nx.NetworkXError(f"node {str(s)} not in graph")
103
+ if t not in G:
104
+ raise nx.NetworkXError(f"node {str(t)} not in graph")
105
+ if s == t:
106
+ raise nx.NetworkXError("source and sink are the same node")
107
+
108
+ if residual is None:
109
+ R = build_residual_network(G, capacity)
110
+ else:
111
+ R = residual
112
+
113
+ # Initialize/reset the residual network.
114
+ for u in R:
115
+ for e in R[u].values():
116
+ e["flow"] = 0
117
+
118
+ if cutoff is None:
119
+ cutoff = float("inf")
120
+ R.graph["flow_value"] = edmonds_karp_core(R, s, t, cutoff)
121
+
122
+ return R
123
+
124
+
125
+ @nx._dispatch(
126
+ graphs={"G": 0, "residual?": 4},
127
+ edge_attrs={"capacity": float("inf")},
128
+ preserve_edge_attrs={"residual": {"capacity": float("inf")}},
129
+ preserve_graph_attrs={"residual"},
130
+ )
131
+ def edmonds_karp(
132
+ G, s, t, capacity="capacity", residual=None, value_only=False, cutoff=None
133
+ ):
134
+ """Find a maximum single-commodity flow using the Edmonds-Karp algorithm.
135
+
136
+ This function returns the residual network resulting after computing
137
+ the maximum flow. See below for details about the conventions
138
+ NetworkX uses for defining residual networks.
139
+
140
+ This algorithm has a running time of $O(n m^2)$ for $n$ nodes and $m$
141
+ edges.
142
+
143
+
144
+ Parameters
145
+ ----------
146
+ G : NetworkX graph
147
+ Edges of the graph are expected to have an attribute called
148
+ 'capacity'. If this attribute is not present, the edge is
149
+ considered to have infinite capacity.
150
+
151
+ s : node
152
+ Source node for the flow.
153
+
154
+ t : node
155
+ Sink node for the flow.
156
+
157
+ capacity : string
158
+ Edges of the graph G are expected to have an attribute capacity
159
+ that indicates how much flow the edge can support. If this
160
+ attribute is not present, the edge is considered to have
161
+ infinite capacity. Default value: 'capacity'.
162
+
163
+ residual : NetworkX graph
164
+ Residual network on which the algorithm is to be executed. If None, a
165
+ new residual network is created. Default value: None.
166
+
167
+ value_only : bool
168
+ If True compute only the value of the maximum flow. This parameter
169
+ will be ignored by this algorithm because it is not applicable.
170
+
171
+ cutoff : integer, float
172
+ If specified, the algorithm will terminate when the flow value reaches
173
+ or exceeds the cutoff. In this case, it may be unable to immediately
174
+ determine a minimum cut. Default value: None.
175
+
176
+ Returns
177
+ -------
178
+ R : NetworkX DiGraph
179
+ Residual network after computing the maximum flow.
180
+
181
+ Raises
182
+ ------
183
+ NetworkXError
184
+ The algorithm does not support MultiGraph and MultiDiGraph. If
185
+ the input graph is an instance of one of these two classes, a
186
+ NetworkXError is raised.
187
+
188
+ NetworkXUnbounded
189
+ If the graph has a path of infinite capacity, the value of a
190
+ feasible flow on the graph is unbounded above and the function
191
+ raises a NetworkXUnbounded.
192
+
193
+ See also
194
+ --------
195
+ :meth:`maximum_flow`
196
+ :meth:`minimum_cut`
197
+ :meth:`preflow_push`
198
+ :meth:`shortest_augmenting_path`
199
+
200
+ Notes
201
+ -----
202
+ The residual network :samp:`R` from an input graph :samp:`G` has the
203
+ same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
204
+ of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
205
+ self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
206
+ in :samp:`G`.
207
+
208
+ For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
209
+ is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
210
+ in :samp:`G` or zero otherwise. If the capacity is infinite,
211
+ :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
212
+ that does not affect the solution of the problem. This value is stored in
213
+ :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
214
+ :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
215
+ satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
216
+
217
+ The flow value, defined as the total flow into :samp:`t`, the sink, is
218
+ stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not
219
+ specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such
220
+ that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
221
+ :samp:`s`-:samp:`t` cut.
222
+
223
+ Examples
224
+ --------
225
+ >>> from networkx.algorithms.flow import edmonds_karp
226
+
227
+ The functions that implement flow algorithms and output a residual
228
+ network, such as this one, are not imported to the base NetworkX
229
+ namespace, so you have to explicitly import them from the flow package.
230
+
231
+ >>> G = nx.DiGraph()
232
+ >>> G.add_edge("x", "a", capacity=3.0)
233
+ >>> G.add_edge("x", "b", capacity=1.0)
234
+ >>> G.add_edge("a", "c", capacity=3.0)
235
+ >>> G.add_edge("b", "c", capacity=5.0)
236
+ >>> G.add_edge("b", "d", capacity=4.0)
237
+ >>> G.add_edge("d", "e", capacity=2.0)
238
+ >>> G.add_edge("c", "y", capacity=2.0)
239
+ >>> G.add_edge("e", "y", capacity=3.0)
240
+ >>> R = edmonds_karp(G, "x", "y")
241
+ >>> flow_value = nx.maximum_flow_value(G, "x", "y")
242
+ >>> flow_value
243
+ 3.0
244
+ >>> flow_value == R.graph["flow_value"]
245
+ True
246
+
247
+ """
248
+ R = edmonds_karp_impl(G, s, t, capacity, residual, cutoff)
249
+ R.graph["algorithm"] = "edmonds_karp"
250
+ return R
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/mincost.py ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Minimum cost flow algorithms on directed connected graphs.
3
+ """
4
+
5
+ __all__ = ["min_cost_flow_cost", "min_cost_flow", "cost_of_flow", "max_flow_min_cost"]
6
+
7
+ import networkx as nx
8
+
9
+
10
+ @nx._dispatch(node_attrs="demand", edge_attrs={"capacity": float("inf"), "weight": 0})
11
+ def min_cost_flow_cost(G, demand="demand", capacity="capacity", weight="weight"):
12
+ r"""Find the cost of a minimum cost flow satisfying all demands in digraph G.
13
+
14
+ G is a digraph with edge costs and capacities and in which nodes
15
+ have demand, i.e., they want to send or receive some amount of
16
+ flow. A negative demand means that the node wants to send flow, a
17
+ positive demand means that the node want to receive flow. A flow on
18
+ the digraph G satisfies all demand if the net flow into each node
19
+ is equal to the demand of that node.
20
+
21
+ Parameters
22
+ ----------
23
+ G : NetworkX graph
24
+ DiGraph on which a minimum cost flow satisfying all demands is
25
+ to be found.
26
+
27
+ demand : string
28
+ Nodes of the graph G are expected to have an attribute demand
29
+ that indicates how much flow a node wants to send (negative
30
+ demand) or receive (positive demand). Note that the sum of the
31
+ demands should be 0 otherwise the problem in not feasible. If
32
+ this attribute is not present, a node is considered to have 0
33
+ demand. Default value: 'demand'.
34
+
35
+ capacity : string
36
+ Edges of the graph G are expected to have an attribute capacity
37
+ that indicates how much flow the edge can support. If this
38
+ attribute is not present, the edge is considered to have
39
+ infinite capacity. Default value: 'capacity'.
40
+
41
+ weight : string
42
+ Edges of the graph G are expected to have an attribute weight
43
+ that indicates the cost incurred by sending one unit of flow on
44
+ that edge. If not present, the weight is considered to be 0.
45
+ Default value: 'weight'.
46
+
47
+ Returns
48
+ -------
49
+ flowCost : integer, float
50
+ Cost of a minimum cost flow satisfying all demands.
51
+
52
+ Raises
53
+ ------
54
+ NetworkXError
55
+ This exception is raised if the input graph is not directed or
56
+ not connected.
57
+
58
+ NetworkXUnfeasible
59
+ This exception is raised in the following situations:
60
+
61
+ * The sum of the demands is not zero. Then, there is no
62
+ flow satisfying all demands.
63
+ * There is no flow satisfying all demand.
64
+
65
+ NetworkXUnbounded
66
+ This exception is raised if the digraph G has a cycle of
67
+ negative cost and infinite capacity. Then, the cost of a flow
68
+ satisfying all demands is unbounded below.
69
+
70
+ See also
71
+ --------
72
+ cost_of_flow, max_flow_min_cost, min_cost_flow, network_simplex
73
+
74
+ Notes
75
+ -----
76
+ This algorithm is not guaranteed to work if edge weights or demands
77
+ are floating point numbers (overflows and roundoff errors can
78
+ cause problems). As a workaround you can use integer numbers by
79
+ multiplying the relevant edge attributes by a convenient
80
+ constant factor (eg 100).
81
+
82
+ Examples
83
+ --------
84
+ A simple example of a min cost flow problem.
85
+
86
+ >>> G = nx.DiGraph()
87
+ >>> G.add_node("a", demand=-5)
88
+ >>> G.add_node("d", demand=5)
89
+ >>> G.add_edge("a", "b", weight=3, capacity=4)
90
+ >>> G.add_edge("a", "c", weight=6, capacity=10)
91
+ >>> G.add_edge("b", "d", weight=1, capacity=9)
92
+ >>> G.add_edge("c", "d", weight=2, capacity=5)
93
+ >>> flowCost = nx.min_cost_flow_cost(G)
94
+ >>> flowCost
95
+ 24
96
+ """
97
+ return nx.network_simplex(G, demand=demand, capacity=capacity, weight=weight)[0]
98
+
99
+
100
+ @nx._dispatch(node_attrs="demand", edge_attrs={"capacity": float("inf"), "weight": 0})
101
+ def min_cost_flow(G, demand="demand", capacity="capacity", weight="weight"):
102
+ r"""Returns a minimum cost flow satisfying all demands in digraph G.
103
+
104
+ G is a digraph with edge costs and capacities and in which nodes
105
+ have demand, i.e., they want to send or receive some amount of
106
+ flow. A negative demand means that the node wants to send flow, a
107
+ positive demand means that the node want to receive flow. A flow on
108
+ the digraph G satisfies all demand if the net flow into each node
109
+ is equal to the demand of that node.
110
+
111
+ Parameters
112
+ ----------
113
+ G : NetworkX graph
114
+ DiGraph on which a minimum cost flow satisfying all demands is
115
+ to be found.
116
+
117
+ demand : string
118
+ Nodes of the graph G are expected to have an attribute demand
119
+ that indicates how much flow a node wants to send (negative
120
+ demand) or receive (positive demand). Note that the sum of the
121
+ demands should be 0 otherwise the problem in not feasible. If
122
+ this attribute is not present, a node is considered to have 0
123
+ demand. Default value: 'demand'.
124
+
125
+ capacity : string
126
+ Edges of the graph G are expected to have an attribute capacity
127
+ that indicates how much flow the edge can support. If this
128
+ attribute is not present, the edge is considered to have
129
+ infinite capacity. Default value: 'capacity'.
130
+
131
+ weight : string
132
+ Edges of the graph G are expected to have an attribute weight
133
+ that indicates the cost incurred by sending one unit of flow on
134
+ that edge. If not present, the weight is considered to be 0.
135
+ Default value: 'weight'.
136
+
137
+ Returns
138
+ -------
139
+ flowDict : dictionary
140
+ Dictionary of dictionaries keyed by nodes such that
141
+ flowDict[u][v] is the flow edge (u, v).
142
+
143
+ Raises
144
+ ------
145
+ NetworkXError
146
+ This exception is raised if the input graph is not directed or
147
+ not connected.
148
+
149
+ NetworkXUnfeasible
150
+ This exception is raised in the following situations:
151
+
152
+ * The sum of the demands is not zero. Then, there is no
153
+ flow satisfying all demands.
154
+ * There is no flow satisfying all demand.
155
+
156
+ NetworkXUnbounded
157
+ This exception is raised if the digraph G has a cycle of
158
+ negative cost and infinite capacity. Then, the cost of a flow
159
+ satisfying all demands is unbounded below.
160
+
161
+ See also
162
+ --------
163
+ cost_of_flow, max_flow_min_cost, min_cost_flow_cost, network_simplex
164
+
165
+ Notes
166
+ -----
167
+ This algorithm is not guaranteed to work if edge weights or demands
168
+ are floating point numbers (overflows and roundoff errors can
169
+ cause problems). As a workaround you can use integer numbers by
170
+ multiplying the relevant edge attributes by a convenient
171
+ constant factor (eg 100).
172
+
173
+ Examples
174
+ --------
175
+ A simple example of a min cost flow problem.
176
+
177
+ >>> G = nx.DiGraph()
178
+ >>> G.add_node("a", demand=-5)
179
+ >>> G.add_node("d", demand=5)
180
+ >>> G.add_edge("a", "b", weight=3, capacity=4)
181
+ >>> G.add_edge("a", "c", weight=6, capacity=10)
182
+ >>> G.add_edge("b", "d", weight=1, capacity=9)
183
+ >>> G.add_edge("c", "d", weight=2, capacity=5)
184
+ >>> flowDict = nx.min_cost_flow(G)
185
+ """
186
+ return nx.network_simplex(G, demand=demand, capacity=capacity, weight=weight)[1]
187
+
188
+
189
+ @nx._dispatch(edge_attrs={"weight": 0})
190
+ def cost_of_flow(G, flowDict, weight="weight"):
191
+ """Compute the cost of the flow given by flowDict on graph G.
192
+
193
+ Note that this function does not check for the validity of the
194
+ flow flowDict. This function will fail if the graph G and the
195
+ flow don't have the same edge set.
196
+
197
+ Parameters
198
+ ----------
199
+ G : NetworkX graph
200
+ DiGraph on which a minimum cost flow satisfying all demands is
201
+ to be found.
202
+
203
+ weight : string
204
+ Edges of the graph G are expected to have an attribute weight
205
+ that indicates the cost incurred by sending one unit of flow on
206
+ that edge. If not present, the weight is considered to be 0.
207
+ Default value: 'weight'.
208
+
209
+ flowDict : dictionary
210
+ Dictionary of dictionaries keyed by nodes such that
211
+ flowDict[u][v] is the flow edge (u, v).
212
+
213
+ Returns
214
+ -------
215
+ cost : Integer, float
216
+ The total cost of the flow. This is given by the sum over all
217
+ edges of the product of the edge's flow and the edge's weight.
218
+
219
+ See also
220
+ --------
221
+ max_flow_min_cost, min_cost_flow, min_cost_flow_cost, network_simplex
222
+
223
+ Notes
224
+ -----
225
+ This algorithm is not guaranteed to work if edge weights or demands
226
+ are floating point numbers (overflows and roundoff errors can
227
+ cause problems). As a workaround you can use integer numbers by
228
+ multiplying the relevant edge attributes by a convenient
229
+ constant factor (eg 100).
230
+ """
231
+ return sum((flowDict[u][v] * d.get(weight, 0) for u, v, d in G.edges(data=True)))
232
+
233
+
234
+ @nx._dispatch(edge_attrs={"capacity": float("inf"), "weight": 0})
235
+ def max_flow_min_cost(G, s, t, capacity="capacity", weight="weight"):
236
+ """Returns a maximum (s, t)-flow of minimum cost.
237
+
238
+ G is a digraph with edge costs and capacities. There is a source
239
+ node s and a sink node t. This function finds a maximum flow from
240
+ s to t whose total cost is minimized.
241
+
242
+ Parameters
243
+ ----------
244
+ G : NetworkX graph
245
+ DiGraph on which a minimum cost flow satisfying all demands is
246
+ to be found.
247
+
248
+ s: node label
249
+ Source of the flow.
250
+
251
+ t: node label
252
+ Destination of the flow.
253
+
254
+ capacity: string
255
+ Edges of the graph G are expected to have an attribute capacity
256
+ that indicates how much flow the edge can support. If this
257
+ attribute is not present, the edge is considered to have
258
+ infinite capacity. Default value: 'capacity'.
259
+
260
+ weight: string
261
+ Edges of the graph G are expected to have an attribute weight
262
+ that indicates the cost incurred by sending one unit of flow on
263
+ that edge. If not present, the weight is considered to be 0.
264
+ Default value: 'weight'.
265
+
266
+ Returns
267
+ -------
268
+ flowDict: dictionary
269
+ Dictionary of dictionaries keyed by nodes such that
270
+ flowDict[u][v] is the flow edge (u, v).
271
+
272
+ Raises
273
+ ------
274
+ NetworkXError
275
+ This exception is raised if the input graph is not directed or
276
+ not connected.
277
+
278
+ NetworkXUnbounded
279
+ This exception is raised if there is an infinite capacity path
280
+ from s to t in G. In this case there is no maximum flow. This
281
+ exception is also raised if the digraph G has a cycle of
282
+ negative cost and infinite capacity. Then, the cost of a flow
283
+ is unbounded below.
284
+
285
+ See also
286
+ --------
287
+ cost_of_flow, min_cost_flow, min_cost_flow_cost, network_simplex
288
+
289
+ Notes
290
+ -----
291
+ This algorithm is not guaranteed to work if edge weights or demands
292
+ are floating point numbers (overflows and roundoff errors can
293
+ cause problems). As a workaround you can use integer numbers by
294
+ multiplying the relevant edge attributes by a convenient
295
+ constant factor (eg 100).
296
+
297
+ Examples
298
+ --------
299
+ >>> G = nx.DiGraph()
300
+ >>> G.add_edges_from(
301
+ ... [
302
+ ... (1, 2, {"capacity": 12, "weight": 4}),
303
+ ... (1, 3, {"capacity": 20, "weight": 6}),
304
+ ... (2, 3, {"capacity": 6, "weight": -3}),
305
+ ... (2, 6, {"capacity": 14, "weight": 1}),
306
+ ... (3, 4, {"weight": 9}),
307
+ ... (3, 5, {"capacity": 10, "weight": 5}),
308
+ ... (4, 2, {"capacity": 19, "weight": 13}),
309
+ ... (4, 5, {"capacity": 4, "weight": 0}),
310
+ ... (5, 7, {"capacity": 28, "weight": 2}),
311
+ ... (6, 5, {"capacity": 11, "weight": 1}),
312
+ ... (6, 7, {"weight": 8}),
313
+ ... (7, 4, {"capacity": 6, "weight": 6}),
314
+ ... ]
315
+ ... )
316
+ >>> mincostFlow = nx.max_flow_min_cost(G, 1, 7)
317
+ >>> mincost = nx.cost_of_flow(G, mincostFlow)
318
+ >>> mincost
319
+ 373
320
+ >>> from networkx.algorithms.flow import maximum_flow
321
+ >>> maxFlow = maximum_flow(G, 1, 7)[1]
322
+ >>> nx.cost_of_flow(G, maxFlow) >= mincost
323
+ True
324
+ >>> mincostFlowValue = sum((mincostFlow[u][7] for u in G.predecessors(7))) - sum(
325
+ ... (mincostFlow[7][v] for v in G.successors(7))
326
+ ... )
327
+ >>> mincostFlowValue == nx.maximum_flow_value(G, 1, 7)
328
+ True
329
+
330
+ """
331
+ maxFlow = nx.maximum_flow_value(G, s, t, capacity=capacity)
332
+ H = nx.DiGraph(G)
333
+ H.add_node(s, demand=-maxFlow)
334
+ H.add_node(t, demand=maxFlow)
335
+ return min_cost_flow(H, capacity=capacity, weight=weight)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/shortestaugmentingpath.py ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Shortest augmenting path algorithm for maximum flow problems.
3
+ """
4
+
5
+ from collections import deque
6
+
7
+ import networkx as nx
8
+
9
+ from .edmondskarp import edmonds_karp_core
10
+ from .utils import CurrentEdge, build_residual_network
11
+
12
+ __all__ = ["shortest_augmenting_path"]
13
+
14
+
15
+ def shortest_augmenting_path_impl(G, s, t, capacity, residual, two_phase, cutoff):
16
+ """Implementation of the shortest augmenting path algorithm."""
17
+ if s not in G:
18
+ raise nx.NetworkXError(f"node {str(s)} not in graph")
19
+ if t not in G:
20
+ raise nx.NetworkXError(f"node {str(t)} not in graph")
21
+ if s == t:
22
+ raise nx.NetworkXError("source and sink are the same node")
23
+
24
+ if residual is None:
25
+ R = build_residual_network(G, capacity)
26
+ else:
27
+ R = residual
28
+
29
+ R_nodes = R.nodes
30
+ R_pred = R.pred
31
+ R_succ = R.succ
32
+
33
+ # Initialize/reset the residual network.
34
+ for u in R:
35
+ for e in R_succ[u].values():
36
+ e["flow"] = 0
37
+
38
+ # Initialize heights of the nodes.
39
+ heights = {t: 0}
40
+ q = deque([(t, 0)])
41
+ while q:
42
+ u, height = q.popleft()
43
+ height += 1
44
+ for v, attr in R_pred[u].items():
45
+ if v not in heights and attr["flow"] < attr["capacity"]:
46
+ heights[v] = height
47
+ q.append((v, height))
48
+
49
+ if s not in heights:
50
+ # t is not reachable from s in the residual network. The maximum flow
51
+ # must be zero.
52
+ R.graph["flow_value"] = 0
53
+ return R
54
+
55
+ n = len(G)
56
+ m = R.size() / 2
57
+
58
+ # Initialize heights and 'current edge' data structures of the nodes.
59
+ for u in R:
60
+ R_nodes[u]["height"] = heights[u] if u in heights else n
61
+ R_nodes[u]["curr_edge"] = CurrentEdge(R_succ[u])
62
+
63
+ # Initialize counts of nodes in each level.
64
+ counts = [0] * (2 * n - 1)
65
+ for u in R:
66
+ counts[R_nodes[u]["height"]] += 1
67
+
68
+ inf = R.graph["inf"]
69
+
70
+ def augment(path):
71
+ """Augment flow along a path from s to t."""
72
+ # Determine the path residual capacity.
73
+ flow = inf
74
+ it = iter(path)
75
+ u = next(it)
76
+ for v in it:
77
+ attr = R_succ[u][v]
78
+ flow = min(flow, attr["capacity"] - attr["flow"])
79
+ u = v
80
+ if flow * 2 > inf:
81
+ raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.")
82
+ # Augment flow along the path.
83
+ it = iter(path)
84
+ u = next(it)
85
+ for v in it:
86
+ R_succ[u][v]["flow"] += flow
87
+ R_succ[v][u]["flow"] -= flow
88
+ u = v
89
+ return flow
90
+
91
+ def relabel(u):
92
+ """Relabel a node to create an admissible edge."""
93
+ height = n - 1
94
+ for v, attr in R_succ[u].items():
95
+ if attr["flow"] < attr["capacity"]:
96
+ height = min(height, R_nodes[v]["height"])
97
+ return height + 1
98
+
99
+ if cutoff is None:
100
+ cutoff = float("inf")
101
+
102
+ # Phase 1: Look for shortest augmenting paths using depth-first search.
103
+
104
+ flow_value = 0
105
+ path = [s]
106
+ u = s
107
+ d = n if not two_phase else int(min(m**0.5, 2 * n ** (2.0 / 3)))
108
+ done = R_nodes[s]["height"] >= d
109
+ while not done:
110
+ height = R_nodes[u]["height"]
111
+ curr_edge = R_nodes[u]["curr_edge"]
112
+ # Depth-first search for the next node on the path to t.
113
+ while True:
114
+ v, attr = curr_edge.get()
115
+ if height == R_nodes[v]["height"] + 1 and attr["flow"] < attr["capacity"]:
116
+ # Advance to the next node following an admissible edge.
117
+ path.append(v)
118
+ u = v
119
+ break
120
+ try:
121
+ curr_edge.move_to_next()
122
+ except StopIteration:
123
+ counts[height] -= 1
124
+ if counts[height] == 0:
125
+ # Gap heuristic: If relabeling causes a level to become
126
+ # empty, a minimum cut has been identified. The algorithm
127
+ # can now be terminated.
128
+ R.graph["flow_value"] = flow_value
129
+ return R
130
+ height = relabel(u)
131
+ if u == s and height >= d:
132
+ if not two_phase:
133
+ # t is disconnected from s in the residual network. No
134
+ # more augmenting paths exist.
135
+ R.graph["flow_value"] = flow_value
136
+ return R
137
+ else:
138
+ # t is at least d steps away from s. End of phase 1.
139
+ done = True
140
+ break
141
+ counts[height] += 1
142
+ R_nodes[u]["height"] = height
143
+ if u != s:
144
+ # After relabeling, the last edge on the path is no longer
145
+ # admissible. Retreat one step to look for an alternative.
146
+ path.pop()
147
+ u = path[-1]
148
+ break
149
+ if u == t:
150
+ # t is reached. Augment flow along the path and reset it for a new
151
+ # depth-first search.
152
+ flow_value += augment(path)
153
+ if flow_value >= cutoff:
154
+ R.graph["flow_value"] = flow_value
155
+ return R
156
+ path = [s]
157
+ u = s
158
+
159
+ # Phase 2: Look for shortest augmenting paths using breadth-first search.
160
+ flow_value += edmonds_karp_core(R, s, t, cutoff - flow_value)
161
+
162
+ R.graph["flow_value"] = flow_value
163
+ return R
164
+
165
+
166
+ @nx._dispatch(
167
+ graphs={"G": 0, "residual?": 4},
168
+ edge_attrs={"capacity": float("inf")},
169
+ preserve_edge_attrs={"residual": {"capacity": float("inf")}},
170
+ preserve_graph_attrs={"residual"},
171
+ )
172
+ def shortest_augmenting_path(
173
+ G,
174
+ s,
175
+ t,
176
+ capacity="capacity",
177
+ residual=None,
178
+ value_only=False,
179
+ two_phase=False,
180
+ cutoff=None,
181
+ ):
182
+ r"""Find a maximum single-commodity flow using the shortest augmenting path
183
+ algorithm.
184
+
185
+ This function returns the residual network resulting after computing
186
+ the maximum flow. See below for details about the conventions
187
+ NetworkX uses for defining residual networks.
188
+
189
+ This algorithm has a running time of $O(n^2 m)$ for $n$ nodes and $m$
190
+ edges.
191
+
192
+
193
+ Parameters
194
+ ----------
195
+ G : NetworkX graph
196
+ Edges of the graph are expected to have an attribute called
197
+ 'capacity'. If this attribute is not present, the edge is
198
+ considered to have infinite capacity.
199
+
200
+ s : node
201
+ Source node for the flow.
202
+
203
+ t : node
204
+ Sink node for the flow.
205
+
206
+ capacity : string
207
+ Edges of the graph G are expected to have an attribute capacity
208
+ that indicates how much flow the edge can support. If this
209
+ attribute is not present, the edge is considered to have
210
+ infinite capacity. Default value: 'capacity'.
211
+
212
+ residual : NetworkX graph
213
+ Residual network on which the algorithm is to be executed. If None, a
214
+ new residual network is created. Default value: None.
215
+
216
+ value_only : bool
217
+ If True compute only the value of the maximum flow. This parameter
218
+ will be ignored by this algorithm because it is not applicable.
219
+
220
+ two_phase : bool
221
+ If True, a two-phase variant is used. The two-phase variant improves
222
+ the running time on unit-capacity networks from $O(nm)$ to
223
+ $O(\min(n^{2/3}, m^{1/2}) m)$. Default value: False.
224
+
225
+ cutoff : integer, float
226
+ If specified, the algorithm will terminate when the flow value reaches
227
+ or exceeds the cutoff. In this case, it may be unable to immediately
228
+ determine a minimum cut. Default value: None.
229
+
230
+ Returns
231
+ -------
232
+ R : NetworkX DiGraph
233
+ Residual network after computing the maximum flow.
234
+
235
+ Raises
236
+ ------
237
+ NetworkXError
238
+ The algorithm does not support MultiGraph and MultiDiGraph. If
239
+ the input graph is an instance of one of these two classes, a
240
+ NetworkXError is raised.
241
+
242
+ NetworkXUnbounded
243
+ If the graph has a path of infinite capacity, the value of a
244
+ feasible flow on the graph is unbounded above and the function
245
+ raises a NetworkXUnbounded.
246
+
247
+ See also
248
+ --------
249
+ :meth:`maximum_flow`
250
+ :meth:`minimum_cut`
251
+ :meth:`edmonds_karp`
252
+ :meth:`preflow_push`
253
+
254
+ Notes
255
+ -----
256
+ The residual network :samp:`R` from an input graph :samp:`G` has the
257
+ same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
258
+ of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
259
+ self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
260
+ in :samp:`G`.
261
+
262
+ For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
263
+ is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
264
+ in :samp:`G` or zero otherwise. If the capacity is infinite,
265
+ :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
266
+ that does not affect the solution of the problem. This value is stored in
267
+ :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
268
+ :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
269
+ satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
270
+
271
+ The flow value, defined as the total flow into :samp:`t`, the sink, is
272
+ stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not
273
+ specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such
274
+ that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
275
+ :samp:`s`-:samp:`t` cut.
276
+
277
+ Examples
278
+ --------
279
+ >>> from networkx.algorithms.flow import shortest_augmenting_path
280
+
281
+ The functions that implement flow algorithms and output a residual
282
+ network, such as this one, are not imported to the base NetworkX
283
+ namespace, so you have to explicitly import them from the flow package.
284
+
285
+ >>> G = nx.DiGraph()
286
+ >>> G.add_edge("x", "a", capacity=3.0)
287
+ >>> G.add_edge("x", "b", capacity=1.0)
288
+ >>> G.add_edge("a", "c", capacity=3.0)
289
+ >>> G.add_edge("b", "c", capacity=5.0)
290
+ >>> G.add_edge("b", "d", capacity=4.0)
291
+ >>> G.add_edge("d", "e", capacity=2.0)
292
+ >>> G.add_edge("c", "y", capacity=2.0)
293
+ >>> G.add_edge("e", "y", capacity=3.0)
294
+ >>> R = shortest_augmenting_path(G, "x", "y")
295
+ >>> flow_value = nx.maximum_flow_value(G, "x", "y")
296
+ >>> flow_value
297
+ 3.0
298
+ >>> flow_value == R.graph["flow_value"]
299
+ True
300
+
301
+ """
302
+ R = shortest_augmenting_path_impl(G, s, t, capacity, residual, two_phase, cutoff)
303
+ R.graph["algorithm"] = "shortest_augmenting_path"
304
+ return R
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/test_mincost.py ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import bz2
2
+ import importlib.resources
3
+ import os
4
+ import pickle
5
+
6
+ import pytest
7
+
8
+ import networkx as nx
9
+
10
+
11
+ class TestMinCostFlow:
12
+ def test_simple_digraph(self):
13
+ G = nx.DiGraph()
14
+ G.add_node("a", demand=-5)
15
+ G.add_node("d", demand=5)
16
+ G.add_edge("a", "b", weight=3, capacity=4)
17
+ G.add_edge("a", "c", weight=6, capacity=10)
18
+ G.add_edge("b", "d", weight=1, capacity=9)
19
+ G.add_edge("c", "d", weight=2, capacity=5)
20
+ flowCost, H = nx.network_simplex(G)
21
+ soln = {"a": {"b": 4, "c": 1}, "b": {"d": 4}, "c": {"d": 1}, "d": {}}
22
+ assert flowCost == 24
23
+ assert nx.min_cost_flow_cost(G) == 24
24
+ assert H == soln
25
+ assert nx.min_cost_flow(G) == soln
26
+ assert nx.cost_of_flow(G, H) == 24
27
+
28
+ flowCost, H = nx.capacity_scaling(G)
29
+ assert flowCost == 24
30
+ assert nx.cost_of_flow(G, H) == 24
31
+ assert H == soln
32
+
33
+ def test_negcycle_infcap(self):
34
+ G = nx.DiGraph()
35
+ G.add_node("s", demand=-5)
36
+ G.add_node("t", demand=5)
37
+ G.add_edge("s", "a", weight=1, capacity=3)
38
+ G.add_edge("a", "b", weight=3)
39
+ G.add_edge("c", "a", weight=-6)
40
+ G.add_edge("b", "d", weight=1)
41
+ G.add_edge("d", "c", weight=-2)
42
+ G.add_edge("d", "t", weight=1, capacity=3)
43
+ pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
44
+ pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
45
+
46
+ def test_sum_demands_not_zero(self):
47
+ G = nx.DiGraph()
48
+ G.add_node("s", demand=-5)
49
+ G.add_node("t", demand=4)
50
+ G.add_edge("s", "a", weight=1, capacity=3)
51
+ G.add_edge("a", "b", weight=3)
52
+ G.add_edge("a", "c", weight=-6)
53
+ G.add_edge("b", "d", weight=1)
54
+ G.add_edge("c", "d", weight=-2)
55
+ G.add_edge("d", "t", weight=1, capacity=3)
56
+ pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
57
+ pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
58
+
59
+ def test_no_flow_satisfying_demands(self):
60
+ G = nx.DiGraph()
61
+ G.add_node("s", demand=-5)
62
+ G.add_node("t", demand=5)
63
+ G.add_edge("s", "a", weight=1, capacity=3)
64
+ G.add_edge("a", "b", weight=3)
65
+ G.add_edge("a", "c", weight=-6)
66
+ G.add_edge("b", "d", weight=1)
67
+ G.add_edge("c", "d", weight=-2)
68
+ G.add_edge("d", "t", weight=1, capacity=3)
69
+ pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
70
+ pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
71
+
72
+ def test_transshipment(self):
73
+ G = nx.DiGraph()
74
+ G.add_node("a", demand=1)
75
+ G.add_node("b", demand=-2)
76
+ G.add_node("c", demand=-2)
77
+ G.add_node("d", demand=3)
78
+ G.add_node("e", demand=-4)
79
+ G.add_node("f", demand=-4)
80
+ G.add_node("g", demand=3)
81
+ G.add_node("h", demand=2)
82
+ G.add_node("r", demand=3)
83
+ G.add_edge("a", "c", weight=3)
84
+ G.add_edge("r", "a", weight=2)
85
+ G.add_edge("b", "a", weight=9)
86
+ G.add_edge("r", "c", weight=0)
87
+ G.add_edge("b", "r", weight=-6)
88
+ G.add_edge("c", "d", weight=5)
89
+ G.add_edge("e", "r", weight=4)
90
+ G.add_edge("e", "f", weight=3)
91
+ G.add_edge("h", "b", weight=4)
92
+ G.add_edge("f", "d", weight=7)
93
+ G.add_edge("f", "h", weight=12)
94
+ G.add_edge("g", "d", weight=12)
95
+ G.add_edge("f", "g", weight=-1)
96
+ G.add_edge("h", "g", weight=-10)
97
+ flowCost, H = nx.network_simplex(G)
98
+ soln = {
99
+ "a": {"c": 0},
100
+ "b": {"a": 0, "r": 2},
101
+ "c": {"d": 3},
102
+ "d": {},
103
+ "e": {"r": 3, "f": 1},
104
+ "f": {"d": 0, "g": 3, "h": 2},
105
+ "g": {"d": 0},
106
+ "h": {"b": 0, "g": 0},
107
+ "r": {"a": 1, "c": 1},
108
+ }
109
+ assert flowCost == 41
110
+ assert nx.min_cost_flow_cost(G) == 41
111
+ assert H == soln
112
+ assert nx.min_cost_flow(G) == soln
113
+ assert nx.cost_of_flow(G, H) == 41
114
+
115
+ flowCost, H = nx.capacity_scaling(G)
116
+ assert flowCost == 41
117
+ assert nx.cost_of_flow(G, H) == 41
118
+ assert H == soln
119
+
120
+ def test_max_flow_min_cost(self):
121
+ G = nx.DiGraph()
122
+ G.add_edge("s", "a", bandwidth=6)
123
+ G.add_edge("s", "c", bandwidth=10, cost=10)
124
+ G.add_edge("a", "b", cost=6)
125
+ G.add_edge("b", "d", bandwidth=8, cost=7)
126
+ G.add_edge("c", "d", cost=10)
127
+ G.add_edge("d", "t", bandwidth=5, cost=5)
128
+ soln = {
129
+ "s": {"a": 5, "c": 0},
130
+ "a": {"b": 5},
131
+ "b": {"d": 5},
132
+ "c": {"d": 0},
133
+ "d": {"t": 5},
134
+ "t": {},
135
+ }
136
+ flow = nx.max_flow_min_cost(G, "s", "t", capacity="bandwidth", weight="cost")
137
+ assert flow == soln
138
+ assert nx.cost_of_flow(G, flow, weight="cost") == 90
139
+
140
+ G.add_edge("t", "s", cost=-100)
141
+ flowCost, flow = nx.capacity_scaling(G, capacity="bandwidth", weight="cost")
142
+ G.remove_edge("t", "s")
143
+ assert flowCost == -410
144
+ assert flow["t"]["s"] == 5
145
+ del flow["t"]["s"]
146
+ assert flow == soln
147
+ assert nx.cost_of_flow(G, flow, weight="cost") == 90
148
+
149
+ def test_digraph1(self):
150
+ # From Bradley, S. P., Hax, A. C. and Magnanti, T. L. Applied
151
+ # Mathematical Programming. Addison-Wesley, 1977.
152
+ G = nx.DiGraph()
153
+ G.add_node(1, demand=-20)
154
+ G.add_node(4, demand=5)
155
+ G.add_node(5, demand=15)
156
+ G.add_edges_from(
157
+ [
158
+ (1, 2, {"capacity": 15, "weight": 4}),
159
+ (1, 3, {"capacity": 8, "weight": 4}),
160
+ (2, 3, {"weight": 2}),
161
+ (2, 4, {"capacity": 4, "weight": 2}),
162
+ (2, 5, {"capacity": 10, "weight": 6}),
163
+ (3, 4, {"capacity": 15, "weight": 1}),
164
+ (3, 5, {"capacity": 5, "weight": 3}),
165
+ (4, 5, {"weight": 2}),
166
+ (5, 3, {"capacity": 4, "weight": 1}),
167
+ ]
168
+ )
169
+ flowCost, H = nx.network_simplex(G)
170
+ soln = {
171
+ 1: {2: 12, 3: 8},
172
+ 2: {3: 8, 4: 4, 5: 0},
173
+ 3: {4: 11, 5: 5},
174
+ 4: {5: 10},
175
+ 5: {3: 0},
176
+ }
177
+ assert flowCost == 150
178
+ assert nx.min_cost_flow_cost(G) == 150
179
+ assert H == soln
180
+ assert nx.min_cost_flow(G) == soln
181
+ assert nx.cost_of_flow(G, H) == 150
182
+
183
+ flowCost, H = nx.capacity_scaling(G)
184
+ assert flowCost == 150
185
+ assert H == soln
186
+ assert nx.cost_of_flow(G, H) == 150
187
+
188
+ def test_digraph2(self):
189
+ # Example from ticket #430 from mfrasca. Original source:
190
+ # http://www.cs.princeton.edu/courses/archive/spr03/cs226/lectures/mincost.4up.pdf, slide 11.
191
+ G = nx.DiGraph()
192
+ G.add_edge("s", 1, capacity=12)
193
+ G.add_edge("s", 2, capacity=6)
194
+ G.add_edge("s", 3, capacity=14)
195
+ G.add_edge(1, 2, capacity=11, weight=4)
196
+ G.add_edge(2, 3, capacity=9, weight=6)
197
+ G.add_edge(1, 4, capacity=5, weight=5)
198
+ G.add_edge(1, 5, capacity=2, weight=12)
199
+ G.add_edge(2, 5, capacity=4, weight=4)
200
+ G.add_edge(2, 6, capacity=2, weight=6)
201
+ G.add_edge(3, 6, capacity=31, weight=3)
202
+ G.add_edge(4, 5, capacity=18, weight=4)
203
+ G.add_edge(5, 6, capacity=9, weight=5)
204
+ G.add_edge(4, "t", capacity=3)
205
+ G.add_edge(5, "t", capacity=7)
206
+ G.add_edge(6, "t", capacity=22)
207
+ flow = nx.max_flow_min_cost(G, "s", "t")
208
+ soln = {
209
+ 1: {2: 6, 4: 5, 5: 1},
210
+ 2: {3: 6, 5: 4, 6: 2},
211
+ 3: {6: 20},
212
+ 4: {5: 2, "t": 3},
213
+ 5: {6: 0, "t": 7},
214
+ 6: {"t": 22},
215
+ "s": {1: 12, 2: 6, 3: 14},
216
+ "t": {},
217
+ }
218
+ assert flow == soln
219
+
220
+ G.add_edge("t", "s", weight=-100)
221
+ flowCost, flow = nx.capacity_scaling(G)
222
+ G.remove_edge("t", "s")
223
+ assert flow["t"]["s"] == 32
224
+ assert flowCost == -3007
225
+ del flow["t"]["s"]
226
+ assert flow == soln
227
+ assert nx.cost_of_flow(G, flow) == 193
228
+
229
+ def test_digraph3(self):
230
+ """Combinatorial Optimization: Algorithms and Complexity,
231
+ Papadimitriou Steiglitz at page 140 has an example, 7.1, but that
232
+ admits multiple solutions, so I alter it a bit. From ticket #430
233
+ by mfrasca."""
234
+
235
+ G = nx.DiGraph()
236
+ G.add_edge("s", "a")
237
+ G["s"]["a"].update({0: 2, 1: 4})
238
+ G.add_edge("s", "b")
239
+ G["s"]["b"].update({0: 2, 1: 1})
240
+ G.add_edge("a", "b")
241
+ G["a"]["b"].update({0: 5, 1: 2})
242
+ G.add_edge("a", "t")
243
+ G["a"]["t"].update({0: 1, 1: 5})
244
+ G.add_edge("b", "a")
245
+ G["b"]["a"].update({0: 1, 1: 3})
246
+ G.add_edge("b", "t")
247
+ G["b"]["t"].update({0: 3, 1: 2})
248
+
249
+ "PS.ex.7.1: testing main function"
250
+ sol = nx.max_flow_min_cost(G, "s", "t", capacity=0, weight=1)
251
+ flow = sum(v for v in sol["s"].values())
252
+ assert 4 == flow
253
+ assert 23 == nx.cost_of_flow(G, sol, weight=1)
254
+ assert sol["s"] == {"a": 2, "b": 2}
255
+ assert sol["a"] == {"b": 1, "t": 1}
256
+ assert sol["b"] == {"a": 0, "t": 3}
257
+ assert sol["t"] == {}
258
+
259
+ G.add_edge("t", "s")
260
+ G["t"]["s"].update({1: -100})
261
+ flowCost, sol = nx.capacity_scaling(G, capacity=0, weight=1)
262
+ G.remove_edge("t", "s")
263
+ flow = sum(v for v in sol["s"].values())
264
+ assert 4 == flow
265
+ assert sol["t"]["s"] == 4
266
+ assert flowCost == -377
267
+ del sol["t"]["s"]
268
+ assert sol["s"] == {"a": 2, "b": 2}
269
+ assert sol["a"] == {"b": 1, "t": 1}
270
+ assert sol["b"] == {"a": 0, "t": 3}
271
+ assert sol["t"] == {}
272
+ assert nx.cost_of_flow(G, sol, weight=1) == 23
273
+
274
+ def test_zero_capacity_edges(self):
275
+ """Address issue raised in ticket #617 by arv."""
276
+ G = nx.DiGraph()
277
+ G.add_edges_from(
278
+ [
279
+ (1, 2, {"capacity": 1, "weight": 1}),
280
+ (1, 5, {"capacity": 1, "weight": 1}),
281
+ (2, 3, {"capacity": 0, "weight": 1}),
282
+ (2, 5, {"capacity": 1, "weight": 1}),
283
+ (5, 3, {"capacity": 2, "weight": 1}),
284
+ (5, 4, {"capacity": 0, "weight": 1}),
285
+ (3, 4, {"capacity": 2, "weight": 1}),
286
+ ]
287
+ )
288
+ G.nodes[1]["demand"] = -1
289
+ G.nodes[2]["demand"] = -1
290
+ G.nodes[4]["demand"] = 2
291
+
292
+ flowCost, H = nx.network_simplex(G)
293
+ soln = {1: {2: 0, 5: 1}, 2: {3: 0, 5: 1}, 3: {4: 2}, 4: {}, 5: {3: 2, 4: 0}}
294
+ assert flowCost == 6
295
+ assert nx.min_cost_flow_cost(G) == 6
296
+ assert H == soln
297
+ assert nx.min_cost_flow(G) == soln
298
+ assert nx.cost_of_flow(G, H) == 6
299
+
300
+ flowCost, H = nx.capacity_scaling(G)
301
+ assert flowCost == 6
302
+ assert H == soln
303
+ assert nx.cost_of_flow(G, H) == 6
304
+
305
+ def test_digon(self):
306
+ """Check if digons are handled properly. Taken from ticket
307
+ #618 by arv."""
308
+ nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
309
+ edges = [
310
+ (1, 2, {"capacity": 3, "weight": 600000}),
311
+ (2, 1, {"capacity": 2, "weight": 0}),
312
+ (2, 3, {"capacity": 5, "weight": 714285}),
313
+ (3, 2, {"capacity": 2, "weight": 0}),
314
+ ]
315
+ G = nx.DiGraph(edges)
316
+ G.add_nodes_from(nodes)
317
+ flowCost, H = nx.network_simplex(G)
318
+ soln = {1: {2: 0}, 2: {1: 0, 3: 4}, 3: {2: 0}}
319
+ assert flowCost == 2857140
320
+ assert nx.min_cost_flow_cost(G) == 2857140
321
+ assert H == soln
322
+ assert nx.min_cost_flow(G) == soln
323
+ assert nx.cost_of_flow(G, H) == 2857140
324
+
325
+ flowCost, H = nx.capacity_scaling(G)
326
+ assert flowCost == 2857140
327
+ assert H == soln
328
+ assert nx.cost_of_flow(G, H) == 2857140
329
+
330
+ def test_deadend(self):
331
+ """Check if one-node cycles are handled properly. Taken from ticket
332
+ #2906 from @sshraven."""
333
+ G = nx.DiGraph()
334
+
335
+ G.add_nodes_from(range(5), demand=0)
336
+ G.nodes[4]["demand"] = -13
337
+ G.nodes[3]["demand"] = 13
338
+
339
+ G.add_edges_from([(0, 2), (0, 3), (2, 1)], capacity=20, weight=0.1)
340
+ pytest.raises(nx.NetworkXUnfeasible, nx.min_cost_flow, G)
341
+
342
+ def test_infinite_capacity_neg_digon(self):
343
+ """An infinite capacity negative cost digon results in an unbounded
344
+ instance."""
345
+ nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
346
+ edges = [
347
+ (1, 2, {"weight": -600}),
348
+ (2, 1, {"weight": 0}),
349
+ (2, 3, {"capacity": 5, "weight": 714285}),
350
+ (3, 2, {"capacity": 2, "weight": 0}),
351
+ ]
352
+ G = nx.DiGraph(edges)
353
+ G.add_nodes_from(nodes)
354
+ pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
355
+ pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
356
+
357
+ def test_finite_capacity_neg_digon(self):
358
+ """The digon should receive the maximum amount of flow it can handle.
359
+ Taken from ticket #749 by @chuongdo."""
360
+ G = nx.DiGraph()
361
+ G.add_edge("a", "b", capacity=1, weight=-1)
362
+ G.add_edge("b", "a", capacity=1, weight=-1)
363
+ min_cost = -2
364
+ assert nx.min_cost_flow_cost(G) == min_cost
365
+
366
+ flowCost, H = nx.capacity_scaling(G)
367
+ assert flowCost == -2
368
+ assert H == {"a": {"b": 1}, "b": {"a": 1}}
369
+ assert nx.cost_of_flow(G, H) == -2
370
+
371
+ def test_multidigraph(self):
372
+ """Multidigraphs are acceptable."""
373
+ G = nx.MultiDiGraph()
374
+ G.add_weighted_edges_from([(1, 2, 1), (2, 3, 2)], weight="capacity")
375
+ flowCost, H = nx.network_simplex(G)
376
+ assert flowCost == 0
377
+ assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
378
+
379
+ flowCost, H = nx.capacity_scaling(G)
380
+ assert flowCost == 0
381
+ assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
382
+
383
+ def test_negative_selfloops(self):
384
+ """Negative selfloops should cause an exception if uncapacitated and
385
+ always be saturated otherwise.
386
+ """
387
+ G = nx.DiGraph()
388
+ G.add_edge(1, 1, weight=-1)
389
+ pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
390
+ pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
391
+ G[1][1]["capacity"] = 2
392
+ flowCost, H = nx.network_simplex(G)
393
+ assert flowCost == -2
394
+ assert H == {1: {1: 2}}
395
+ flowCost, H = nx.capacity_scaling(G)
396
+ assert flowCost == -2
397
+ assert H == {1: {1: 2}}
398
+
399
+ G = nx.MultiDiGraph()
400
+ G.add_edge(1, 1, "x", weight=-1)
401
+ G.add_edge(1, 1, "y", weight=1)
402
+ pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
403
+ pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
404
+ G[1][1]["x"]["capacity"] = 2
405
+ flowCost, H = nx.network_simplex(G)
406
+ assert flowCost == -2
407
+ assert H == {1: {1: {"x": 2, "y": 0}}}
408
+ flowCost, H = nx.capacity_scaling(G)
409
+ assert flowCost == -2
410
+ assert H == {1: {1: {"x": 2, "y": 0}}}
411
+
412
+ def test_bone_shaped(self):
413
+ # From #1283
414
+ G = nx.DiGraph()
415
+ G.add_node(0, demand=-4)
416
+ G.add_node(1, demand=2)
417
+ G.add_node(2, demand=2)
418
+ G.add_node(3, demand=4)
419
+ G.add_node(4, demand=-2)
420
+ G.add_node(5, demand=-2)
421
+ G.add_edge(0, 1, capacity=4)
422
+ G.add_edge(0, 2, capacity=4)
423
+ G.add_edge(4, 3, capacity=4)
424
+ G.add_edge(5, 3, capacity=4)
425
+ G.add_edge(0, 3, capacity=0)
426
+ flowCost, H = nx.network_simplex(G)
427
+ assert flowCost == 0
428
+ assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
429
+ flowCost, H = nx.capacity_scaling(G)
430
+ assert flowCost == 0
431
+ assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
432
+
433
+ def test_exceptions(self):
434
+ G = nx.Graph()
435
+ pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
436
+ pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
437
+ G = nx.MultiGraph()
438
+ pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
439
+ pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
440
+ G = nx.DiGraph()
441
+ pytest.raises(nx.NetworkXError, nx.network_simplex, G)
442
+ # pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
443
+ G.add_node(0, demand=float("inf"))
444
+ pytest.raises(nx.NetworkXError, nx.network_simplex, G)
445
+ pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
446
+ G.nodes[0]["demand"] = 0
447
+ G.add_node(1, demand=0)
448
+ G.add_edge(0, 1, weight=-float("inf"))
449
+ pytest.raises(nx.NetworkXError, nx.network_simplex, G)
450
+ pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
451
+ G[0][1]["weight"] = 0
452
+ G.add_edge(0, 0, weight=float("inf"))
453
+ pytest.raises(nx.NetworkXError, nx.network_simplex, G)
454
+ # pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
455
+ G[0][0]["weight"] = 0
456
+ G[0][1]["capacity"] = -1
457
+ pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
458
+ # pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
459
+ G[0][1]["capacity"] = 0
460
+ G[0][0]["capacity"] = -1
461
+ pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
462
+ # pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
463
+
464
+ def test_large(self):
465
+ fname = (
466
+ importlib.resources.files("networkx.algorithms.flow.tests")
467
+ / "netgen-2.gpickle.bz2"
468
+ )
469
+ with bz2.BZ2File(fname, "rb") as f:
470
+ G = pickle.load(f)
471
+ flowCost, flowDict = nx.network_simplex(G)
472
+ assert 6749969302 == flowCost
473
+ assert 6749969302 == nx.cost_of_flow(G, flowDict)
474
+ flowCost, flowDict = nx.capacity_scaling(G)
475
+ assert 6749969302 == flowCost
476
+ assert 6749969302 == nx.cost_of_flow(G, flowDict)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/graph_hashing.py ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Functions for hashing graphs to strings.
3
+ Isomorphic graphs should be assigned identical hashes.
4
+ For now, only Weisfeiler-Lehman hashing is implemented.
5
+ """
6
+
7
+ from collections import Counter, defaultdict
8
+ from hashlib import blake2b
9
+
10
+ import networkx as nx
11
+
12
+ __all__ = ["weisfeiler_lehman_graph_hash", "weisfeiler_lehman_subgraph_hashes"]
13
+
14
+
15
+ def _hash_label(label, digest_size):
16
+ return blake2b(label.encode("ascii"), digest_size=digest_size).hexdigest()
17
+
18
+
19
+ def _init_node_labels(G, edge_attr, node_attr):
20
+ if node_attr:
21
+ return {u: str(dd[node_attr]) for u, dd in G.nodes(data=True)}
22
+ elif edge_attr:
23
+ return {u: "" for u in G}
24
+ else:
25
+ return {u: str(deg) for u, deg in G.degree()}
26
+
27
+
28
+ def _neighborhood_aggregate(G, node, node_labels, edge_attr=None):
29
+ """
30
+ Compute new labels for given node by aggregating
31
+ the labels of each node's neighbors.
32
+ """
33
+ label_list = []
34
+ for nbr in G.neighbors(node):
35
+ prefix = "" if edge_attr is None else str(G[node][nbr][edge_attr])
36
+ label_list.append(prefix + node_labels[nbr])
37
+ return node_labels[node] + "".join(sorted(label_list))
38
+
39
+
40
+ @nx._dispatch(edge_attrs={"edge_attr": None}, node_attrs="node_attr")
41
+ def weisfeiler_lehman_graph_hash(
42
+ G, edge_attr=None, node_attr=None, iterations=3, digest_size=16
43
+ ):
44
+ """Return Weisfeiler Lehman (WL) graph hash.
45
+
46
+ The function iteratively aggregates and hashes neighbourhoods of each node.
47
+ After each node's neighbors are hashed to obtain updated node labels,
48
+ a hashed histogram of resulting labels is returned as the final hash.
49
+
50
+ Hashes are identical for isomorphic graphs and strong guarantees that
51
+ non-isomorphic graphs will get different hashes. See [1]_ for details.
52
+
53
+ If no node or edge attributes are provided, the degree of each node
54
+ is used as its initial label.
55
+ Otherwise, node and/or edge labels are used to compute the hash.
56
+
57
+ Parameters
58
+ ----------
59
+ G: graph
60
+ The graph to be hashed.
61
+ Can have node and/or edge attributes. Can also have no attributes.
62
+ edge_attr: string, default=None
63
+ The key in edge attribute dictionary to be used for hashing.
64
+ If None, edge labels are ignored.
65
+ node_attr: string, default=None
66
+ The key in node attribute dictionary to be used for hashing.
67
+ If None, and no edge_attr given, use the degrees of the nodes as labels.
68
+ iterations: int, default=3
69
+ Number of neighbor aggregations to perform.
70
+ Should be larger for larger graphs.
71
+ digest_size: int, default=16
72
+ Size (in bits) of blake2b hash digest to use for hashing node labels.
73
+
74
+ Returns
75
+ -------
76
+ h : string
77
+ Hexadecimal string corresponding to hash of the input graph.
78
+
79
+ Examples
80
+ --------
81
+ Two graphs with edge attributes that are isomorphic, except for
82
+ differences in the edge labels.
83
+
84
+ >>> G1 = nx.Graph()
85
+ >>> G1.add_edges_from(
86
+ ... [
87
+ ... (1, 2, {"label": "A"}),
88
+ ... (2, 3, {"label": "A"}),
89
+ ... (3, 1, {"label": "A"}),
90
+ ... (1, 4, {"label": "B"}),
91
+ ... ]
92
+ ... )
93
+ >>> G2 = nx.Graph()
94
+ >>> G2.add_edges_from(
95
+ ... [
96
+ ... (5, 6, {"label": "B"}),
97
+ ... (6, 7, {"label": "A"}),
98
+ ... (7, 5, {"label": "A"}),
99
+ ... (7, 8, {"label": "A"}),
100
+ ... ]
101
+ ... )
102
+
103
+ Omitting the `edge_attr` option, results in identical hashes.
104
+
105
+ >>> nx.weisfeiler_lehman_graph_hash(G1)
106
+ '7bc4dde9a09d0b94c5097b219891d81a'
107
+ >>> nx.weisfeiler_lehman_graph_hash(G2)
108
+ '7bc4dde9a09d0b94c5097b219891d81a'
109
+
110
+ With edge labels, the graphs are no longer assigned
111
+ the same hash digest.
112
+
113
+ >>> nx.weisfeiler_lehman_graph_hash(G1, edge_attr="label")
114
+ 'c653d85538bcf041d88c011f4f905f10'
115
+ >>> nx.weisfeiler_lehman_graph_hash(G2, edge_attr="label")
116
+ '3dcd84af1ca855d0eff3c978d88e7ec7'
117
+
118
+ Notes
119
+ -----
120
+ To return the WL hashes of each subgraph of a graph, use
121
+ `weisfeiler_lehman_subgraph_hashes`
122
+
123
+ Similarity between hashes does not imply similarity between graphs.
124
+
125
+ References
126
+ ----------
127
+ .. [1] Shervashidze, Nino, Pascal Schweitzer, Erik Jan Van Leeuwen,
128
+ Kurt Mehlhorn, and Karsten M. Borgwardt. Weisfeiler Lehman
129
+ Graph Kernels. Journal of Machine Learning Research. 2011.
130
+ http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf
131
+
132
+ See also
133
+ --------
134
+ weisfeiler_lehman_subgraph_hashes
135
+ """
136
+
137
+ def weisfeiler_lehman_step(G, labels, edge_attr=None):
138
+ """
139
+ Apply neighborhood aggregation to each node
140
+ in the graph.
141
+ Computes a dictionary with labels for each node.
142
+ """
143
+ new_labels = {}
144
+ for node in G.nodes():
145
+ label = _neighborhood_aggregate(G, node, labels, edge_attr=edge_attr)
146
+ new_labels[node] = _hash_label(label, digest_size)
147
+ return new_labels
148
+
149
+ # set initial node labels
150
+ node_labels = _init_node_labels(G, edge_attr, node_attr)
151
+
152
+ subgraph_hash_counts = []
153
+ for _ in range(iterations):
154
+ node_labels = weisfeiler_lehman_step(G, node_labels, edge_attr=edge_attr)
155
+ counter = Counter(node_labels.values())
156
+ # sort the counter, extend total counts
157
+ subgraph_hash_counts.extend(sorted(counter.items(), key=lambda x: x[0]))
158
+
159
+ # hash the final counter
160
+ return _hash_label(str(tuple(subgraph_hash_counts)), digest_size)
161
+
162
+
163
+ @nx._dispatch(edge_attrs={"edge_attr": None}, node_attrs="node_attr")
164
+ def weisfeiler_lehman_subgraph_hashes(
165
+ G, edge_attr=None, node_attr=None, iterations=3, digest_size=16
166
+ ):
167
+ """
168
+ Return a dictionary of subgraph hashes by node.
169
+
170
+ Dictionary keys are nodes in `G`, and values are a list of hashes.
171
+ Each hash corresponds to a subgraph rooted at a given node u in `G`.
172
+ Lists of subgraph hashes are sorted in increasing order of depth from
173
+ their root node, with the hash at index i corresponding to a subgraph
174
+ of nodes at most i edges distance from u. Thus, each list will contain
175
+ ``iterations + 1`` elements - a hash for a subgraph at each depth, and
176
+ additionally a hash of the initial node label (or equivalently a
177
+ subgraph of depth 0)
178
+
179
+ The function iteratively aggregates and hashes neighbourhoods of each node.
180
+ This is achieved for each step by replacing for each node its label from
181
+ the previous iteration with its hashed 1-hop neighborhood aggregate.
182
+ The new node label is then appended to a list of node labels for each
183
+ node.
184
+
185
+ To aggregate neighborhoods at each step for a node $n$, all labels of
186
+ nodes adjacent to $n$ are concatenated. If the `edge_attr` parameter is set,
187
+ labels for each neighboring node are prefixed with the value of this attribute
188
+ along the connecting edge from this neighbor to node $n$. The resulting string
189
+ is then hashed to compress this information into a fixed digest size.
190
+
191
+ Thus, at the $i$-th iteration, nodes within $i$ hops influence any given
192
+ hashed node label. We can therefore say that at depth $i$ for node $n$
193
+ we have a hash for a subgraph induced by the $2i$-hop neighborhood of $n$.
194
+
195
+ The output can be used to to create general Weisfeiler-Lehman graph kernels,
196
+ or generate features for graphs or nodes - for example to generate 'words' in
197
+ a graph as seen in the 'graph2vec' algorithm.
198
+ See [1]_ & [2]_ respectively for details.
199
+
200
+ Hashes are identical for isomorphic subgraphs and there exist strong
201
+ guarantees that non-isomorphic graphs will get different hashes.
202
+ See [1]_ for details.
203
+
204
+ If no node or edge attributes are provided, the degree of each node
205
+ is used as its initial label.
206
+ Otherwise, node and/or edge labels are used to compute the hash.
207
+
208
+ Parameters
209
+ ----------
210
+ G: graph
211
+ The graph to be hashed.
212
+ Can have node and/or edge attributes. Can also have no attributes.
213
+ edge_attr: string, default=None
214
+ The key in edge attribute dictionary to be used for hashing.
215
+ If None, edge labels are ignored.
216
+ node_attr: string, default=None
217
+ The key in node attribute dictionary to be used for hashing.
218
+ If None, and no edge_attr given, use the degrees of the nodes as labels.
219
+ iterations: int, default=3
220
+ Number of neighbor aggregations to perform.
221
+ Should be larger for larger graphs.
222
+ digest_size: int, default=16
223
+ Size (in bits) of blake2b hash digest to use for hashing node labels.
224
+ The default size is 16 bits
225
+
226
+ Returns
227
+ -------
228
+ node_subgraph_hashes : dict
229
+ A dictionary with each key given by a node in G, and each value given
230
+ by the subgraph hashes in order of depth from the key node.
231
+
232
+ Examples
233
+ --------
234
+ Finding similar nodes in different graphs:
235
+
236
+ >>> G1 = nx.Graph()
237
+ >>> G1.add_edges_from([
238
+ ... (1, 2), (2, 3), (2, 4), (3, 5), (4, 6), (5, 7), (6, 7)
239
+ ... ])
240
+ >>> G2 = nx.Graph()
241
+ >>> G2.add_edges_from([
242
+ ... (1, 3), (2, 3), (1, 6), (1, 5), (4, 6)
243
+ ... ])
244
+ >>> g1_hashes = nx.weisfeiler_lehman_subgraph_hashes(G1, iterations=3, digest_size=8)
245
+ >>> g2_hashes = nx.weisfeiler_lehman_subgraph_hashes(G2, iterations=3, digest_size=8)
246
+
247
+ Even though G1 and G2 are not isomorphic (they have different numbers of edges),
248
+ the hash sequence of depth 3 for node 1 in G1 and node 5 in G2 are similar:
249
+
250
+ >>> g1_hashes[1]
251
+ ['a93b64973cfc8897', 'db1b43ae35a1878f', '57872a7d2059c1c0']
252
+ >>> g2_hashes[5]
253
+ ['a93b64973cfc8897', 'db1b43ae35a1878f', '1716d2a4012fa4bc']
254
+
255
+ The first 2 WL subgraph hashes match. From this we can conclude that it's very
256
+ likely the neighborhood of 4 hops around these nodes are isomorphic: each
257
+ iteration aggregates 1-hop neighbourhoods meaning hashes at depth $n$ are influenced
258
+ by every node within $2n$ hops.
259
+
260
+ However the neighborhood of 6 hops is no longer isomorphic since their 3rd hash does
261
+ not match.
262
+
263
+ These nodes may be candidates to be classified together since their local topology
264
+ is similar.
265
+
266
+ Notes
267
+ -----
268
+ To hash the full graph when subgraph hashes are not needed, use
269
+ `weisfeiler_lehman_graph_hash` for efficiency.
270
+
271
+ Similarity between hashes does not imply similarity between graphs.
272
+
273
+ References
274
+ ----------
275
+ .. [1] Shervashidze, Nino, Pascal Schweitzer, Erik Jan Van Leeuwen,
276
+ Kurt Mehlhorn, and Karsten M. Borgwardt. Weisfeiler Lehman
277
+ Graph Kernels. Journal of Machine Learning Research. 2011.
278
+ http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf
279
+ .. [2] Annamalai Narayanan, Mahinthan Chandramohan, Rajasekar Venkatesan,
280
+ Lihui Chen, Yang Liu and Shantanu Jaiswa. graph2vec: Learning
281
+ Distributed Representations of Graphs. arXiv. 2017
282
+ https://arxiv.org/pdf/1707.05005.pdf
283
+
284
+ See also
285
+ --------
286
+ weisfeiler_lehman_graph_hash
287
+ """
288
+
289
+ def weisfeiler_lehman_step(G, labels, node_subgraph_hashes, edge_attr=None):
290
+ """
291
+ Apply neighborhood aggregation to each node
292
+ in the graph.
293
+ Computes a dictionary with labels for each node.
294
+ Appends the new hashed label to the dictionary of subgraph hashes
295
+ originating from and indexed by each node in G
296
+ """
297
+ new_labels = {}
298
+ for node in G.nodes():
299
+ label = _neighborhood_aggregate(G, node, labels, edge_attr=edge_attr)
300
+ hashed_label = _hash_label(label, digest_size)
301
+ new_labels[node] = hashed_label
302
+ node_subgraph_hashes[node].append(hashed_label)
303
+ return new_labels
304
+
305
+ node_labels = _init_node_labels(G, edge_attr, node_attr)
306
+
307
+ node_subgraph_hashes = defaultdict(list)
308
+ for _ in range(iterations):
309
+ node_labels = weisfeiler_lehman_step(
310
+ G, node_labels, node_subgraph_hashes, edge_attr
311
+ )
312
+
313
+ return dict(node_subgraph_hashes)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/graphical.py ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test sequences for graphiness.
2
+ """
3
+ import heapq
4
+
5
+ import networkx as nx
6
+
7
+ __all__ = [
8
+ "is_graphical",
9
+ "is_multigraphical",
10
+ "is_pseudographical",
11
+ "is_digraphical",
12
+ "is_valid_degree_sequence_erdos_gallai",
13
+ "is_valid_degree_sequence_havel_hakimi",
14
+ ]
15
+
16
+
17
+ @nx._dispatch(graphs=None)
18
+ def is_graphical(sequence, method="eg"):
19
+ """Returns True if sequence is a valid degree sequence.
20
+
21
+ A degree sequence is valid if some graph can realize it.
22
+
23
+ Parameters
24
+ ----------
25
+ sequence : list or iterable container
26
+ A sequence of integer node degrees
27
+
28
+ method : "eg" | "hh" (default: 'eg')
29
+ The method used to validate the degree sequence.
30
+ "eg" corresponds to the Erdős-Gallai algorithm
31
+ [EG1960]_, [choudum1986]_, and
32
+ "hh" to the Havel-Hakimi algorithm
33
+ [havel1955]_, [hakimi1962]_, [CL1996]_.
34
+
35
+ Returns
36
+ -------
37
+ valid : bool
38
+ True if the sequence is a valid degree sequence and False if not.
39
+
40
+ Examples
41
+ --------
42
+ >>> G = nx.path_graph(4)
43
+ >>> sequence = (d for n, d in G.degree())
44
+ >>> nx.is_graphical(sequence)
45
+ True
46
+
47
+ To test a non-graphical sequence:
48
+ >>> sequence_list = [d for n, d in G.degree()]
49
+ >>> sequence_list[-1] += 1
50
+ >>> nx.is_graphical(sequence_list)
51
+ False
52
+
53
+ References
54
+ ----------
55
+ .. [EG1960] Erdős and Gallai, Mat. Lapok 11 264, 1960.
56
+ .. [choudum1986] S.A. Choudum. "A simple proof of the Erdős-Gallai theorem on
57
+ graph sequences." Bulletin of the Australian Mathematical Society, 33,
58
+ pp 67-70, 1986. https://doi.org/10.1017/S0004972700002872
59
+ .. [havel1955] Havel, V. "A Remark on the Existence of Finite Graphs"
60
+ Casopis Pest. Mat. 80, 477-480, 1955.
61
+ .. [hakimi1962] Hakimi, S. "On the Realizability of a Set of Integers as
62
+ Degrees of the Vertices of a Graph." SIAM J. Appl. Math. 10, 496-506, 1962.
63
+ .. [CL1996] G. Chartrand and L. Lesniak, "Graphs and Digraphs",
64
+ Chapman and Hall/CRC, 1996.
65
+ """
66
+ if method == "eg":
67
+ valid = is_valid_degree_sequence_erdos_gallai(list(sequence))
68
+ elif method == "hh":
69
+ valid = is_valid_degree_sequence_havel_hakimi(list(sequence))
70
+ else:
71
+ msg = "`method` must be 'eg' or 'hh'"
72
+ raise nx.NetworkXException(msg)
73
+ return valid
74
+
75
+
76
+ def _basic_graphical_tests(deg_sequence):
77
+ # Sort and perform some simple tests on the sequence
78
+ deg_sequence = nx.utils.make_list_of_ints(deg_sequence)
79
+ p = len(deg_sequence)
80
+ num_degs = [0] * p
81
+ dmax, dmin, dsum, n = 0, p, 0, 0
82
+ for d in deg_sequence:
83
+ # Reject if degree is negative or larger than the sequence length
84
+ if d < 0 or d >= p:
85
+ raise nx.NetworkXUnfeasible
86
+ # Process only the non-zero integers
87
+ elif d > 0:
88
+ dmax, dmin, dsum, n = max(dmax, d), min(dmin, d), dsum + d, n + 1
89
+ num_degs[d] += 1
90
+ # Reject sequence if it has odd sum or is oversaturated
91
+ if dsum % 2 or dsum > n * (n - 1):
92
+ raise nx.NetworkXUnfeasible
93
+ return dmax, dmin, dsum, n, num_degs
94
+
95
+
96
+ @nx._dispatch(graphs=None)
97
+ def is_valid_degree_sequence_havel_hakimi(deg_sequence):
98
+ r"""Returns True if deg_sequence can be realized by a simple graph.
99
+
100
+ The validation proceeds using the Havel-Hakimi theorem
101
+ [havel1955]_, [hakimi1962]_, [CL1996]_.
102
+ Worst-case run time is $O(s)$ where $s$ is the sum of the sequence.
103
+
104
+ Parameters
105
+ ----------
106
+ deg_sequence : list
107
+ A list of integers where each element specifies the degree of a node
108
+ in a graph.
109
+
110
+ Returns
111
+ -------
112
+ valid : bool
113
+ True if deg_sequence is graphical and False if not.
114
+
115
+ Examples
116
+ --------
117
+ >>> G = nx.Graph([(1, 2), (1, 3), (2, 3), (3, 4), (4, 2), (5, 1), (5, 4)])
118
+ >>> sequence = (d for _, d in G.degree())
119
+ >>> nx.is_valid_degree_sequence_havel_hakimi(sequence)
120
+ True
121
+
122
+ To test a non-valid sequence:
123
+ >>> sequence_list = [d for _, d in G.degree()]
124
+ >>> sequence_list[-1] += 1
125
+ >>> nx.is_valid_degree_sequence_havel_hakimi(sequence_list)
126
+ False
127
+
128
+ Notes
129
+ -----
130
+ The ZZ condition says that for the sequence d if
131
+
132
+ .. math::
133
+ |d| >= \frac{(\max(d) + \min(d) + 1)^2}{4*\min(d)}
134
+
135
+ then d is graphical. This was shown in Theorem 6 in [1]_.
136
+
137
+ References
138
+ ----------
139
+ .. [1] I.E. Zverovich and V.E. Zverovich. "Contributions to the theory
140
+ of graphic sequences", Discrete Mathematics, 105, pp. 292-303 (1992).
141
+ .. [havel1955] Havel, V. "A Remark on the Existence of Finite Graphs"
142
+ Casopis Pest. Mat. 80, 477-480, 1955.
143
+ .. [hakimi1962] Hakimi, S. "On the Realizability of a Set of Integers as
144
+ Degrees of the Vertices of a Graph." SIAM J. Appl. Math. 10, 496-506, 1962.
145
+ .. [CL1996] G. Chartrand and L. Lesniak, "Graphs and Digraphs",
146
+ Chapman and Hall/CRC, 1996.
147
+ """
148
+ try:
149
+ dmax, dmin, dsum, n, num_degs = _basic_graphical_tests(deg_sequence)
150
+ except nx.NetworkXUnfeasible:
151
+ return False
152
+ # Accept if sequence has no non-zero degrees or passes the ZZ condition
153
+ if n == 0 or 4 * dmin * n >= (dmax + dmin + 1) * (dmax + dmin + 1):
154
+ return True
155
+
156
+ modstubs = [0] * (dmax + 1)
157
+ # Successively reduce degree sequence by removing the maximum degree
158
+ while n > 0:
159
+ # Retrieve the maximum degree in the sequence
160
+ while num_degs[dmax] == 0:
161
+ dmax -= 1
162
+ # If there are not enough stubs to connect to, then the sequence is
163
+ # not graphical
164
+ if dmax > n - 1:
165
+ return False
166
+
167
+ # Remove largest stub in list
168
+ num_degs[dmax], n = num_degs[dmax] - 1, n - 1
169
+ # Reduce the next dmax largest stubs
170
+ mslen = 0
171
+ k = dmax
172
+ for i in range(dmax):
173
+ while num_degs[k] == 0:
174
+ k -= 1
175
+ num_degs[k], n = num_degs[k] - 1, n - 1
176
+ if k > 1:
177
+ modstubs[mslen] = k - 1
178
+ mslen += 1
179
+ # Add back to the list any non-zero stubs that were removed
180
+ for i in range(mslen):
181
+ stub = modstubs[i]
182
+ num_degs[stub], n = num_degs[stub] + 1, n + 1
183
+ return True
184
+
185
+
186
+ @nx._dispatch(graphs=None)
187
+ def is_valid_degree_sequence_erdos_gallai(deg_sequence):
188
+ r"""Returns True if deg_sequence can be realized by a simple graph.
189
+
190
+ The validation is done using the Erdős-Gallai theorem [EG1960]_.
191
+
192
+ Parameters
193
+ ----------
194
+ deg_sequence : list
195
+ A list of integers
196
+
197
+ Returns
198
+ -------
199
+ valid : bool
200
+ True if deg_sequence is graphical and False if not.
201
+
202
+ Examples
203
+ --------
204
+ >>> G = nx.Graph([(1, 2), (1, 3), (2, 3), (3, 4), (4, 2), (5, 1), (5, 4)])
205
+ >>> sequence = (d for _, d in G.degree())
206
+ >>> nx.is_valid_degree_sequence_erdos_gallai(sequence)
207
+ True
208
+
209
+ To test a non-valid sequence:
210
+ >>> sequence_list = [d for _, d in G.degree()]
211
+ >>> sequence_list[-1] += 1
212
+ >>> nx.is_valid_degree_sequence_erdos_gallai(sequence_list)
213
+ False
214
+
215
+ Notes
216
+ -----
217
+
218
+ This implementation uses an equivalent form of the Erdős-Gallai criterion.
219
+ Worst-case run time is $O(n)$ where $n$ is the length of the sequence.
220
+
221
+ Specifically, a sequence d is graphical if and only if the
222
+ sum of the sequence is even and for all strong indices k in the sequence,
223
+
224
+ .. math::
225
+
226
+ \sum_{i=1}^{k} d_i \leq k(k-1) + \sum_{j=k+1}^{n} \min(d_i,k)
227
+ = k(n-1) - ( k \sum_{j=0}^{k-1} n_j - \sum_{j=0}^{k-1} j n_j )
228
+
229
+ A strong index k is any index where d_k >= k and the value n_j is the
230
+ number of occurrences of j in d. The maximal strong index is called the
231
+ Durfee index.
232
+
233
+ This particular rearrangement comes from the proof of Theorem 3 in [2]_.
234
+
235
+ The ZZ condition says that for the sequence d if
236
+
237
+ .. math::
238
+ |d| >= \frac{(\max(d) + \min(d) + 1)^2}{4*\min(d)}
239
+
240
+ then d is graphical. This was shown in Theorem 6 in [2]_.
241
+
242
+ References
243
+ ----------
244
+ .. [1] A. Tripathi and S. Vijay. "A note on a theorem of Erdős & Gallai",
245
+ Discrete Mathematics, 265, pp. 417-420 (2003).
246
+ .. [2] I.E. Zverovich and V.E. Zverovich. "Contributions to the theory
247
+ of graphic sequences", Discrete Mathematics, 105, pp. 292-303 (1992).
248
+ .. [EG1960] Erdős and Gallai, Mat. Lapok 11 264, 1960.
249
+ """
250
+ try:
251
+ dmax, dmin, dsum, n, num_degs = _basic_graphical_tests(deg_sequence)
252
+ except nx.NetworkXUnfeasible:
253
+ return False
254
+ # Accept if sequence has no non-zero degrees or passes the ZZ condition
255
+ if n == 0 or 4 * dmin * n >= (dmax + dmin + 1) * (dmax + dmin + 1):
256
+ return True
257
+
258
+ # Perform the EG checks using the reformulation of Zverovich and Zverovich
259
+ k, sum_deg, sum_nj, sum_jnj = 0, 0, 0, 0
260
+ for dk in range(dmax, dmin - 1, -1):
261
+ if dk < k + 1: # Check if already past Durfee index
262
+ return True
263
+ if num_degs[dk] > 0:
264
+ run_size = num_degs[dk] # Process a run of identical-valued degrees
265
+ if dk < k + run_size: # Check if end of run is past Durfee index
266
+ run_size = dk - k # Adjust back to Durfee index
267
+ sum_deg += run_size * dk
268
+ for v in range(run_size):
269
+ sum_nj += num_degs[k + v]
270
+ sum_jnj += (k + v) * num_degs[k + v]
271
+ k += run_size
272
+ if sum_deg > k * (n - 1) - k * sum_nj + sum_jnj:
273
+ return False
274
+ return True
275
+
276
+
277
+ @nx._dispatch(graphs=None)
278
+ def is_multigraphical(sequence):
279
+ """Returns True if some multigraph can realize the sequence.
280
+
281
+ Parameters
282
+ ----------
283
+ sequence : list
284
+ A list of integers
285
+
286
+ Returns
287
+ -------
288
+ valid : bool
289
+ True if deg_sequence is a multigraphic degree sequence and False if not.
290
+
291
+ Examples
292
+ --------
293
+ >>> G = nx.MultiGraph([(1, 2), (1, 3), (2, 3), (3, 4), (4, 2), (5, 1), (5, 4)])
294
+ >>> sequence = (d for _, d in G.degree())
295
+ >>> nx.is_multigraphical(sequence)
296
+ True
297
+
298
+ To test a non-multigraphical sequence:
299
+ >>> sequence_list = [d for _, d in G.degree()]
300
+ >>> sequence_list[-1] += 1
301
+ >>> nx.is_multigraphical(sequence_list)
302
+ False
303
+
304
+ Notes
305
+ -----
306
+ The worst-case run time is $O(n)$ where $n$ is the length of the sequence.
307
+
308
+ References
309
+ ----------
310
+ .. [1] S. L. Hakimi. "On the realizability of a set of integers as
311
+ degrees of the vertices of a linear graph", J. SIAM, 10, pp. 496-506
312
+ (1962).
313
+ """
314
+ try:
315
+ deg_sequence = nx.utils.make_list_of_ints(sequence)
316
+ except nx.NetworkXError:
317
+ return False
318
+ dsum, dmax = 0, 0
319
+ for d in deg_sequence:
320
+ if d < 0:
321
+ return False
322
+ dsum, dmax = dsum + d, max(dmax, d)
323
+ if dsum % 2 or dsum < 2 * dmax:
324
+ return False
325
+ return True
326
+
327
+
328
+ @nx._dispatch(graphs=None)
329
+ def is_pseudographical(sequence):
330
+ """Returns True if some pseudograph can realize the sequence.
331
+
332
+ Every nonnegative integer sequence with an even sum is pseudographical
333
+ (see [1]_).
334
+
335
+ Parameters
336
+ ----------
337
+ sequence : list or iterable container
338
+ A sequence of integer node degrees
339
+
340
+ Returns
341
+ -------
342
+ valid : bool
343
+ True if the sequence is a pseudographic degree sequence and False if not.
344
+
345
+ Examples
346
+ --------
347
+ >>> G = nx.Graph([(1, 2), (1, 3), (2, 3), (3, 4), (4, 2), (5, 1), (5, 4)])
348
+ >>> sequence = (d for _, d in G.degree())
349
+ >>> nx.is_pseudographical(sequence)
350
+ True
351
+
352
+ To test a non-pseudographical sequence:
353
+ >>> sequence_list = [d for _, d in G.degree()]
354
+ >>> sequence_list[-1] += 1
355
+ >>> nx.is_pseudographical(sequence_list)
356
+ False
357
+
358
+ Notes
359
+ -----
360
+ The worst-case run time is $O(n)$ where n is the length of the sequence.
361
+
362
+ References
363
+ ----------
364
+ .. [1] F. Boesch and F. Harary. "Line removal algorithms for graphs
365
+ and their degree lists", IEEE Trans. Circuits and Systems, CAS-23(12),
366
+ pp. 778-782 (1976).
367
+ """
368
+ try:
369
+ deg_sequence = nx.utils.make_list_of_ints(sequence)
370
+ except nx.NetworkXError:
371
+ return False
372
+ return sum(deg_sequence) % 2 == 0 and min(deg_sequence) >= 0
373
+
374
+
375
+ @nx._dispatch(graphs=None)
376
+ def is_digraphical(in_sequence, out_sequence):
377
+ r"""Returns True if some directed graph can realize the in- and out-degree
378
+ sequences.
379
+
380
+ Parameters
381
+ ----------
382
+ in_sequence : list or iterable container
383
+ A sequence of integer node in-degrees
384
+
385
+ out_sequence : list or iterable container
386
+ A sequence of integer node out-degrees
387
+
388
+ Returns
389
+ -------
390
+ valid : bool
391
+ True if in and out-sequences are digraphic False if not.
392
+
393
+ Examples
394
+ --------
395
+ >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 3), (3, 4), (4, 2), (5, 1), (5, 4)])
396
+ >>> in_seq = (d for n, d in G.in_degree())
397
+ >>> out_seq = (d for n, d in G.out_degree())
398
+ >>> nx.is_digraphical(in_seq, out_seq)
399
+ True
400
+
401
+ To test a non-digraphical scenario:
402
+ >>> in_seq_list = [d for n, d in G.in_degree()]
403
+ >>> in_seq_list[-1] += 1
404
+ >>> nx.is_digraphical(in_seq_list, out_seq)
405
+ False
406
+
407
+ Notes
408
+ -----
409
+ This algorithm is from Kleitman and Wang [1]_.
410
+ The worst case runtime is $O(s \times \log n)$ where $s$ and $n$ are the
411
+ sum and length of the sequences respectively.
412
+
413
+ References
414
+ ----------
415
+ .. [1] D.J. Kleitman and D.L. Wang
416
+ Algorithms for Constructing Graphs and Digraphs with Given Valences
417
+ and Factors, Discrete Mathematics, 6(1), pp. 79-88 (1973)
418
+ """
419
+ try:
420
+ in_deg_sequence = nx.utils.make_list_of_ints(in_sequence)
421
+ out_deg_sequence = nx.utils.make_list_of_ints(out_sequence)
422
+ except nx.NetworkXError:
423
+ return False
424
+ # Process the sequences and form two heaps to store degree pairs with
425
+ # either zero or non-zero out degrees
426
+ sumin, sumout, nin, nout = 0, 0, len(in_deg_sequence), len(out_deg_sequence)
427
+ maxn = max(nin, nout)
428
+ maxin = 0
429
+ if maxn == 0:
430
+ return True
431
+ stubheap, zeroheap = [], []
432
+ for n in range(maxn):
433
+ in_deg, out_deg = 0, 0
434
+ if n < nout:
435
+ out_deg = out_deg_sequence[n]
436
+ if n < nin:
437
+ in_deg = in_deg_sequence[n]
438
+ if in_deg < 0 or out_deg < 0:
439
+ return False
440
+ sumin, sumout, maxin = sumin + in_deg, sumout + out_deg, max(maxin, in_deg)
441
+ if in_deg > 0:
442
+ stubheap.append((-1 * out_deg, -1 * in_deg))
443
+ elif out_deg > 0:
444
+ zeroheap.append(-1 * out_deg)
445
+ if sumin != sumout:
446
+ return False
447
+ heapq.heapify(stubheap)
448
+ heapq.heapify(zeroheap)
449
+
450
+ modstubs = [(0, 0)] * (maxin + 1)
451
+ # Successively reduce degree sequence by removing the maximum out degree
452
+ while stubheap:
453
+ # Take the first value in the sequence with non-zero in degree
454
+ (freeout, freein) = heapq.heappop(stubheap)
455
+ freein *= -1
456
+ if freein > len(stubheap) + len(zeroheap):
457
+ return False
458
+
459
+ # Attach out stubs to the nodes with the most in stubs
460
+ mslen = 0
461
+ for i in range(freein):
462
+ if zeroheap and (not stubheap or stubheap[0][0] > zeroheap[0]):
463
+ stubout = heapq.heappop(zeroheap)
464
+ stubin = 0
465
+ else:
466
+ (stubout, stubin) = heapq.heappop(stubheap)
467
+ if stubout == 0:
468
+ return False
469
+ # Check if target is now totally connected
470
+ if stubout + 1 < 0 or stubin < 0:
471
+ modstubs[mslen] = (stubout + 1, stubin)
472
+ mslen += 1
473
+
474
+ # Add back the nodes to the heap that still have available stubs
475
+ for i in range(mslen):
476
+ stub = modstubs[i]
477
+ if stub[1] < 0:
478
+ heapq.heappush(stubheap, stub)
479
+ else:
480
+ heapq.heappush(zeroheap, stub[0])
481
+ if freeout < 0:
482
+ heapq.heappush(zeroheap, freeout)
483
+ return True
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/hierarchy.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Flow Hierarchy.
3
+ """
4
+ import networkx as nx
5
+
6
+ __all__ = ["flow_hierarchy"]
7
+
8
+
9
+ @nx._dispatch(edge_attrs="weight")
10
+ def flow_hierarchy(G, weight=None):
11
+ """Returns the flow hierarchy of a directed network.
12
+
13
+ Flow hierarchy is defined as the fraction of edges not participating
14
+ in cycles in a directed graph [1]_.
15
+
16
+ Parameters
17
+ ----------
18
+ G : DiGraph or MultiDiGraph
19
+ A directed graph
20
+
21
+ weight : string, optional (default=None)
22
+ Attribute to use for edge weights. If None the weight defaults to 1.
23
+
24
+ Returns
25
+ -------
26
+ h : float
27
+ Flow hierarchy value
28
+
29
+ Notes
30
+ -----
31
+ The algorithm described in [1]_ computes the flow hierarchy through
32
+ exponentiation of the adjacency matrix. This function implements an
33
+ alternative approach that finds strongly connected components.
34
+ An edge is in a cycle if and only if it is in a strongly connected
35
+ component, which can be found in $O(m)$ time using Tarjan's algorithm.
36
+
37
+ References
38
+ ----------
39
+ .. [1] Luo, J.; Magee, C.L. (2011),
40
+ Detecting evolving patterns of self-organizing networks by flow
41
+ hierarchy measurement, Complexity, Volume 16 Issue 6 53-61.
42
+ DOI: 10.1002/cplx.20368
43
+ http://web.mit.edu/~cmagee/www/documents/28-DetectingEvolvingPatterns_FlowHierarchy.pdf
44
+ """
45
+ if not G.is_directed():
46
+ raise nx.NetworkXError("G must be a digraph in flow_hierarchy")
47
+ scc = nx.strongly_connected_components(G)
48
+ return 1 - sum(G.subgraph(c).size(weight) for c in scc) / G.size(weight)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/isolate.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Functions for identifying isolate (degree zero) nodes.
3
+ """
4
+ import networkx as nx
5
+
6
+ __all__ = ["is_isolate", "isolates", "number_of_isolates"]
7
+
8
+
9
+ @nx._dispatch
10
+ def is_isolate(G, n):
11
+ """Determines whether a node is an isolate.
12
+
13
+ An *isolate* is a node with no neighbors (that is, with degree
14
+ zero). For directed graphs, this means no in-neighbors and no
15
+ out-neighbors.
16
+
17
+ Parameters
18
+ ----------
19
+ G : NetworkX graph
20
+
21
+ n : node
22
+ A node in `G`.
23
+
24
+ Returns
25
+ -------
26
+ is_isolate : bool
27
+ True if and only if `n` has no neighbors.
28
+
29
+ Examples
30
+ --------
31
+ >>> G = nx.Graph()
32
+ >>> G.add_edge(1, 2)
33
+ >>> G.add_node(3)
34
+ >>> nx.is_isolate(G, 2)
35
+ False
36
+ >>> nx.is_isolate(G, 3)
37
+ True
38
+ """
39
+ return G.degree(n) == 0
40
+
41
+
42
+ @nx._dispatch
43
+ def isolates(G):
44
+ """Iterator over isolates in the graph.
45
+
46
+ An *isolate* is a node with no neighbors (that is, with degree
47
+ zero). For directed graphs, this means no in-neighbors and no
48
+ out-neighbors.
49
+
50
+ Parameters
51
+ ----------
52
+ G : NetworkX graph
53
+
54
+ Returns
55
+ -------
56
+ iterator
57
+ An iterator over the isolates of `G`.
58
+
59
+ Examples
60
+ --------
61
+ To get a list of all isolates of a graph, use the :class:`list`
62
+ constructor::
63
+
64
+ >>> G = nx.Graph()
65
+ >>> G.add_edge(1, 2)
66
+ >>> G.add_node(3)
67
+ >>> list(nx.isolates(G))
68
+ [3]
69
+
70
+ To remove all isolates in the graph, first create a list of the
71
+ isolates, then use :meth:`Graph.remove_nodes_from`::
72
+
73
+ >>> G.remove_nodes_from(list(nx.isolates(G)))
74
+ >>> list(G)
75
+ [1, 2]
76
+
77
+ For digraphs, isolates have zero in-degree and zero out_degre::
78
+
79
+ >>> G = nx.DiGraph([(0, 1), (1, 2)])
80
+ >>> G.add_node(3)
81
+ >>> list(nx.isolates(G))
82
+ [3]
83
+
84
+ """
85
+ return (n for n, d in G.degree() if d == 0)
86
+
87
+
88
+ @nx._dispatch
89
+ def number_of_isolates(G):
90
+ """Returns the number of isolates in the graph.
91
+
92
+ An *isolate* is a node with no neighbors (that is, with degree
93
+ zero). For directed graphs, this means no in-neighbors and no
94
+ out-neighbors.
95
+
96
+ Parameters
97
+ ----------
98
+ G : NetworkX graph
99
+
100
+ Returns
101
+ -------
102
+ int
103
+ The number of degree zero nodes in the graph `G`.
104
+
105
+ """
106
+ # TODO This can be parallelized.
107
+ return sum(1 for v in isolates(G))
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/test_pagerank.cpython-311.pyc ADDED
Binary file (14.4 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/test_hits.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+
5
+ np = pytest.importorskip("numpy")
6
+ sp = pytest.importorskip("scipy")
7
+
8
+ from networkx.algorithms.link_analysis.hits_alg import (
9
+ _hits_numpy,
10
+ _hits_python,
11
+ _hits_scipy,
12
+ )
13
+
14
+ # Example from
15
+ # A. Langville and C. Meyer, "A survey of eigenvector methods of web
16
+ # information retrieval." http://citeseer.ist.psu.edu/713792.html
17
+
18
+
19
+ class TestHITS:
20
+ @classmethod
21
+ def setup_class(cls):
22
+ G = nx.DiGraph()
23
+
24
+ edges = [(1, 3), (1, 5), (2, 1), (3, 5), (5, 4), (5, 3), (6, 5)]
25
+
26
+ G.add_edges_from(edges, weight=1)
27
+ cls.G = G
28
+ cls.G.a = dict(
29
+ zip(sorted(G), [0.000000, 0.000000, 0.366025, 0.133975, 0.500000, 0.000000])
30
+ )
31
+ cls.G.h = dict(
32
+ zip(sorted(G), [0.366025, 0.000000, 0.211325, 0.000000, 0.211325, 0.211325])
33
+ )
34
+
35
+ def test_hits_numpy(self):
36
+ G = self.G
37
+ h, a = _hits_numpy(G)
38
+ for n in G:
39
+ assert h[n] == pytest.approx(G.h[n], abs=1e-4)
40
+ for n in G:
41
+ assert a[n] == pytest.approx(G.a[n], abs=1e-4)
42
+
43
+ @pytest.mark.parametrize("hits_alg", (nx.hits, _hits_python, _hits_scipy))
44
+ def test_hits(self, hits_alg):
45
+ G = self.G
46
+ h, a = hits_alg(G, tol=1.0e-08)
47
+ for n in G:
48
+ assert h[n] == pytest.approx(G.h[n], abs=1e-4)
49
+ for n in G:
50
+ assert a[n] == pytest.approx(G.a[n], abs=1e-4)
51
+ nstart = {i: 1.0 / 2 for i in G}
52
+ h, a = hits_alg(G, nstart=nstart)
53
+ for n in G:
54
+ assert h[n] == pytest.approx(G.h[n], abs=1e-4)
55
+ for n in G:
56
+ assert a[n] == pytest.approx(G.a[n], abs=1e-4)
57
+
58
+ def test_empty(self):
59
+ G = nx.Graph()
60
+ assert nx.hits(G) == ({}, {})
61
+ assert _hits_numpy(G) == ({}, {})
62
+ assert _hits_python(G) == ({}, {})
63
+ assert _hits_scipy(G) == ({}, {})
64
+
65
+ def test_hits_not_convergent(self):
66
+ G = nx.path_graph(50)
67
+ with pytest.raises(nx.PowerIterationFailedConvergence):
68
+ _hits_scipy(G, max_iter=1)
69
+ with pytest.raises(nx.PowerIterationFailedConvergence):
70
+ _hits_python(G, max_iter=1)
71
+ with pytest.raises(nx.PowerIterationFailedConvergence):
72
+ _hits_scipy(G, max_iter=0)
73
+ with pytest.raises(nx.PowerIterationFailedConvergence):
74
+ _hits_python(G, max_iter=0)
75
+ with pytest.raises(ValueError):
76
+ nx.hits(G, max_iter=0)
77
+ with pytest.raises(sp.sparse.linalg.ArpackNoConvergence):
78
+ nx.hits(G, max_iter=1)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/lowest_common_ancestors.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Algorithms for finding the lowest common ancestor of trees and DAGs."""
2
+ from collections import defaultdict
3
+ from collections.abc import Mapping, Set
4
+ from itertools import combinations_with_replacement
5
+
6
+ import networkx as nx
7
+ from networkx.utils import UnionFind, arbitrary_element, not_implemented_for
8
+
9
+ __all__ = [
10
+ "all_pairs_lowest_common_ancestor",
11
+ "tree_all_pairs_lowest_common_ancestor",
12
+ "lowest_common_ancestor",
13
+ ]
14
+
15
+
16
+ @not_implemented_for("undirected")
17
+ @nx._dispatch
18
+ def all_pairs_lowest_common_ancestor(G, pairs=None):
19
+ """Return the lowest common ancestor of all pairs or the provided pairs
20
+
21
+ Parameters
22
+ ----------
23
+ G : NetworkX directed graph
24
+
25
+ pairs : iterable of pairs of nodes, optional (default: all pairs)
26
+ The pairs of nodes of interest.
27
+ If None, will find the LCA of all pairs of nodes.
28
+
29
+ Yields
30
+ ------
31
+ ((node1, node2), lca) : 2-tuple
32
+ Where lca is least common ancestor of node1 and node2.
33
+ Note that for the default case, the order of the node pair is not considered,
34
+ e.g. you will not get both ``(a, b)`` and ``(b, a)``
35
+
36
+ Raises
37
+ ------
38
+ NetworkXPointlessConcept
39
+ If `G` is null.
40
+ NetworkXError
41
+ If `G` is not a DAG.
42
+
43
+ Examples
44
+ --------
45
+ The default behavior is to yield the lowest common ancestor for all
46
+ possible combinations of nodes in `G`, including self-pairings:
47
+
48
+ >>> G = nx.DiGraph([(0, 1), (0, 3), (1, 2)])
49
+ >>> dict(nx.all_pairs_lowest_common_ancestor(G))
50
+ {(0, 0): 0, (0, 1): 0, (0, 3): 0, (0, 2): 0, (1, 1): 1, (1, 3): 0, (1, 2): 1, (3, 3): 3, (3, 2): 0, (2, 2): 2}
51
+
52
+ The pairs argument can be used to limit the output to only the
53
+ specified node pairings:
54
+
55
+ >>> dict(nx.all_pairs_lowest_common_ancestor(G, pairs=[(1, 2), (2, 3)]))
56
+ {(1, 2): 1, (2, 3): 0}
57
+
58
+ Notes
59
+ -----
60
+ Only defined on non-null directed acyclic graphs.
61
+
62
+ See Also
63
+ --------
64
+ lowest_common_ancestor
65
+ """
66
+ if not nx.is_directed_acyclic_graph(G):
67
+ raise nx.NetworkXError("LCA only defined on directed acyclic graphs.")
68
+ if len(G) == 0:
69
+ raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
70
+
71
+ if pairs is None:
72
+ pairs = combinations_with_replacement(G, 2)
73
+ else:
74
+ # Convert iterator to iterable, if necessary. Trim duplicates.
75
+ pairs = dict.fromkeys(pairs)
76
+ # Verify that each of the nodes in the provided pairs is in G
77
+ nodeset = set(G)
78
+ for pair in pairs:
79
+ if set(pair) - nodeset:
80
+ raise nx.NodeNotFound(
81
+ f"Node(s) {set(pair) - nodeset} from pair {pair} not in G."
82
+ )
83
+
84
+ # Once input validation is done, construct the generator
85
+ def generate_lca_from_pairs(G, pairs):
86
+ ancestor_cache = {}
87
+
88
+ for v, w in pairs:
89
+ if v not in ancestor_cache:
90
+ ancestor_cache[v] = nx.ancestors(G, v)
91
+ ancestor_cache[v].add(v)
92
+ if w not in ancestor_cache:
93
+ ancestor_cache[w] = nx.ancestors(G, w)
94
+ ancestor_cache[w].add(w)
95
+
96
+ common_ancestors = ancestor_cache[v] & ancestor_cache[w]
97
+
98
+ if common_ancestors:
99
+ common_ancestor = next(iter(common_ancestors))
100
+ while True:
101
+ successor = None
102
+ for lower_ancestor in G.successors(common_ancestor):
103
+ if lower_ancestor in common_ancestors:
104
+ successor = lower_ancestor
105
+ break
106
+ if successor is None:
107
+ break
108
+ common_ancestor = successor
109
+ yield ((v, w), common_ancestor)
110
+
111
+ return generate_lca_from_pairs(G, pairs)
112
+
113
+
114
+ @not_implemented_for("undirected")
115
+ @nx._dispatch
116
+ def lowest_common_ancestor(G, node1, node2, default=None):
117
+ """Compute the lowest common ancestor of the given pair of nodes.
118
+
119
+ Parameters
120
+ ----------
121
+ G : NetworkX directed graph
122
+
123
+ node1, node2 : nodes in the graph.
124
+
125
+ default : object
126
+ Returned if no common ancestor between `node1` and `node2`
127
+
128
+ Returns
129
+ -------
130
+ The lowest common ancestor of node1 and node2,
131
+ or default if they have no common ancestors.
132
+
133
+ Examples
134
+ --------
135
+ >>> G = nx.DiGraph()
136
+ >>> nx.add_path(G, (0, 1, 2, 3))
137
+ >>> nx.add_path(G, (0, 4, 3))
138
+ >>> nx.lowest_common_ancestor(G, 2, 4)
139
+ 0
140
+
141
+ See Also
142
+ --------
143
+ all_pairs_lowest_common_ancestor"""
144
+
145
+ ans = list(all_pairs_lowest_common_ancestor(G, pairs=[(node1, node2)]))
146
+ if ans:
147
+ assert len(ans) == 1
148
+ return ans[0][1]
149
+ return default
150
+
151
+
152
+ @not_implemented_for("undirected")
153
+ @nx._dispatch
154
+ def tree_all_pairs_lowest_common_ancestor(G, root=None, pairs=None):
155
+ r"""Yield the lowest common ancestor for sets of pairs in a tree.
156
+
157
+ Parameters
158
+ ----------
159
+ G : NetworkX directed graph (must be a tree)
160
+
161
+ root : node, optional (default: None)
162
+ The root of the subtree to operate on.
163
+ If None, assume the entire graph has exactly one source and use that.
164
+
165
+ pairs : iterable or iterator of pairs of nodes, optional (default: None)
166
+ The pairs of interest. If None, Defaults to all pairs of nodes
167
+ under `root` that have a lowest common ancestor.
168
+
169
+ Returns
170
+ -------
171
+ lcas : generator of tuples `((u, v), lca)` where `u` and `v` are nodes
172
+ in `pairs` and `lca` is their lowest common ancestor.
173
+
174
+ Examples
175
+ --------
176
+ >>> import pprint
177
+ >>> G = nx.DiGraph([(1, 3), (2, 4), (1, 2)])
178
+ >>> pprint.pprint(dict(nx.tree_all_pairs_lowest_common_ancestor(G)))
179
+ {(1, 1): 1,
180
+ (2, 1): 1,
181
+ (2, 2): 2,
182
+ (3, 1): 1,
183
+ (3, 2): 1,
184
+ (3, 3): 3,
185
+ (3, 4): 1,
186
+ (4, 1): 1,
187
+ (4, 2): 2,
188
+ (4, 4): 4}
189
+
190
+ We can also use `pairs` argument to specify the pairs of nodes for which we
191
+ want to compute lowest common ancestors. Here is an example:
192
+
193
+ >>> dict(nx.tree_all_pairs_lowest_common_ancestor(G, pairs=[(1, 4), (2, 3)]))
194
+ {(2, 3): 1, (1, 4): 1}
195
+
196
+ Notes
197
+ -----
198
+ Only defined on non-null trees represented with directed edges from
199
+ parents to children. Uses Tarjan's off-line lowest-common-ancestors
200
+ algorithm. Runs in time $O(4 \times (V + E + P))$ time, where 4 is the largest
201
+ value of the inverse Ackermann function likely to ever come up in actual
202
+ use, and $P$ is the number of pairs requested (or $V^2$ if all are needed).
203
+
204
+ Tarjan, R. E. (1979), "Applications of path compression on balanced trees",
205
+ Journal of the ACM 26 (4): 690-715, doi:10.1145/322154.322161.
206
+
207
+ See Also
208
+ --------
209
+ all_pairs_lowest_common_ancestor: similar routine for general DAGs
210
+ lowest_common_ancestor: just a single pair for general DAGs
211
+ """
212
+ if len(G) == 0:
213
+ raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
214
+
215
+ # Index pairs of interest for efficient lookup from either side.
216
+ if pairs is not None:
217
+ pair_dict = defaultdict(set)
218
+ # See note on all_pairs_lowest_common_ancestor.
219
+ if not isinstance(pairs, (Mapping, Set)):
220
+ pairs = set(pairs)
221
+ for u, v in pairs:
222
+ for n in (u, v):
223
+ if n not in G:
224
+ msg = f"The node {str(n)} is not in the digraph."
225
+ raise nx.NodeNotFound(msg)
226
+ pair_dict[u].add(v)
227
+ pair_dict[v].add(u)
228
+
229
+ # If root is not specified, find the exactly one node with in degree 0 and
230
+ # use it. Raise an error if none are found, or more than one is. Also check
231
+ # for any nodes with in degree larger than 1, which would imply G is not a
232
+ # tree.
233
+ if root is None:
234
+ for n, deg in G.in_degree:
235
+ if deg == 0:
236
+ if root is not None:
237
+ msg = "No root specified and tree has multiple sources."
238
+ raise nx.NetworkXError(msg)
239
+ root = n
240
+ # checking deg>1 is not sufficient for MultiDiGraphs
241
+ elif deg > 1 and len(G.pred[n]) > 1:
242
+ msg = "Tree LCA only defined on trees; use DAG routine."
243
+ raise nx.NetworkXError(msg)
244
+ if root is None:
245
+ raise nx.NetworkXError("Graph contains a cycle.")
246
+
247
+ # Iterative implementation of Tarjan's offline lca algorithm
248
+ # as described in CLRS on page 521 (2nd edition)/page 584 (3rd edition)
249
+ uf = UnionFind()
250
+ ancestors = {}
251
+ for node in G:
252
+ ancestors[node] = uf[node]
253
+
254
+ colors = defaultdict(bool)
255
+ for node in nx.dfs_postorder_nodes(G, root):
256
+ colors[node] = True
257
+ for v in pair_dict[node] if pairs is not None else G:
258
+ if colors[v]:
259
+ # If the user requested both directions of a pair, give it.
260
+ # Otherwise, just give one.
261
+ if pairs is not None and (node, v) in pairs:
262
+ yield (node, v), ancestors[uf[v]]
263
+ if pairs is None or (v, node) in pairs:
264
+ yield (v, node), ancestors[uf[v]]
265
+ if node != root:
266
+ parent = arbitrary_element(G.pred[node])
267
+ uf.union(parent, node)
268
+ ancestors[uf[parent]] = parent
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/__pycache__/contraction.cpython-311.pyc ADDED
Binary file (25 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/tests/test_contraction.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.minors.contraction` module."""
2
+ import pytest
3
+
4
+ import networkx as nx
5
+ from networkx.utils import arbitrary_element, edges_equal, nodes_equal
6
+
7
+
8
+ def test_quotient_graph_complete_multipartite():
9
+ """Tests that the quotient graph of the complete *n*-partite graph
10
+ under the "same neighbors" node relation is the complete graph on *n*
11
+ nodes.
12
+
13
+ """
14
+ G = nx.complete_multipartite_graph(2, 3, 4)
15
+ # Two nodes are equivalent if they are not adjacent but have the same
16
+ # neighbor set.
17
+
18
+ def same_neighbors(u, v):
19
+ return u not in G[v] and v not in G[u] and G[u] == G[v]
20
+
21
+ expected = nx.complete_graph(3)
22
+ actual = nx.quotient_graph(G, same_neighbors)
23
+ # It won't take too long to run a graph isomorphism algorithm on such
24
+ # small graphs.
25
+ assert nx.is_isomorphic(expected, actual)
26
+
27
+
28
+ def test_quotient_graph_complete_bipartite():
29
+ """Tests that the quotient graph of the complete bipartite graph under
30
+ the "same neighbors" node relation is `K_2`.
31
+
32
+ """
33
+ G = nx.complete_bipartite_graph(2, 3)
34
+ # Two nodes are equivalent if they are not adjacent but have the same
35
+ # neighbor set.
36
+
37
+ def same_neighbors(u, v):
38
+ return u not in G[v] and v not in G[u] and G[u] == G[v]
39
+
40
+ expected = nx.complete_graph(2)
41
+ actual = nx.quotient_graph(G, same_neighbors)
42
+ # It won't take too long to run a graph isomorphism algorithm on such
43
+ # small graphs.
44
+ assert nx.is_isomorphic(expected, actual)
45
+
46
+
47
+ def test_quotient_graph_edge_relation():
48
+ """Tests for specifying an alternate edge relation for the quotient
49
+ graph.
50
+
51
+ """
52
+ G = nx.path_graph(5)
53
+
54
+ def identity(u, v):
55
+ return u == v
56
+
57
+ def same_parity(b, c):
58
+ return arbitrary_element(b) % 2 == arbitrary_element(c) % 2
59
+
60
+ actual = nx.quotient_graph(G, identity, same_parity)
61
+ expected = nx.Graph()
62
+ expected.add_edges_from([(0, 2), (0, 4), (2, 4)])
63
+ expected.add_edge(1, 3)
64
+ assert nx.is_isomorphic(actual, expected)
65
+
66
+
67
+ def test_condensation_as_quotient():
68
+ """This tests that the condensation of a graph can be viewed as the
69
+ quotient graph under the "in the same connected component" equivalence
70
+ relation.
71
+
72
+ """
73
+ # This example graph comes from the file `test_strongly_connected.py`.
74
+ G = nx.DiGraph()
75
+ G.add_edges_from(
76
+ [
77
+ (1, 2),
78
+ (2, 3),
79
+ (2, 11),
80
+ (2, 12),
81
+ (3, 4),
82
+ (4, 3),
83
+ (4, 5),
84
+ (5, 6),
85
+ (6, 5),
86
+ (6, 7),
87
+ (7, 8),
88
+ (7, 9),
89
+ (7, 10),
90
+ (8, 9),
91
+ (9, 7),
92
+ (10, 6),
93
+ (11, 2),
94
+ (11, 4),
95
+ (11, 6),
96
+ (12, 6),
97
+ (12, 11),
98
+ ]
99
+ )
100
+ scc = list(nx.strongly_connected_components(G))
101
+ C = nx.condensation(G, scc)
102
+ component_of = C.graph["mapping"]
103
+ # Two nodes are equivalent if they are in the same connected component.
104
+
105
+ def same_component(u, v):
106
+ return component_of[u] == component_of[v]
107
+
108
+ Q = nx.quotient_graph(G, same_component)
109
+ assert nx.is_isomorphic(C, Q)
110
+
111
+
112
+ def test_path():
113
+ G = nx.path_graph(6)
114
+ partition = [{0, 1}, {2, 3}, {4, 5}]
115
+ M = nx.quotient_graph(G, partition, relabel=True)
116
+ assert nodes_equal(M, [0, 1, 2])
117
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
118
+ for n in M:
119
+ assert M.nodes[n]["nedges"] == 1
120
+ assert M.nodes[n]["nnodes"] == 2
121
+ assert M.nodes[n]["density"] == 1
122
+
123
+
124
+ def test_path__partition_provided_as_dict_of_lists():
125
+ G = nx.path_graph(6)
126
+ partition = {0: [0, 1], 2: [2, 3], 4: [4, 5]}
127
+ M = nx.quotient_graph(G, partition, relabel=True)
128
+ assert nodes_equal(M, [0, 1, 2])
129
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
130
+ for n in M:
131
+ assert M.nodes[n]["nedges"] == 1
132
+ assert M.nodes[n]["nnodes"] == 2
133
+ assert M.nodes[n]["density"] == 1
134
+
135
+
136
+ def test_path__partition_provided_as_dict_of_tuples():
137
+ G = nx.path_graph(6)
138
+ partition = {0: (0, 1), 2: (2, 3), 4: (4, 5)}
139
+ M = nx.quotient_graph(G, partition, relabel=True)
140
+ assert nodes_equal(M, [0, 1, 2])
141
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
142
+ for n in M:
143
+ assert M.nodes[n]["nedges"] == 1
144
+ assert M.nodes[n]["nnodes"] == 2
145
+ assert M.nodes[n]["density"] == 1
146
+
147
+
148
+ def test_path__partition_provided_as_dict_of_sets():
149
+ G = nx.path_graph(6)
150
+ partition = {0: {0, 1}, 2: {2, 3}, 4: {4, 5}}
151
+ M = nx.quotient_graph(G, partition, relabel=True)
152
+ assert nodes_equal(M, [0, 1, 2])
153
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
154
+ for n in M:
155
+ assert M.nodes[n]["nedges"] == 1
156
+ assert M.nodes[n]["nnodes"] == 2
157
+ assert M.nodes[n]["density"] == 1
158
+
159
+
160
+ def test_multigraph_path():
161
+ G = nx.MultiGraph(nx.path_graph(6))
162
+ partition = [{0, 1}, {2, 3}, {4, 5}]
163
+ M = nx.quotient_graph(G, partition, relabel=True)
164
+ assert nodes_equal(M, [0, 1, 2])
165
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
166
+ for n in M:
167
+ assert M.nodes[n]["nedges"] == 1
168
+ assert M.nodes[n]["nnodes"] == 2
169
+ assert M.nodes[n]["density"] == 1
170
+
171
+
172
+ def test_directed_path():
173
+ G = nx.DiGraph()
174
+ nx.add_path(G, range(6))
175
+ partition = [{0, 1}, {2, 3}, {4, 5}]
176
+ M = nx.quotient_graph(G, partition, relabel=True)
177
+ assert nodes_equal(M, [0, 1, 2])
178
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
179
+ for n in M:
180
+ assert M.nodes[n]["nedges"] == 1
181
+ assert M.nodes[n]["nnodes"] == 2
182
+ assert M.nodes[n]["density"] == 0.5
183
+
184
+
185
+ def test_directed_multigraph_path():
186
+ G = nx.MultiDiGraph()
187
+ nx.add_path(G, range(6))
188
+ partition = [{0, 1}, {2, 3}, {4, 5}]
189
+ M = nx.quotient_graph(G, partition, relabel=True)
190
+ assert nodes_equal(M, [0, 1, 2])
191
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
192
+ for n in M:
193
+ assert M.nodes[n]["nedges"] == 1
194
+ assert M.nodes[n]["nnodes"] == 2
195
+ assert M.nodes[n]["density"] == 0.5
196
+
197
+
198
+ def test_overlapping_blocks():
199
+ with pytest.raises(nx.NetworkXException):
200
+ G = nx.path_graph(6)
201
+ partition = [{0, 1, 2}, {2, 3}, {4, 5}]
202
+ nx.quotient_graph(G, partition)
203
+
204
+
205
+ def test_weighted_path():
206
+ G = nx.path_graph(6)
207
+ for i in range(5):
208
+ G[i][i + 1]["w"] = i + 1
209
+ partition = [{0, 1}, {2, 3}, {4, 5}]
210
+ M = nx.quotient_graph(G, partition, weight="w", relabel=True)
211
+ assert nodes_equal(M, [0, 1, 2])
212
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
213
+ assert M[0][1]["weight"] == 2
214
+ assert M[1][2]["weight"] == 4
215
+ for n in M:
216
+ assert M.nodes[n]["nedges"] == 1
217
+ assert M.nodes[n]["nnodes"] == 2
218
+ assert M.nodes[n]["density"] == 1
219
+
220
+
221
+ def test_barbell():
222
+ G = nx.barbell_graph(3, 0)
223
+ partition = [{0, 1, 2}, {3, 4, 5}]
224
+ M = nx.quotient_graph(G, partition, relabel=True)
225
+ assert nodes_equal(M, [0, 1])
226
+ assert edges_equal(M.edges(), [(0, 1)])
227
+ for n in M:
228
+ assert M.nodes[n]["nedges"] == 3
229
+ assert M.nodes[n]["nnodes"] == 3
230
+ assert M.nodes[n]["density"] == 1
231
+
232
+
233
+ def test_barbell_plus():
234
+ G = nx.barbell_graph(3, 0)
235
+ # Add an extra edge joining the bells.
236
+ G.add_edge(0, 5)
237
+ partition = [{0, 1, 2}, {3, 4, 5}]
238
+ M = nx.quotient_graph(G, partition, relabel=True)
239
+ assert nodes_equal(M, [0, 1])
240
+ assert edges_equal(M.edges(), [(0, 1)])
241
+ assert M[0][1]["weight"] == 2
242
+ for n in M:
243
+ assert M.nodes[n]["nedges"] == 3
244
+ assert M.nodes[n]["nnodes"] == 3
245
+ assert M.nodes[n]["density"] == 1
246
+
247
+
248
+ def test_blockmodel():
249
+ G = nx.path_graph(6)
250
+ partition = [[0, 1], [2, 3], [4, 5]]
251
+ M = nx.quotient_graph(G, partition, relabel=True)
252
+ assert nodes_equal(M.nodes(), [0, 1, 2])
253
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
254
+ for n in M.nodes():
255
+ assert M.nodes[n]["nedges"] == 1
256
+ assert M.nodes[n]["nnodes"] == 2
257
+ assert M.nodes[n]["density"] == 1.0
258
+
259
+
260
+ def test_multigraph_blockmodel():
261
+ G = nx.MultiGraph(nx.path_graph(6))
262
+ partition = [[0, 1], [2, 3], [4, 5]]
263
+ M = nx.quotient_graph(G, partition, create_using=nx.MultiGraph(), relabel=True)
264
+ assert nodes_equal(M.nodes(), [0, 1, 2])
265
+ assert edges_equal(M.edges(), [(0, 1), (1, 2)])
266
+ for n in M.nodes():
267
+ assert M.nodes[n]["nedges"] == 1
268
+ assert M.nodes[n]["nnodes"] == 2
269
+ assert M.nodes[n]["density"] == 1.0
270
+
271
+
272
+ def test_quotient_graph_incomplete_partition():
273
+ G = nx.path_graph(6)
274
+ partition = []
275
+ H = nx.quotient_graph(G, partition, relabel=True)
276
+ assert nodes_equal(H.nodes(), [])
277
+ assert edges_equal(H.edges(), [])
278
+
279
+ partition = [[0, 1], [2, 3], [5]]
280
+ H = nx.quotient_graph(G, partition, relabel=True)
281
+ assert nodes_equal(H.nodes(), [0, 1, 2])
282
+ assert edges_equal(H.edges(), [(0, 1)])
283
+
284
+
285
+ def test_undirected_node_contraction():
286
+ """Tests for node contraction in an undirected graph."""
287
+ G = nx.cycle_graph(4)
288
+ actual = nx.contracted_nodes(G, 0, 1)
289
+ expected = nx.cycle_graph(3)
290
+ expected.add_edge(0, 0)
291
+ assert nx.is_isomorphic(actual, expected)
292
+
293
+
294
+ def test_directed_node_contraction():
295
+ """Tests for node contraction in a directed graph."""
296
+ G = nx.DiGraph(nx.cycle_graph(4))
297
+ actual = nx.contracted_nodes(G, 0, 1)
298
+ expected = nx.DiGraph(nx.cycle_graph(3))
299
+ expected.add_edge(0, 0)
300
+ expected.add_edge(0, 0)
301
+ assert nx.is_isomorphic(actual, expected)
302
+
303
+
304
+ def test_undirected_node_contraction_no_copy():
305
+ """Tests for node contraction in an undirected graph
306
+ by making changes in place."""
307
+ G = nx.cycle_graph(4)
308
+ actual = nx.contracted_nodes(G, 0, 1, copy=False)
309
+ expected = nx.cycle_graph(3)
310
+ expected.add_edge(0, 0)
311
+ assert nx.is_isomorphic(actual, G)
312
+ assert nx.is_isomorphic(actual, expected)
313
+
314
+
315
+ def test_directed_node_contraction_no_copy():
316
+ """Tests for node contraction in a directed graph
317
+ by making changes in place."""
318
+ G = nx.DiGraph(nx.cycle_graph(4))
319
+ actual = nx.contracted_nodes(G, 0, 1, copy=False)
320
+ expected = nx.DiGraph(nx.cycle_graph(3))
321
+ expected.add_edge(0, 0)
322
+ expected.add_edge(0, 0)
323
+ assert nx.is_isomorphic(actual, G)
324
+ assert nx.is_isomorphic(actual, expected)
325
+
326
+
327
+ def test_create_multigraph():
328
+ """Tests that using a MultiGraph creates multiple edges."""
329
+ G = nx.path_graph(3, create_using=nx.MultiGraph())
330
+ G.add_edge(0, 1)
331
+ G.add_edge(0, 0)
332
+ G.add_edge(0, 2)
333
+ actual = nx.contracted_nodes(G, 0, 2)
334
+ expected = nx.MultiGraph()
335
+ expected.add_edge(0, 1)
336
+ expected.add_edge(0, 1)
337
+ expected.add_edge(0, 1)
338
+ expected.add_edge(0, 0)
339
+ expected.add_edge(0, 0)
340
+ assert edges_equal(actual.edges, expected.edges)
341
+
342
+
343
+ def test_multigraph_keys():
344
+ """Tests that multiedge keys are reset in new graph."""
345
+ G = nx.path_graph(3, create_using=nx.MultiGraph())
346
+ G.add_edge(0, 1, 5)
347
+ G.add_edge(0, 0, 0)
348
+ G.add_edge(0, 2, 5)
349
+ actual = nx.contracted_nodes(G, 0, 2)
350
+ expected = nx.MultiGraph()
351
+ expected.add_edge(0, 1, 0)
352
+ expected.add_edge(0, 1, 5)
353
+ expected.add_edge(0, 1, 2) # keyed as 2 b/c 2 edges already in G
354
+ expected.add_edge(0, 0, 0)
355
+ expected.add_edge(0, 0, 1) # this comes from (0, 2, 5)
356
+ assert edges_equal(actual.edges, expected.edges)
357
+
358
+
359
+ def test_node_attributes():
360
+ """Tests that node contraction preserves node attributes."""
361
+ G = nx.cycle_graph(4)
362
+ # Add some data to the two nodes being contracted.
363
+ G.nodes[0]["foo"] = "bar"
364
+ G.nodes[1]["baz"] = "xyzzy"
365
+ actual = nx.contracted_nodes(G, 0, 1)
366
+ # We expect that contracting the nodes 0 and 1 in C_4 yields K_3, but
367
+ # with nodes labeled 0, 2, and 3, and with a -loop on 0.
368
+ expected = nx.complete_graph(3)
369
+ expected = nx.relabel_nodes(expected, {1: 2, 2: 3})
370
+ expected.add_edge(0, 0)
371
+ cdict = {1: {"baz": "xyzzy"}}
372
+ expected.nodes[0].update({"foo": "bar", "contraction": cdict})
373
+ assert nx.is_isomorphic(actual, expected)
374
+ assert actual.nodes == expected.nodes
375
+
376
+
377
+ def test_edge_attributes():
378
+ """Tests that node contraction preserves edge attributes."""
379
+ # Shape: src1 --> dest <-- src2
380
+ G = nx.DiGraph([("src1", "dest"), ("src2", "dest")])
381
+ G["src1"]["dest"]["value"] = "src1-->dest"
382
+ G["src2"]["dest"]["value"] = "src2-->dest"
383
+ H = nx.MultiDiGraph(G)
384
+
385
+ G = nx.contracted_nodes(G, "src1", "src2") # New Shape: src1 --> dest
386
+ assert G.edges[("src1", "dest")]["value"] == "src1-->dest"
387
+ assert (
388
+ G.edges[("src1", "dest")]["contraction"][("src2", "dest")]["value"]
389
+ == "src2-->dest"
390
+ )
391
+
392
+ H = nx.contracted_nodes(H, "src1", "src2") # New Shape: src1 -(x2)-> dest
393
+ assert len(H.edges(("src1", "dest"))) == 2
394
+
395
+
396
+ def test_without_self_loops():
397
+ """Tests for node contraction without preserving -loops."""
398
+ G = nx.cycle_graph(4)
399
+ actual = nx.contracted_nodes(G, 0, 1, self_loops=False)
400
+ expected = nx.complete_graph(3)
401
+ assert nx.is_isomorphic(actual, expected)
402
+
403
+
404
+ def test_contract_loop_graph():
405
+ """Tests for node contraction when nodes have loops."""
406
+ G = nx.cycle_graph(4)
407
+ G.add_edge(0, 0)
408
+ actual = nx.contracted_nodes(G, 0, 1)
409
+ expected = nx.complete_graph([0, 2, 3])
410
+ expected.add_edge(0, 0)
411
+ expected.add_edge(0, 0)
412
+ assert edges_equal(actual.edges, expected.edges)
413
+ actual = nx.contracted_nodes(G, 1, 0)
414
+ expected = nx.complete_graph([1, 2, 3])
415
+ expected.add_edge(1, 1)
416
+ expected.add_edge(1, 1)
417
+ assert edges_equal(actual.edges, expected.edges)
418
+
419
+
420
+ def test_undirected_edge_contraction():
421
+ """Tests for edge contraction in an undirected graph."""
422
+ G = nx.cycle_graph(4)
423
+ actual = nx.contracted_edge(G, (0, 1))
424
+ expected = nx.complete_graph(3)
425
+ expected.add_edge(0, 0)
426
+ assert nx.is_isomorphic(actual, expected)
427
+
428
+
429
+ def test_multigraph_edge_contraction():
430
+ """Tests for edge contraction in a multigraph"""
431
+ G = nx.cycle_graph(4)
432
+ actual = nx.contracted_edge(G, (0, 1, 0))
433
+ expected = nx.complete_graph(3)
434
+ expected.add_edge(0, 0)
435
+ assert nx.is_isomorphic(actual, expected)
436
+
437
+
438
+ def test_nonexistent_edge():
439
+ """Tests that attempting to contract a nonexistent edge raises an
440
+ exception.
441
+
442
+ """
443
+ with pytest.raises(ValueError):
444
+ G = nx.cycle_graph(4)
445
+ nx.contracted_edge(G, (0, 2))
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/mis.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Algorithm to find a maximal (not maximum) independent set.
3
+
4
+ """
5
+ import networkx as nx
6
+ from networkx.utils import not_implemented_for, py_random_state
7
+
8
+ __all__ = ["maximal_independent_set"]
9
+
10
+
11
+ @not_implemented_for("directed")
12
+ @py_random_state(2)
13
+ @nx._dispatch
14
+ def maximal_independent_set(G, nodes=None, seed=None):
15
+ """Returns a random maximal independent set guaranteed to contain
16
+ a given set of nodes.
17
+
18
+ An independent set is a set of nodes such that the subgraph
19
+ of G induced by these nodes contains no edges. A maximal
20
+ independent set is an independent set such that it is not possible
21
+ to add a new node and still get an independent set.
22
+
23
+ Parameters
24
+ ----------
25
+ G : NetworkX graph
26
+
27
+ nodes : list or iterable
28
+ Nodes that must be part of the independent set. This set of nodes
29
+ must be independent.
30
+
31
+ seed : integer, random_state, or None (default)
32
+ Indicator of random number generation state.
33
+ See :ref:`Randomness<randomness>`.
34
+
35
+ Returns
36
+ -------
37
+ indep_nodes : list
38
+ List of nodes that are part of a maximal independent set.
39
+
40
+ Raises
41
+ ------
42
+ NetworkXUnfeasible
43
+ If the nodes in the provided list are not part of the graph or
44
+ do not form an independent set, an exception is raised.
45
+
46
+ NetworkXNotImplemented
47
+ If `G` is directed.
48
+
49
+ Examples
50
+ --------
51
+ >>> G = nx.path_graph(5)
52
+ >>> nx.maximal_independent_set(G) # doctest: +SKIP
53
+ [4, 0, 2]
54
+ >>> nx.maximal_independent_set(G, [1]) # doctest: +SKIP
55
+ [1, 3]
56
+
57
+ Notes
58
+ -----
59
+ This algorithm does not solve the maximum independent set problem.
60
+
61
+ """
62
+ if not nodes:
63
+ nodes = {seed.choice(list(G))}
64
+ else:
65
+ nodes = set(nodes)
66
+ if not nodes.issubset(G):
67
+ raise nx.NetworkXUnfeasible(f"{nodes} is not a subset of the nodes of G")
68
+ neighbors = set.union(*[set(G.adj[v]) for v in nodes])
69
+ if set.intersection(neighbors, nodes):
70
+ raise nx.NetworkXUnfeasible(f"{nodes} is not an independent set of G")
71
+ indep_nodes = list(nodes)
72
+ available_nodes = set(G.nodes()).difference(neighbors.union(nodes))
73
+ while available_nodes:
74
+ node = seed.choice(list(available_nodes))
75
+ indep_nodes.append(node)
76
+ available_nodes.difference_update(list(G.adj[node]) + [node])
77
+ return indep_nodes
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/moral.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ r"""Function for computing the moral graph of a directed graph."""
2
+
3
+ import itertools
4
+
5
+ import networkx as nx
6
+ from networkx.utils import not_implemented_for
7
+
8
+ __all__ = ["moral_graph"]
9
+
10
+
11
+ @not_implemented_for("undirected")
12
+ @nx._dispatch
13
+ def moral_graph(G):
14
+ r"""Return the Moral Graph
15
+
16
+ Returns the moralized graph of a given directed graph.
17
+
18
+ Parameters
19
+ ----------
20
+ G : NetworkX graph
21
+ Directed graph
22
+
23
+ Returns
24
+ -------
25
+ H : NetworkX graph
26
+ The undirected moralized graph of G
27
+
28
+ Raises
29
+ ------
30
+ NetworkXNotImplemented
31
+ If `G` is undirected.
32
+
33
+ Examples
34
+ --------
35
+ >>> G = nx.DiGraph([(1, 2), (2, 3), (2, 5), (3, 4), (4, 3)])
36
+ >>> G_moral = nx.moral_graph(G)
37
+ >>> G_moral.edges()
38
+ EdgeView([(1, 2), (2, 3), (2, 5), (2, 4), (3, 4)])
39
+
40
+ Notes
41
+ -----
42
+ A moral graph is an undirected graph H = (V, E) generated from a
43
+ directed Graph, where if a node has more than one parent node, edges
44
+ between these parent nodes are inserted and all directed edges become
45
+ undirected.
46
+
47
+ https://en.wikipedia.org/wiki/Moral_graph
48
+
49
+ References
50
+ ----------
51
+ .. [1] Wray L. Buntine. 1995. Chain graphs for learning.
52
+ In Proceedings of the Eleventh conference on Uncertainty
53
+ in artificial intelligence (UAI'95)
54
+ """
55
+ H = G.to_undirected()
56
+ for preds in G.pred.values():
57
+ predecessors_combinations = itertools.combinations(preds, r=2)
58
+ H.add_edges_from(predecessors_combinations)
59
+ return H
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/node_classification.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ This module provides the functions for node classification problem.
2
+
3
+ The functions in this module are not imported
4
+ into the top level `networkx` namespace.
5
+ You can access these functions by importing
6
+ the `networkx.algorithms.node_classification` modules,
7
+ then accessing the functions as attributes of `node_classification`.
8
+ For example:
9
+
10
+ >>> from networkx.algorithms import node_classification
11
+ >>> G = nx.path_graph(4)
12
+ >>> G.edges()
13
+ EdgeView([(0, 1), (1, 2), (2, 3)])
14
+ >>> G.nodes[0]["label"] = "A"
15
+ >>> G.nodes[3]["label"] = "B"
16
+ >>> node_classification.harmonic_function(G)
17
+ ['A', 'A', 'B', 'B']
18
+
19
+ References
20
+ ----------
21
+ Zhu, X., Ghahramani, Z., & Lafferty, J. (2003, August).
22
+ Semi-supervised learning using gaussian fields and harmonic functions.
23
+ In ICML (Vol. 3, pp. 912-919).
24
+ """
25
+ import networkx as nx
26
+
27
+ __all__ = ["harmonic_function", "local_and_global_consistency"]
28
+
29
+
30
+ @nx.utils.not_implemented_for("directed")
31
+ @nx._dispatch(node_attrs="label_name")
32
+ def harmonic_function(G, max_iter=30, label_name="label"):
33
+ """Node classification by Harmonic function
34
+
35
+ Function for computing Harmonic function algorithm by Zhu et al.
36
+
37
+ Parameters
38
+ ----------
39
+ G : NetworkX Graph
40
+ max_iter : int
41
+ maximum number of iterations allowed
42
+ label_name : string
43
+ name of target labels to predict
44
+
45
+ Returns
46
+ -------
47
+ predicted : list
48
+ List of length ``len(G)`` with the predicted labels for each node.
49
+
50
+ Raises
51
+ ------
52
+ NetworkXError
53
+ If no nodes in `G` have attribute `label_name`.
54
+
55
+ Examples
56
+ --------
57
+ >>> from networkx.algorithms import node_classification
58
+ >>> G = nx.path_graph(4)
59
+ >>> G.nodes[0]["label"] = "A"
60
+ >>> G.nodes[3]["label"] = "B"
61
+ >>> G.nodes(data=True)
62
+ NodeDataView({0: {'label': 'A'}, 1: {}, 2: {}, 3: {'label': 'B'}})
63
+ >>> G.edges()
64
+ EdgeView([(0, 1), (1, 2), (2, 3)])
65
+ >>> predicted = node_classification.harmonic_function(G)
66
+ >>> predicted
67
+ ['A', 'A', 'B', 'B']
68
+
69
+ References
70
+ ----------
71
+ Zhu, X., Ghahramani, Z., & Lafferty, J. (2003, August).
72
+ Semi-supervised learning using gaussian fields and harmonic functions.
73
+ In ICML (Vol. 3, pp. 912-919).
74
+ """
75
+ import numpy as np
76
+ import scipy as sp
77
+
78
+ X = nx.to_scipy_sparse_array(G) # adjacency matrix
79
+ labels, label_dict = _get_label_info(G, label_name)
80
+
81
+ if labels.shape[0] == 0:
82
+ raise nx.NetworkXError(
83
+ f"No node on the input graph is labeled by '{label_name}'."
84
+ )
85
+
86
+ n_samples = X.shape[0]
87
+ n_classes = label_dict.shape[0]
88
+ F = np.zeros((n_samples, n_classes))
89
+
90
+ # Build propagation matrix
91
+ degrees = X.sum(axis=0)
92
+ degrees[degrees == 0] = 1 # Avoid division by 0
93
+ # TODO: csr_array
94
+ D = sp.sparse.csr_array(sp.sparse.diags((1.0 / degrees), offsets=0))
95
+ P = (D @ X).tolil()
96
+ P[labels[:, 0]] = 0 # labels[:, 0] indicates IDs of labeled nodes
97
+ # Build base matrix
98
+ B = np.zeros((n_samples, n_classes))
99
+ B[labels[:, 0], labels[:, 1]] = 1
100
+
101
+ for _ in range(max_iter):
102
+ F = (P @ F) + B
103
+
104
+ return label_dict[np.argmax(F, axis=1)].tolist()
105
+
106
+
107
+ @nx.utils.not_implemented_for("directed")
108
+ @nx._dispatch(node_attrs="label_name")
109
+ def local_and_global_consistency(G, alpha=0.99, max_iter=30, label_name="label"):
110
+ """Node classification by Local and Global Consistency
111
+
112
+ Function for computing Local and global consistency algorithm by Zhou et al.
113
+
114
+ Parameters
115
+ ----------
116
+ G : NetworkX Graph
117
+ alpha : float
118
+ Clamping factor
119
+ max_iter : int
120
+ Maximum number of iterations allowed
121
+ label_name : string
122
+ Name of target labels to predict
123
+
124
+ Returns
125
+ -------
126
+ predicted : list
127
+ List of length ``len(G)`` with the predicted labels for each node.
128
+
129
+ Raises
130
+ ------
131
+ NetworkXError
132
+ If no nodes in `G` have attribute `label_name`.
133
+
134
+ Examples
135
+ --------
136
+ >>> from networkx.algorithms import node_classification
137
+ >>> G = nx.path_graph(4)
138
+ >>> G.nodes[0]["label"] = "A"
139
+ >>> G.nodes[3]["label"] = "B"
140
+ >>> G.nodes(data=True)
141
+ NodeDataView({0: {'label': 'A'}, 1: {}, 2: {}, 3: {'label': 'B'}})
142
+ >>> G.edges()
143
+ EdgeView([(0, 1), (1, 2), (2, 3)])
144
+ >>> predicted = node_classification.local_and_global_consistency(G)
145
+ >>> predicted
146
+ ['A', 'A', 'B', 'B']
147
+
148
+ References
149
+ ----------
150
+ Zhou, D., Bousquet, O., Lal, T. N., Weston, J., & Schölkopf, B. (2004).
151
+ Learning with local and global consistency.
152
+ Advances in neural information processing systems, 16(16), 321-328.
153
+ """
154
+ import numpy as np
155
+ import scipy as sp
156
+
157
+ X = nx.to_scipy_sparse_array(G) # adjacency matrix
158
+ labels, label_dict = _get_label_info(G, label_name)
159
+
160
+ if labels.shape[0] == 0:
161
+ raise nx.NetworkXError(
162
+ f"No node on the input graph is labeled by '{label_name}'."
163
+ )
164
+
165
+ n_samples = X.shape[0]
166
+ n_classes = label_dict.shape[0]
167
+ F = np.zeros((n_samples, n_classes))
168
+
169
+ # Build propagation matrix
170
+ degrees = X.sum(axis=0)
171
+ degrees[degrees == 0] = 1 # Avoid division by 0
172
+ # TODO: csr_array
173
+ D2 = np.sqrt(sp.sparse.csr_array(sp.sparse.diags((1.0 / degrees), offsets=0)))
174
+ P = alpha * ((D2 @ X) @ D2)
175
+ # Build base matrix
176
+ B = np.zeros((n_samples, n_classes))
177
+ B[labels[:, 0], labels[:, 1]] = 1 - alpha
178
+
179
+ for _ in range(max_iter):
180
+ F = (P @ F) + B
181
+
182
+ return label_dict[np.argmax(F, axis=1)].tolist()
183
+
184
+
185
+ def _get_label_info(G, label_name):
186
+ """Get and return information of labels from the input graph
187
+
188
+ Parameters
189
+ ----------
190
+ G : Network X graph
191
+ label_name : string
192
+ Name of the target label
193
+
194
+ Returns
195
+ -------
196
+ labels : numpy array, shape = [n_labeled_samples, 2]
197
+ Array of pairs of labeled node ID and label ID
198
+ label_dict : numpy array, shape = [n_classes]
199
+ Array of labels
200
+ i-th element contains the label corresponding label ID `i`
201
+ """
202
+ import numpy as np
203
+
204
+ labels = []
205
+ label_to_id = {}
206
+ lid = 0
207
+ for i, n in enumerate(G.nodes(data=True)):
208
+ if label_name in n[1]:
209
+ label = n[1][label_name]
210
+ if label not in label_to_id:
211
+ label_to_id[label] = lid
212
+ lid += 1
213
+ labels.append([i, label_to_id[label]])
214
+ labels = np.array(labels)
215
+ label_dict = np.array(
216
+ [label for label, _ in sorted(label_to_id.items(), key=lambda x: x[1])]
217
+ )
218
+ return (labels, label_dict)