koichi12 commited on
Commit
558d3d0
·
verified ·
1 Parent(s): 738db08

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/approximation/__pycache__/steinertree.cpython-311.pyc +0 -0
  2. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_clique.cpython-311.pyc +0 -0
  3. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_maxcut.cpython-311.pyc +0 -0
  4. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_treewidth.cpython-311.pyc +0 -0
  5. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/boundary.py +167 -0
  6. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/lukes.py +226 -0
  7. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/quality.py +346 -0
  8. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kernighan_lin.py +91 -0
  9. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/dominance.py +135 -0
  10. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/networksimplex.cpython-311.pyc +0 -0
  11. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/utils.cpython-311.pyc +0 -0
  12. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/dinitz_alg.py +217 -0
  13. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/__init__.py +0 -0
  14. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/__pycache__/test_gomory_hu.cpython-311.pyc +0 -0
  15. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +157 -0
  16. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/__init__.py +2 -0
  17. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/__pycache__/pagerank_alg.cpython-311.pyc +0 -0
  18. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/hits_alg.py +334 -0
  19. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/pagerank_alg.py +499 -0
  20. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  21. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/test_hits.cpython-311.pyc +0 -0
  22. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/test_pagerank.py +217 -0
  23. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/__init__.py +27 -0
  24. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/non_randomness.py +96 -0
  25. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/__init__.py +4 -0
  26. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/all.py +319 -0
  27. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/product.py +534 -0
  28. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/tests/__pycache__/test_all.cpython-311.pyc +0 -0
  29. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/swap.py +405 -0
  30. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__init__.py +13 -0
  31. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/digraph.cpython-311.pyc +0 -0
  32. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/graphviews.cpython-311.pyc +0 -0
  33. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/multidigraph.cpython-311.pyc +0 -0
  34. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/multigraph.cpython-311.pyc +0 -0
  35. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/reportviews.py +1431 -0
  36. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__init__.py +0 -0
  37. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/dispatch_interface.cpython-311.pyc +0 -0
  38. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/historical_tests.cpython-311.pyc +0 -0
  39. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_digraph.cpython-311.pyc +0 -0
  40. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_digraph_historical.cpython-311.pyc +0 -0
  41. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_filters.cpython-311.pyc +0 -0
  42. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_function.cpython-311.pyc +0 -0
  43. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_graph.cpython-311.pyc +0 -0
  44. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_subgraphviews.cpython-311.pyc +0 -0
  45. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/dispatch_interface.py +194 -0
  46. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_backends.py +76 -0
  47. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_coreviews.py +362 -0
  48. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_digraph.py +331 -0
  49. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_function.py +782 -0
  50. tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_graph.py +920 -0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/__pycache__/steinertree.cpython-311.pyc ADDED
Binary file (11.7 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_clique.cpython-311.pyc ADDED
Binary file (7.28 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_maxcut.cpython-311.pyc ADDED
Binary file (5.27 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/approximation/tests/__pycache__/test_treewidth.cpython-311.pyc ADDED
Binary file (15.1 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/boundary.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Routines to find the boundary of a set of nodes.
2
+
3
+ An edge boundary is a set of edges, each of which has exactly one
4
+ endpoint in a given set of nodes (or, in the case of directed graphs,
5
+ the set of edges whose source node is in the set).
6
+
7
+ A node boundary of a set *S* of nodes is the set of (out-)neighbors of
8
+ nodes in *S* that are outside *S*.
9
+
10
+ """
11
+ from itertools import chain
12
+
13
+ import networkx as nx
14
+
15
+ __all__ = ["edge_boundary", "node_boundary"]
16
+
17
+
18
+ @nx._dispatch(edge_attrs={"data": "default"}, preserve_edge_attrs="data")
19
+ def edge_boundary(G, nbunch1, nbunch2=None, data=False, keys=False, default=None):
20
+ """Returns the edge boundary of `nbunch1`.
21
+
22
+ The *edge boundary* of a set *S* with respect to a set *T* is the
23
+ set of edges (*u*, *v*) such that *u* is in *S* and *v* is in *T*.
24
+ If *T* is not specified, it is assumed to be the set of all nodes
25
+ not in *S*.
26
+
27
+ Parameters
28
+ ----------
29
+ G : NetworkX graph
30
+
31
+ nbunch1 : iterable
32
+ Iterable of nodes in the graph representing the set of nodes
33
+ whose edge boundary will be returned. (This is the set *S* from
34
+ the definition above.)
35
+
36
+ nbunch2 : iterable
37
+ Iterable of nodes representing the target (or "exterior") set of
38
+ nodes. (This is the set *T* from the definition above.) If not
39
+ specified, this is assumed to be the set of all nodes in `G`
40
+ not in `nbunch1`.
41
+
42
+ keys : bool
43
+ This parameter has the same meaning as in
44
+ :meth:`MultiGraph.edges`.
45
+
46
+ data : bool or object
47
+ This parameter has the same meaning as in
48
+ :meth:`MultiGraph.edges`.
49
+
50
+ default : object
51
+ This parameter has the same meaning as in
52
+ :meth:`MultiGraph.edges`.
53
+
54
+ Returns
55
+ -------
56
+ iterator
57
+ An iterator over the edges in the boundary of `nbunch1` with
58
+ respect to `nbunch2`. If `keys`, `data`, or `default`
59
+ are specified and `G` is a multigraph, then edges are returned
60
+ with keys and/or data, as in :meth:`MultiGraph.edges`.
61
+
62
+ Examples
63
+ --------
64
+ >>> G = nx.wheel_graph(6)
65
+
66
+ When nbunch2=None:
67
+
68
+ >>> list(nx.edge_boundary(G, (1, 3)))
69
+ [(1, 0), (1, 2), (1, 5), (3, 0), (3, 2), (3, 4)]
70
+
71
+ When nbunch2 is given:
72
+
73
+ >>> list(nx.edge_boundary(G, (1, 3), (2, 0)))
74
+ [(1, 0), (1, 2), (3, 0), (3, 2)]
75
+
76
+ Notes
77
+ -----
78
+ Any element of `nbunch` that is not in the graph `G` will be
79
+ ignored.
80
+
81
+ `nbunch1` and `nbunch2` are usually meant to be disjoint, but in
82
+ the interest of speed and generality, that is not required here.
83
+
84
+ """
85
+ nset1 = {n for n in nbunch1 if n in G}
86
+ # Here we create an iterator over edges incident to nodes in the set
87
+ # `nset1`. The `Graph.edges()` method does not provide a guarantee
88
+ # on the orientation of the edges, so our algorithm below must
89
+ # handle the case in which exactly one orientation, either (u, v) or
90
+ # (v, u), appears in this iterable.
91
+ if G.is_multigraph():
92
+ edges = G.edges(nset1, data=data, keys=keys, default=default)
93
+ else:
94
+ edges = G.edges(nset1, data=data, default=default)
95
+ # If `nbunch2` is not provided, then it is assumed to be the set
96
+ # complement of `nbunch1`. For the sake of efficiency, this is
97
+ # implemented by using the `not in` operator, instead of by creating
98
+ # an additional set and using the `in` operator.
99
+ if nbunch2 is None:
100
+ return (e for e in edges if (e[0] in nset1) ^ (e[1] in nset1))
101
+ nset2 = set(nbunch2)
102
+ return (
103
+ e
104
+ for e in edges
105
+ if (e[0] in nset1 and e[1] in nset2) or (e[1] in nset1 and e[0] in nset2)
106
+ )
107
+
108
+
109
+ @nx._dispatch
110
+ def node_boundary(G, nbunch1, nbunch2=None):
111
+ """Returns the node boundary of `nbunch1`.
112
+
113
+ The *node boundary* of a set *S* with respect to a set *T* is the
114
+ set of nodes *v* in *T* such that for some *u* in *S*, there is an
115
+ edge joining *u* to *v*. If *T* is not specified, it is assumed to
116
+ be the set of all nodes not in *S*.
117
+
118
+ Parameters
119
+ ----------
120
+ G : NetworkX graph
121
+
122
+ nbunch1 : iterable
123
+ Iterable of nodes in the graph representing the set of nodes
124
+ whose node boundary will be returned. (This is the set *S* from
125
+ the definition above.)
126
+
127
+ nbunch2 : iterable
128
+ Iterable of nodes representing the target (or "exterior") set of
129
+ nodes. (This is the set *T* from the definition above.) If not
130
+ specified, this is assumed to be the set of all nodes in `G`
131
+ not in `nbunch1`.
132
+
133
+ Returns
134
+ -------
135
+ set
136
+ The node boundary of `nbunch1` with respect to `nbunch2`.
137
+
138
+ Examples
139
+ --------
140
+ >>> G = nx.wheel_graph(6)
141
+
142
+ When nbunch2=None:
143
+
144
+ >>> list(nx.node_boundary(G, (3, 4)))
145
+ [0, 2, 5]
146
+
147
+ When nbunch2 is given:
148
+
149
+ >>> list(nx.node_boundary(G, (3, 4), (0, 1, 5)))
150
+ [0, 5]
151
+
152
+ Notes
153
+ -----
154
+ Any element of `nbunch` that is not in the graph `G` will be
155
+ ignored.
156
+
157
+ `nbunch1` and `nbunch2` are usually meant to be disjoint, but in
158
+ the interest of speed and generality, that is not required here.
159
+
160
+ """
161
+ nset1 = {n for n in nbunch1 if n in G}
162
+ bdy = set(chain.from_iterable(G[v] for v in nset1)) - nset1
163
+ # If `nbunch2` is not specified, it is assumed to be the set
164
+ # complement of `nbunch1`.
165
+ if nbunch2 is not None:
166
+ bdy &= set(nbunch2)
167
+ return bdy
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/lukes.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Lukes Algorithm for exact optimal weighted tree partitioning."""
2
+
3
+ from copy import deepcopy
4
+ from functools import lru_cache
5
+ from random import choice
6
+
7
+ import networkx as nx
8
+ from networkx.utils import not_implemented_for
9
+
10
+ __all__ = ["lukes_partitioning"]
11
+
12
+ D_EDGE_W = "weight"
13
+ D_EDGE_VALUE = 1.0
14
+ D_NODE_W = "weight"
15
+ D_NODE_VALUE = 1
16
+ PKEY = "partitions"
17
+ CLUSTER_EVAL_CACHE_SIZE = 2048
18
+
19
+
20
+ def _split_n_from(n, min_size_of_first_part):
21
+ # splits j in two parts of which the first is at least
22
+ # the second argument
23
+ assert n >= min_size_of_first_part
24
+ for p1 in range(min_size_of_first_part, n + 1):
25
+ yield p1, n - p1
26
+
27
+
28
+ @nx._dispatch(node_attrs="node_weight", edge_attrs="edge_weight")
29
+ def lukes_partitioning(G, max_size, node_weight=None, edge_weight=None):
30
+ """Optimal partitioning of a weighted tree using the Lukes algorithm.
31
+
32
+ This algorithm partitions a connected, acyclic graph featuring integer
33
+ node weights and float edge weights. The resulting clusters are such
34
+ that the total weight of the nodes in each cluster does not exceed
35
+ max_size and that the weight of the edges that are cut by the partition
36
+ is minimum. The algorithm is based on [1]_.
37
+
38
+ Parameters
39
+ ----------
40
+ G : NetworkX graph
41
+
42
+ max_size : int
43
+ Maximum weight a partition can have in terms of sum of
44
+ node_weight for all nodes in the partition
45
+
46
+ edge_weight : key
47
+ Edge data key to use as weight. If None, the weights are all
48
+ set to one.
49
+
50
+ node_weight : key
51
+ Node data key to use as weight. If None, the weights are all
52
+ set to one. The data must be int.
53
+
54
+ Returns
55
+ -------
56
+ partition : list
57
+ A list of sets of nodes representing the clusters of the
58
+ partition.
59
+
60
+ Raises
61
+ ------
62
+ NotATree
63
+ If G is not a tree.
64
+ TypeError
65
+ If any of the values of node_weight is not int.
66
+
67
+ References
68
+ ----------
69
+ .. [1] Lukes, J. A. (1974).
70
+ "Efficient Algorithm for the Partitioning of Trees."
71
+ IBM Journal of Research and Development, 18(3), 217–224.
72
+
73
+ """
74
+ # First sanity check and tree preparation
75
+ if not nx.is_tree(G):
76
+ raise nx.NotATree("lukes_partitioning works only on trees")
77
+ else:
78
+ if nx.is_directed(G):
79
+ root = [n for n, d in G.in_degree() if d == 0]
80
+ assert len(root) == 1
81
+ root = root[0]
82
+ t_G = deepcopy(G)
83
+ else:
84
+ root = choice(list(G.nodes))
85
+ # this has the desirable side effect of not inheriting attributes
86
+ t_G = nx.dfs_tree(G, root)
87
+
88
+ # Since we do not want to screw up the original graph,
89
+ # if we have a blank attribute, we make a deepcopy
90
+ if edge_weight is None or node_weight is None:
91
+ safe_G = deepcopy(G)
92
+ if edge_weight is None:
93
+ nx.set_edge_attributes(safe_G, D_EDGE_VALUE, D_EDGE_W)
94
+ edge_weight = D_EDGE_W
95
+ if node_weight is None:
96
+ nx.set_node_attributes(safe_G, D_NODE_VALUE, D_NODE_W)
97
+ node_weight = D_NODE_W
98
+ else:
99
+ safe_G = G
100
+
101
+ # Second sanity check
102
+ # The values of node_weight MUST BE int.
103
+ # I cannot see any room for duck typing without incurring serious
104
+ # danger of subtle bugs.
105
+ all_n_attr = nx.get_node_attributes(safe_G, node_weight).values()
106
+ for x in all_n_attr:
107
+ if not isinstance(x, int):
108
+ raise TypeError(
109
+ "lukes_partitioning needs integer "
110
+ f"values for node_weight ({node_weight})"
111
+ )
112
+
113
+ # SUBROUTINES -----------------------
114
+ # these functions are defined here for two reasons:
115
+ # - brevity: we can leverage global "safe_G"
116
+ # - caching: signatures are hashable
117
+
118
+ @not_implemented_for("undirected")
119
+ # this is intended to be called only on t_G
120
+ def _leaves(gr):
121
+ for x in gr.nodes:
122
+ if not nx.descendants(gr, x):
123
+ yield x
124
+
125
+ @not_implemented_for("undirected")
126
+ def _a_parent_of_leaves_only(gr):
127
+ tleaves = set(_leaves(gr))
128
+ for n in set(gr.nodes) - tleaves:
129
+ if all(x in tleaves for x in nx.descendants(gr, n)):
130
+ return n
131
+
132
+ @lru_cache(CLUSTER_EVAL_CACHE_SIZE)
133
+ def _value_of_cluster(cluster):
134
+ valid_edges = [e for e in safe_G.edges if e[0] in cluster and e[1] in cluster]
135
+ return sum(safe_G.edges[e][edge_weight] for e in valid_edges)
136
+
137
+ def _value_of_partition(partition):
138
+ return sum(_value_of_cluster(frozenset(c)) for c in partition)
139
+
140
+ @lru_cache(CLUSTER_EVAL_CACHE_SIZE)
141
+ def _weight_of_cluster(cluster):
142
+ return sum(safe_G.nodes[n][node_weight] for n in cluster)
143
+
144
+ def _pivot(partition, node):
145
+ ccx = [c for c in partition if node in c]
146
+ assert len(ccx) == 1
147
+ return ccx[0]
148
+
149
+ def _concatenate_or_merge(partition_1, partition_2, x, i, ref_weight):
150
+ ccx = _pivot(partition_1, x)
151
+ cci = _pivot(partition_2, i)
152
+ merged_xi = ccx.union(cci)
153
+
154
+ # We first check if we can do the merge.
155
+ # If so, we do the actual calculations, otherwise we concatenate
156
+ if _weight_of_cluster(frozenset(merged_xi)) <= ref_weight:
157
+ cp1 = list(filter(lambda x: x != ccx, partition_1))
158
+ cp2 = list(filter(lambda x: x != cci, partition_2))
159
+
160
+ option_2 = [merged_xi] + cp1 + cp2
161
+ return option_2, _value_of_partition(option_2)
162
+ else:
163
+ option_1 = partition_1 + partition_2
164
+ return option_1, _value_of_partition(option_1)
165
+
166
+ # INITIALIZATION -----------------------
167
+ leaves = set(_leaves(t_G))
168
+ for lv in leaves:
169
+ t_G.nodes[lv][PKEY] = {}
170
+ slot = safe_G.nodes[lv][node_weight]
171
+ t_G.nodes[lv][PKEY][slot] = [{lv}]
172
+ t_G.nodes[lv][PKEY][0] = [{lv}]
173
+
174
+ for inner in [x for x in t_G.nodes if x not in leaves]:
175
+ t_G.nodes[inner][PKEY] = {}
176
+ slot = safe_G.nodes[inner][node_weight]
177
+ t_G.nodes[inner][PKEY][slot] = [{inner}]
178
+
179
+ # CORE ALGORITHM -----------------------
180
+ while True:
181
+ x_node = _a_parent_of_leaves_only(t_G)
182
+ weight_of_x = safe_G.nodes[x_node][node_weight]
183
+ best_value = 0
184
+ best_partition = None
185
+ bp_buffer = {}
186
+ x_descendants = nx.descendants(t_G, x_node)
187
+ for i_node in x_descendants:
188
+ for j in range(weight_of_x, max_size + 1):
189
+ for a, b in _split_n_from(j, weight_of_x):
190
+ if (
191
+ a not in t_G.nodes[x_node][PKEY]
192
+ or b not in t_G.nodes[i_node][PKEY]
193
+ ):
194
+ # it's not possible to form this particular weight sum
195
+ continue
196
+
197
+ part1 = t_G.nodes[x_node][PKEY][a]
198
+ part2 = t_G.nodes[i_node][PKEY][b]
199
+ part, value = _concatenate_or_merge(part1, part2, x_node, i_node, j)
200
+
201
+ if j not in bp_buffer or bp_buffer[j][1] < value:
202
+ # we annotate in the buffer the best partition for j
203
+ bp_buffer[j] = part, value
204
+
205
+ # we also keep track of the overall best partition
206
+ if best_value <= value:
207
+ best_value = value
208
+ best_partition = part
209
+
210
+ # as illustrated in Lukes, once we finished a child, we can
211
+ # discharge the partitions we found into the graph
212
+ # (the key phrase is make all x == x')
213
+ # so that they are used by the subsequent children
214
+ for w, (best_part_for_vl, vl) in bp_buffer.items():
215
+ t_G.nodes[x_node][PKEY][w] = best_part_for_vl
216
+ bp_buffer.clear()
217
+
218
+ # the absolute best partition for this node
219
+ # across all weights has to be stored at 0
220
+ t_G.nodes[x_node][PKEY][0] = best_partition
221
+ t_G.remove_nodes_from(x_descendants)
222
+
223
+ if x_node == root:
224
+ # the 0-labeled partition of root
225
+ # is the optimal one for the whole tree
226
+ return t_G.nodes[root][PKEY][0]
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/quality.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions for measuring the quality of a partition (into
2
+ communities).
3
+
4
+ """
5
+
6
+ from itertools import combinations
7
+
8
+ import networkx as nx
9
+ from networkx import NetworkXError
10
+ from networkx.algorithms.community.community_utils import is_partition
11
+ from networkx.utils.decorators import argmap
12
+
13
+ __all__ = ["modularity", "partition_quality"]
14
+
15
+
16
+ class NotAPartition(NetworkXError):
17
+ """Raised if a given collection is not a partition."""
18
+
19
+ def __init__(self, G, collection):
20
+ msg = f"{collection} is not a valid partition of the graph {G}"
21
+ super().__init__(msg)
22
+
23
+
24
+ def _require_partition(G, partition):
25
+ """Decorator to check that a valid partition is input to a function
26
+
27
+ Raises :exc:`networkx.NetworkXError` if the partition is not valid.
28
+
29
+ This decorator should be used on functions whose first two arguments
30
+ are a graph and a partition of the nodes of that graph (in that
31
+ order)::
32
+
33
+ >>> @require_partition
34
+ ... def foo(G, partition):
35
+ ... print("partition is valid!")
36
+ ...
37
+ >>> G = nx.complete_graph(5)
38
+ >>> partition = [{0, 1}, {2, 3}, {4}]
39
+ >>> foo(G, partition)
40
+ partition is valid!
41
+ >>> partition = [{0}, {2, 3}, {4}]
42
+ >>> foo(G, partition)
43
+ Traceback (most recent call last):
44
+ ...
45
+ networkx.exception.NetworkXError: `partition` is not a valid partition of the nodes of G
46
+ >>> partition = [{0, 1}, {1, 2, 3}, {4}]
47
+ >>> foo(G, partition)
48
+ Traceback (most recent call last):
49
+ ...
50
+ networkx.exception.NetworkXError: `partition` is not a valid partition of the nodes of G
51
+
52
+ """
53
+ if is_partition(G, partition):
54
+ return G, partition
55
+ raise nx.NetworkXError("`partition` is not a valid partition of the nodes of G")
56
+
57
+
58
+ require_partition = argmap(_require_partition, (0, 1))
59
+
60
+
61
+ @nx._dispatch
62
+ def intra_community_edges(G, partition):
63
+ """Returns the number of intra-community edges for a partition of `G`.
64
+
65
+ Parameters
66
+ ----------
67
+ G : NetworkX graph.
68
+
69
+ partition : iterable of sets of nodes
70
+ This must be a partition of the nodes of `G`.
71
+
72
+ The "intra-community edges" are those edges joining a pair of nodes
73
+ in the same block of the partition.
74
+
75
+ """
76
+ return sum(G.subgraph(block).size() for block in partition)
77
+
78
+
79
+ @nx._dispatch
80
+ def inter_community_edges(G, partition):
81
+ """Returns the number of inter-community edges for a partition of `G`.
82
+ according to the given
83
+ partition of the nodes of `G`.
84
+
85
+ Parameters
86
+ ----------
87
+ G : NetworkX graph.
88
+
89
+ partition : iterable of sets of nodes
90
+ This must be a partition of the nodes of `G`.
91
+
92
+ The *inter-community edges* are those edges joining a pair of nodes
93
+ in different blocks of the partition.
94
+
95
+ Implementation note: this function creates an intermediate graph
96
+ that may require the same amount of memory as that of `G`.
97
+
98
+ """
99
+ # Alternate implementation that does not require constructing a new
100
+ # graph object (but does require constructing an affiliation
101
+ # dictionary):
102
+ #
103
+ # aff = dict(chain.from_iterable(((v, block) for v in block)
104
+ # for block in partition))
105
+ # return sum(1 for u, v in G.edges() if aff[u] != aff[v])
106
+ #
107
+ MG = nx.MultiDiGraph if G.is_directed() else nx.MultiGraph
108
+ return nx.quotient_graph(G, partition, create_using=MG).size()
109
+
110
+
111
+ @nx._dispatch
112
+ def inter_community_non_edges(G, partition):
113
+ """Returns the number of inter-community non-edges according to the
114
+ given partition of the nodes of `G`.
115
+
116
+ Parameters
117
+ ----------
118
+ G : NetworkX graph.
119
+
120
+ partition : iterable of sets of nodes
121
+ This must be a partition of the nodes of `G`.
122
+
123
+ A *non-edge* is a pair of nodes (undirected if `G` is undirected)
124
+ that are not adjacent in `G`. The *inter-community non-edges* are
125
+ those non-edges on a pair of nodes in different blocks of the
126
+ partition.
127
+
128
+ Implementation note: this function creates two intermediate graphs,
129
+ which may require up to twice the amount of memory as required to
130
+ store `G`.
131
+
132
+ """
133
+ # Alternate implementation that does not require constructing two
134
+ # new graph objects (but does require constructing an affiliation
135
+ # dictionary):
136
+ #
137
+ # aff = dict(chain.from_iterable(((v, block) for v in block)
138
+ # for block in partition))
139
+ # return sum(1 for u, v in nx.non_edges(G) if aff[u] != aff[v])
140
+ #
141
+ return inter_community_edges(nx.complement(G), partition)
142
+
143
+
144
+ @nx._dispatch(edge_attrs="weight")
145
+ def modularity(G, communities, weight="weight", resolution=1):
146
+ r"""Returns the modularity of the given partition of the graph.
147
+
148
+ Modularity is defined in [1]_ as
149
+
150
+ .. math::
151
+ Q = \frac{1}{2m} \sum_{ij} \left( A_{ij} - \gamma\frac{k_ik_j}{2m}\right)
152
+ \delta(c_i,c_j)
153
+
154
+ where $m$ is the number of edges (or sum of all edge weights as in [5]_),
155
+ $A$ is the adjacency matrix of `G`, $k_i$ is the (weighted) degree of $i$,
156
+ $\gamma$ is the resolution parameter, and $\delta(c_i, c_j)$ is 1 if $i$ and
157
+ $j$ are in the same community else 0.
158
+
159
+ According to [2]_ (and verified by some algebra) this can be reduced to
160
+
161
+ .. math::
162
+ Q = \sum_{c=1}^{n}
163
+ \left[ \frac{L_c}{m} - \gamma\left( \frac{k_c}{2m} \right) ^2 \right]
164
+
165
+ where the sum iterates over all communities $c$, $m$ is the number of edges,
166
+ $L_c$ is the number of intra-community links for community $c$,
167
+ $k_c$ is the sum of degrees of the nodes in community $c$,
168
+ and $\gamma$ is the resolution parameter.
169
+
170
+ The resolution parameter sets an arbitrary tradeoff between intra-group
171
+ edges and inter-group edges. More complex grouping patterns can be
172
+ discovered by analyzing the same network with multiple values of gamma
173
+ and then combining the results [3]_. That said, it is very common to
174
+ simply use gamma=1. More on the choice of gamma is in [4]_.
175
+
176
+ The second formula is the one actually used in calculation of the modularity.
177
+ For directed graphs the second formula replaces $k_c$ with $k^{in}_c k^{out}_c$.
178
+
179
+ Parameters
180
+ ----------
181
+ G : NetworkX Graph
182
+
183
+ communities : list or iterable of set of nodes
184
+ These node sets must represent a partition of G's nodes.
185
+
186
+ weight : string or None, optional (default="weight")
187
+ The edge attribute that holds the numerical value used
188
+ as a weight. If None or an edge does not have that attribute,
189
+ then that edge has weight 1.
190
+
191
+ resolution : float (default=1)
192
+ If resolution is less than 1, modularity favors larger communities.
193
+ Greater than 1 favors smaller communities.
194
+
195
+ Returns
196
+ -------
197
+ Q : float
198
+ The modularity of the partition.
199
+
200
+ Raises
201
+ ------
202
+ NotAPartition
203
+ If `communities` is not a partition of the nodes of `G`.
204
+
205
+ Examples
206
+ --------
207
+ >>> G = nx.barbell_graph(3, 0)
208
+ >>> nx.community.modularity(G, [{0, 1, 2}, {3, 4, 5}])
209
+ 0.35714285714285715
210
+ >>> nx.community.modularity(G, nx.community.label_propagation_communities(G))
211
+ 0.35714285714285715
212
+
213
+ References
214
+ ----------
215
+ .. [1] M. E. J. Newman "Networks: An Introduction", page 224.
216
+ Oxford University Press, 2011.
217
+ .. [2] Clauset, Aaron, Mark EJ Newman, and Cristopher Moore.
218
+ "Finding community structure in very large networks."
219
+ Phys. Rev. E 70.6 (2004). <https://arxiv.org/abs/cond-mat/0408187>
220
+ .. [3] Reichardt and Bornholdt "Statistical Mechanics of Community Detection"
221
+ Phys. Rev. E 74, 016110, 2006. https://doi.org/10.1103/PhysRevE.74.016110
222
+ .. [4] M. E. J. Newman, "Equivalence between modularity optimization and
223
+ maximum likelihood methods for community detection"
224
+ Phys. Rev. E 94, 052315, 2016. https://doi.org/10.1103/PhysRevE.94.052315
225
+ .. [5] Blondel, V.D. et al. "Fast unfolding of communities in large
226
+ networks" J. Stat. Mech 10008, 1-12 (2008).
227
+ https://doi.org/10.1088/1742-5468/2008/10/P10008
228
+ """
229
+ if not isinstance(communities, list):
230
+ communities = list(communities)
231
+ if not is_partition(G, communities):
232
+ raise NotAPartition(G, communities)
233
+
234
+ directed = G.is_directed()
235
+ if directed:
236
+ out_degree = dict(G.out_degree(weight=weight))
237
+ in_degree = dict(G.in_degree(weight=weight))
238
+ m = sum(out_degree.values())
239
+ norm = 1 / m**2
240
+ else:
241
+ out_degree = in_degree = dict(G.degree(weight=weight))
242
+ deg_sum = sum(out_degree.values())
243
+ m = deg_sum / 2
244
+ norm = 1 / deg_sum**2
245
+
246
+ def community_contribution(community):
247
+ comm = set(community)
248
+ L_c = sum(wt for u, v, wt in G.edges(comm, data=weight, default=1) if v in comm)
249
+
250
+ out_degree_sum = sum(out_degree[u] for u in comm)
251
+ in_degree_sum = sum(in_degree[u] for u in comm) if directed else out_degree_sum
252
+
253
+ return L_c / m - resolution * out_degree_sum * in_degree_sum * norm
254
+
255
+ return sum(map(community_contribution, communities))
256
+
257
+
258
+ @require_partition
259
+ @nx._dispatch
260
+ def partition_quality(G, partition):
261
+ """Returns the coverage and performance of a partition of G.
262
+
263
+ The *coverage* of a partition is the ratio of the number of
264
+ intra-community edges to the total number of edges in the graph.
265
+
266
+ The *performance* of a partition is the number of
267
+ intra-community edges plus inter-community non-edges divided by the total
268
+ number of potential edges.
269
+
270
+ This algorithm has complexity $O(C^2 + L)$ where C is the number of communities and L is the number of links.
271
+
272
+ Parameters
273
+ ----------
274
+ G : NetworkX graph
275
+
276
+ partition : sequence
277
+ Partition of the nodes of `G`, represented as a sequence of
278
+ sets of nodes (blocks). Each block of the partition represents a
279
+ community.
280
+
281
+ Returns
282
+ -------
283
+ (float, float)
284
+ The (coverage, performance) tuple of the partition, as defined above.
285
+
286
+ Raises
287
+ ------
288
+ NetworkXError
289
+ If `partition` is not a valid partition of the nodes of `G`.
290
+
291
+ Notes
292
+ -----
293
+ If `G` is a multigraph;
294
+ - for coverage, the multiplicity of edges is counted
295
+ - for performance, the result is -1 (total number of possible edges is not defined)
296
+
297
+ References
298
+ ----------
299
+ .. [1] Santo Fortunato.
300
+ "Community Detection in Graphs".
301
+ *Physical Reports*, Volume 486, Issue 3--5 pp. 75--174
302
+ <https://arxiv.org/abs/0906.0612>
303
+ """
304
+
305
+ node_community = {}
306
+ for i, community in enumerate(partition):
307
+ for node in community:
308
+ node_community[node] = i
309
+
310
+ # `performance` is not defined for multigraphs
311
+ if not G.is_multigraph():
312
+ # Iterate over the communities, quadratic, to calculate `possible_inter_community_edges`
313
+ possible_inter_community_edges = sum(
314
+ len(p1) * len(p2) for p1, p2 in combinations(partition, 2)
315
+ )
316
+
317
+ if G.is_directed():
318
+ possible_inter_community_edges *= 2
319
+ else:
320
+ possible_inter_community_edges = 0
321
+
322
+ # Compute the number of edges in the complete graph -- `n` nodes,
323
+ # directed or undirected, depending on `G`
324
+ n = len(G)
325
+ total_pairs = n * (n - 1)
326
+ if not G.is_directed():
327
+ total_pairs //= 2
328
+
329
+ intra_community_edges = 0
330
+ inter_community_non_edges = possible_inter_community_edges
331
+
332
+ # Iterate over the links to count `intra_community_edges` and `inter_community_non_edges`
333
+ for e in G.edges():
334
+ if node_community[e[0]] == node_community[e[1]]:
335
+ intra_community_edges += 1
336
+ else:
337
+ inter_community_non_edges -= 1
338
+
339
+ coverage = intra_community_edges / len(G.edges)
340
+
341
+ if G.is_multigraph():
342
+ performance = -1.0
343
+ else:
344
+ performance = (intra_community_edges + inter_community_non_edges) / total_pairs
345
+
346
+ return coverage, performance
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/community/tests/test_kernighan_lin.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unit tests for the :mod:`networkx.algorithms.community.kernighan_lin`
2
+ module.
3
+ """
4
+ from itertools import permutations
5
+
6
+ import pytest
7
+
8
+ import networkx as nx
9
+ from networkx.algorithms.community import kernighan_lin_bisection
10
+
11
+
12
+ def assert_partition_equal(x, y):
13
+ assert set(map(frozenset, x)) == set(map(frozenset, y))
14
+
15
+
16
+ def test_partition():
17
+ G = nx.barbell_graph(3, 0)
18
+ C = kernighan_lin_bisection(G)
19
+ assert_partition_equal(C, [{0, 1, 2}, {3, 4, 5}])
20
+
21
+
22
+ def test_partition_argument():
23
+ G = nx.barbell_graph(3, 0)
24
+ partition = [{0, 1, 2}, {3, 4, 5}]
25
+ C = kernighan_lin_bisection(G, partition)
26
+ assert_partition_equal(C, partition)
27
+
28
+
29
+ def test_partition_argument_non_integer_nodes():
30
+ G = nx.Graph([("A", "B"), ("A", "C"), ("B", "C"), ("C", "D")])
31
+ partition = ({"A", "B"}, {"C", "D"})
32
+ C = kernighan_lin_bisection(G, partition)
33
+ assert_partition_equal(C, partition)
34
+
35
+
36
+ def test_seed_argument():
37
+ G = nx.barbell_graph(3, 0)
38
+ C = kernighan_lin_bisection(G, seed=1)
39
+ assert_partition_equal(C, [{0, 1, 2}, {3, 4, 5}])
40
+
41
+
42
+ def test_non_disjoint_partition():
43
+ with pytest.raises(nx.NetworkXError):
44
+ G = nx.barbell_graph(3, 0)
45
+ partition = ({0, 1, 2}, {2, 3, 4, 5})
46
+ kernighan_lin_bisection(G, partition)
47
+
48
+
49
+ def test_too_many_blocks():
50
+ with pytest.raises(nx.NetworkXError):
51
+ G = nx.barbell_graph(3, 0)
52
+ partition = ({0, 1}, {2}, {3, 4, 5})
53
+ kernighan_lin_bisection(G, partition)
54
+
55
+
56
+ def test_multigraph():
57
+ G = nx.cycle_graph(4)
58
+ M = nx.MultiGraph(G.edges())
59
+ M.add_edges_from(G.edges())
60
+ M.remove_edge(1, 2)
61
+ for labels in permutations(range(4)):
62
+ mapping = dict(zip(M, labels))
63
+ A, B = kernighan_lin_bisection(nx.relabel_nodes(M, mapping), seed=0)
64
+ assert_partition_equal(
65
+ [A, B], [{mapping[0], mapping[1]}, {mapping[2], mapping[3]}]
66
+ )
67
+
68
+
69
+ def test_max_iter_argument():
70
+ G = nx.Graph(
71
+ [
72
+ ("A", "B", {"weight": 1}),
73
+ ("A", "C", {"weight": 2}),
74
+ ("A", "D", {"weight": 3}),
75
+ ("A", "E", {"weight": 2}),
76
+ ("A", "F", {"weight": 4}),
77
+ ("B", "C", {"weight": 1}),
78
+ ("B", "D", {"weight": 4}),
79
+ ("B", "E", {"weight": 2}),
80
+ ("B", "F", {"weight": 1}),
81
+ ("C", "D", {"weight": 3}),
82
+ ("C", "E", {"weight": 2}),
83
+ ("C", "F", {"weight": 1}),
84
+ ("D", "E", {"weight": 4}),
85
+ ("D", "F", {"weight": 3}),
86
+ ("E", "F", {"weight": 2}),
87
+ ]
88
+ )
89
+ partition = ({"A", "B", "C"}, {"D", "E", "F"})
90
+ C = kernighan_lin_bisection(G, partition, max_iter=1)
91
+ assert_partition_equal(C, ({"A", "F", "C"}, {"D", "E", "B"}))
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/dominance.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dominance algorithms.
3
+ """
4
+
5
+ from functools import reduce
6
+
7
+ import networkx as nx
8
+ from networkx.utils import not_implemented_for
9
+
10
+ __all__ = ["immediate_dominators", "dominance_frontiers"]
11
+
12
+
13
+ @not_implemented_for("undirected")
14
+ @nx._dispatch
15
+ def immediate_dominators(G, start):
16
+ """Returns the immediate dominators of all nodes of a directed graph.
17
+
18
+ Parameters
19
+ ----------
20
+ G : a DiGraph or MultiDiGraph
21
+ The graph where dominance is to be computed.
22
+
23
+ start : node
24
+ The start node of dominance computation.
25
+
26
+ Returns
27
+ -------
28
+ idom : dict keyed by nodes
29
+ A dict containing the immediate dominators of each node reachable from
30
+ `start`.
31
+
32
+ Raises
33
+ ------
34
+ NetworkXNotImplemented
35
+ If `G` is undirected.
36
+
37
+ NetworkXError
38
+ If `start` is not in `G`.
39
+
40
+ Notes
41
+ -----
42
+ Except for `start`, the immediate dominators are the parents of their
43
+ corresponding nodes in the dominator tree.
44
+
45
+ Examples
46
+ --------
47
+ >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
48
+ >>> sorted(nx.immediate_dominators(G, 1).items())
49
+ [(1, 1), (2, 1), (3, 1), (4, 3), (5, 1)]
50
+
51
+ References
52
+ ----------
53
+ .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy.
54
+ A simple, fast dominance algorithm.
55
+ Software Practice & Experience, 4:110, 2001.
56
+ """
57
+ if start not in G:
58
+ raise nx.NetworkXError("start is not in G")
59
+
60
+ idom = {start: start}
61
+
62
+ order = list(nx.dfs_postorder_nodes(G, start))
63
+ dfn = {u: i for i, u in enumerate(order)}
64
+ order.pop()
65
+ order.reverse()
66
+
67
+ def intersect(u, v):
68
+ while u != v:
69
+ while dfn[u] < dfn[v]:
70
+ u = idom[u]
71
+ while dfn[u] > dfn[v]:
72
+ v = idom[v]
73
+ return u
74
+
75
+ changed = True
76
+ while changed:
77
+ changed = False
78
+ for u in order:
79
+ new_idom = reduce(intersect, (v for v in G.pred[u] if v in idom))
80
+ if u not in idom or idom[u] != new_idom:
81
+ idom[u] = new_idom
82
+ changed = True
83
+
84
+ return idom
85
+
86
+
87
+ @nx._dispatch
88
+ def dominance_frontiers(G, start):
89
+ """Returns the dominance frontiers of all nodes of a directed graph.
90
+
91
+ Parameters
92
+ ----------
93
+ G : a DiGraph or MultiDiGraph
94
+ The graph where dominance is to be computed.
95
+
96
+ start : node
97
+ The start node of dominance computation.
98
+
99
+ Returns
100
+ -------
101
+ df : dict keyed by nodes
102
+ A dict containing the dominance frontiers of each node reachable from
103
+ `start` as lists.
104
+
105
+ Raises
106
+ ------
107
+ NetworkXNotImplemented
108
+ If `G` is undirected.
109
+
110
+ NetworkXError
111
+ If `start` is not in `G`.
112
+
113
+ Examples
114
+ --------
115
+ >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)])
116
+ >>> sorted((u, sorted(df)) for u, df in nx.dominance_frontiers(G, 1).items())
117
+ [(1, []), (2, [5]), (3, [5]), (4, [5]), (5, [])]
118
+
119
+ References
120
+ ----------
121
+ .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy.
122
+ A simple, fast dominance algorithm.
123
+ Software Practice & Experience, 4:110, 2001.
124
+ """
125
+ idom = nx.immediate_dominators(G, start)
126
+
127
+ df = {u: set() for u in idom}
128
+ for u in idom:
129
+ if len(G.pred[u]) >= 2:
130
+ for v in G.pred[u]:
131
+ if v in idom:
132
+ while v != idom[u]:
133
+ df[v].add(u)
134
+ v = idom[v]
135
+ return df
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/networksimplex.cpython-311.pyc ADDED
Binary file (32.3 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/__pycache__/utils.cpython-311.pyc ADDED
Binary file (9.74 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/dinitz_alg.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dinitz' algorithm for maximum flow problems.
3
+ """
4
+ from collections import deque
5
+
6
+ import networkx as nx
7
+ from networkx.algorithms.flow.utils import build_residual_network
8
+ from networkx.utils import pairwise
9
+
10
+ __all__ = ["dinitz"]
11
+
12
+
13
+ @nx._dispatch(
14
+ graphs={"G": 0, "residual?": 4},
15
+ edge_attrs={"capacity": float("inf")},
16
+ preserve_edge_attrs={"residual": {"capacity": float("inf")}},
17
+ preserve_graph_attrs={"residual"},
18
+ )
19
+ def dinitz(G, s, t, capacity="capacity", residual=None, value_only=False, cutoff=None):
20
+ """Find a maximum single-commodity flow using Dinitz' algorithm.
21
+
22
+ This function returns the residual network resulting after computing
23
+ the maximum flow. See below for details about the conventions
24
+ NetworkX uses for defining residual networks.
25
+
26
+ This algorithm has a running time of $O(n^2 m)$ for $n$ nodes and $m$
27
+ edges [1]_.
28
+
29
+
30
+ Parameters
31
+ ----------
32
+ G : NetworkX graph
33
+ Edges of the graph are expected to have an attribute called
34
+ 'capacity'. If this attribute is not present, the edge is
35
+ considered to have infinite capacity.
36
+
37
+ s : node
38
+ Source node for the flow.
39
+
40
+ t : node
41
+ Sink node for the flow.
42
+
43
+ capacity : string
44
+ Edges of the graph G are expected to have an attribute capacity
45
+ that indicates how much flow the edge can support. If this
46
+ attribute is not present, the edge is considered to have
47
+ infinite capacity. Default value: 'capacity'.
48
+
49
+ residual : NetworkX graph
50
+ Residual network on which the algorithm is to be executed. If None, a
51
+ new residual network is created. Default value: None.
52
+
53
+ value_only : bool
54
+ If True compute only the value of the maximum flow. This parameter
55
+ will be ignored by this algorithm because it is not applicable.
56
+
57
+ cutoff : integer, float
58
+ If specified, the algorithm will terminate when the flow value reaches
59
+ or exceeds the cutoff. In this case, it may be unable to immediately
60
+ determine a minimum cut. Default value: None.
61
+
62
+ Returns
63
+ -------
64
+ R : NetworkX DiGraph
65
+ Residual network after computing the maximum flow.
66
+
67
+ Raises
68
+ ------
69
+ NetworkXError
70
+ The algorithm does not support MultiGraph and MultiDiGraph. If
71
+ the input graph is an instance of one of these two classes, a
72
+ NetworkXError is raised.
73
+
74
+ NetworkXUnbounded
75
+ If the graph has a path of infinite capacity, the value of a
76
+ feasible flow on the graph is unbounded above and the function
77
+ raises a NetworkXUnbounded.
78
+
79
+ See also
80
+ --------
81
+ :meth:`maximum_flow`
82
+ :meth:`minimum_cut`
83
+ :meth:`preflow_push`
84
+ :meth:`shortest_augmenting_path`
85
+
86
+ Notes
87
+ -----
88
+ The residual network :samp:`R` from an input graph :samp:`G` has the
89
+ same nodes as :samp:`G`. :samp:`R` is a DiGraph that contains a pair
90
+ of edges :samp:`(u, v)` and :samp:`(v, u)` iff :samp:`(u, v)` is not a
91
+ self-loop, and at least one of :samp:`(u, v)` and :samp:`(v, u)` exists
92
+ in :samp:`G`.
93
+
94
+ For each edge :samp:`(u, v)` in :samp:`R`, :samp:`R[u][v]['capacity']`
95
+ is equal to the capacity of :samp:`(u, v)` in :samp:`G` if it exists
96
+ in :samp:`G` or zero otherwise. If the capacity is infinite,
97
+ :samp:`R[u][v]['capacity']` will have a high arbitrary finite value
98
+ that does not affect the solution of the problem. This value is stored in
99
+ :samp:`R.graph['inf']`. For each edge :samp:`(u, v)` in :samp:`R`,
100
+ :samp:`R[u][v]['flow']` represents the flow function of :samp:`(u, v)` and
101
+ satisfies :samp:`R[u][v]['flow'] == -R[v][u]['flow']`.
102
+
103
+ The flow value, defined as the total flow into :samp:`t`, the sink, is
104
+ stored in :samp:`R.graph['flow_value']`. If :samp:`cutoff` is not
105
+ specified, reachability to :samp:`t` using only edges :samp:`(u, v)` such
106
+ that :samp:`R[u][v]['flow'] < R[u][v]['capacity']` induces a minimum
107
+ :samp:`s`-:samp:`t` cut.
108
+
109
+ Examples
110
+ --------
111
+ >>> from networkx.algorithms.flow import dinitz
112
+
113
+ The functions that implement flow algorithms and output a residual
114
+ network, such as this one, are not imported to the base NetworkX
115
+ namespace, so you have to explicitly import them from the flow package.
116
+
117
+ >>> G = nx.DiGraph()
118
+ >>> G.add_edge("x", "a", capacity=3.0)
119
+ >>> G.add_edge("x", "b", capacity=1.0)
120
+ >>> G.add_edge("a", "c", capacity=3.0)
121
+ >>> G.add_edge("b", "c", capacity=5.0)
122
+ >>> G.add_edge("b", "d", capacity=4.0)
123
+ >>> G.add_edge("d", "e", capacity=2.0)
124
+ >>> G.add_edge("c", "y", capacity=2.0)
125
+ >>> G.add_edge("e", "y", capacity=3.0)
126
+ >>> R = dinitz(G, "x", "y")
127
+ >>> flow_value = nx.maximum_flow_value(G, "x", "y")
128
+ >>> flow_value
129
+ 3.0
130
+ >>> flow_value == R.graph["flow_value"]
131
+ True
132
+
133
+ References
134
+ ----------
135
+ .. [1] Dinitz' Algorithm: The Original Version and Even's Version.
136
+ 2006. Yefim Dinitz. In Theoretical Computer Science. Lecture
137
+ Notes in Computer Science. Volume 3895. pp 218-240.
138
+ https://doi.org/10.1007/11685654_10
139
+
140
+ """
141
+ R = dinitz_impl(G, s, t, capacity, residual, cutoff)
142
+ R.graph["algorithm"] = "dinitz"
143
+ return R
144
+
145
+
146
+ def dinitz_impl(G, s, t, capacity, residual, cutoff):
147
+ if s not in G:
148
+ raise nx.NetworkXError(f"node {str(s)} not in graph")
149
+ if t not in G:
150
+ raise nx.NetworkXError(f"node {str(t)} not in graph")
151
+ if s == t:
152
+ raise nx.NetworkXError("source and sink are the same node")
153
+
154
+ if residual is None:
155
+ R = build_residual_network(G, capacity)
156
+ else:
157
+ R = residual
158
+
159
+ # Initialize/reset the residual network.
160
+ for u in R:
161
+ for e in R[u].values():
162
+ e["flow"] = 0
163
+
164
+ # Use an arbitrary high value as infinite. It is computed
165
+ # when building the residual network.
166
+ INF = R.graph["inf"]
167
+
168
+ if cutoff is None:
169
+ cutoff = INF
170
+
171
+ R_succ = R.succ
172
+ R_pred = R.pred
173
+
174
+ def breath_first_search():
175
+ parents = {}
176
+ queue = deque([s])
177
+ while queue:
178
+ if t in parents:
179
+ break
180
+ u = queue.popleft()
181
+ for v in R_succ[u]:
182
+ attr = R_succ[u][v]
183
+ if v not in parents and attr["capacity"] - attr["flow"] > 0:
184
+ parents[v] = u
185
+ queue.append(v)
186
+ return parents
187
+
188
+ def depth_first_search(parents):
189
+ """Build a path using DFS starting from the sink"""
190
+ path = []
191
+ u = t
192
+ flow = INF
193
+ while u != s:
194
+ path.append(u)
195
+ v = parents[u]
196
+ flow = min(flow, R_pred[u][v]["capacity"] - R_pred[u][v]["flow"])
197
+ u = v
198
+ path.append(s)
199
+ # Augment the flow along the path found
200
+ if flow > 0:
201
+ for u, v in pairwise(path):
202
+ R_pred[u][v]["flow"] += flow
203
+ R_pred[v][u]["flow"] -= flow
204
+ return flow
205
+
206
+ flow_value = 0
207
+ while flow_value < cutoff:
208
+ parents = breath_first_search()
209
+ if t not in parents:
210
+ break
211
+ this_flow = depth_first_search(parents)
212
+ if this_flow * 2 > INF:
213
+ raise nx.NetworkXUnbounded("Infinite capacity path, flow unbounded above.")
214
+ flow_value += this_flow
215
+
216
+ R.graph["flow_value"] = flow_value
217
+ return R
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/__init__.py ADDED
File without changes
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/__pycache__/test_gomory_hu.cpython-311.pyc ADDED
Binary file (9.78 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/flow/tests/test_maxflow_large_graph.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Maximum flow algorithms test suite on large graphs.
2
+ """
3
+
4
+ import bz2
5
+ import importlib.resources
6
+ import os
7
+ import pickle
8
+
9
+ import pytest
10
+
11
+ import networkx as nx
12
+ from networkx.algorithms.flow import (
13
+ boykov_kolmogorov,
14
+ build_flow_dict,
15
+ build_residual_network,
16
+ dinitz,
17
+ edmonds_karp,
18
+ preflow_push,
19
+ shortest_augmenting_path,
20
+ )
21
+
22
+ flow_funcs = [
23
+ boykov_kolmogorov,
24
+ dinitz,
25
+ edmonds_karp,
26
+ preflow_push,
27
+ shortest_augmenting_path,
28
+ ]
29
+
30
+
31
+ def gen_pyramid(N):
32
+ # This graph admits a flow of value 1 for which every arc is at
33
+ # capacity (except the arcs incident to the sink which have
34
+ # infinite capacity).
35
+ G = nx.DiGraph()
36
+
37
+ for i in range(N - 1):
38
+ cap = 1.0 / (i + 2)
39
+ for j in range(i + 1):
40
+ G.add_edge((i, j), (i + 1, j), capacity=cap)
41
+ cap = 1.0 / (i + 1) - cap
42
+ G.add_edge((i, j), (i + 1, j + 1), capacity=cap)
43
+ cap = 1.0 / (i + 2) - cap
44
+
45
+ for j in range(N):
46
+ G.add_edge((N - 1, j), "t")
47
+
48
+ return G
49
+
50
+
51
+ def read_graph(name):
52
+ fname = (
53
+ importlib.resources.files("networkx.algorithms.flow.tests")
54
+ / f"{name}.gpickle.bz2"
55
+ )
56
+
57
+ with bz2.BZ2File(fname, "rb") as f:
58
+ G = pickle.load(f)
59
+ return G
60
+
61
+
62
+ def validate_flows(G, s, t, soln_value, R, flow_func):
63
+ flow_value = R.graph["flow_value"]
64
+ flow_dict = build_flow_dict(G, R)
65
+ errmsg = f"Assertion failed in function: {flow_func.__name__}"
66
+ assert soln_value == flow_value, errmsg
67
+ assert set(G) == set(flow_dict), errmsg
68
+ for u in G:
69
+ assert set(G[u]) == set(flow_dict[u]), errmsg
70
+ excess = {u: 0 for u in flow_dict}
71
+ for u in flow_dict:
72
+ for v, flow in flow_dict[u].items():
73
+ assert flow <= G[u][v].get("capacity", float("inf")), errmsg
74
+ assert flow >= 0, errmsg
75
+ excess[u] -= flow
76
+ excess[v] += flow
77
+ for u, exc in excess.items():
78
+ if u == s:
79
+ assert exc == -soln_value, errmsg
80
+ elif u == t:
81
+ assert exc == soln_value, errmsg
82
+ else:
83
+ assert exc == 0, errmsg
84
+
85
+
86
+ class TestMaxflowLargeGraph:
87
+ def test_complete_graph(self):
88
+ N = 50
89
+ G = nx.complete_graph(N)
90
+ nx.set_edge_attributes(G, 5, "capacity")
91
+ R = build_residual_network(G, "capacity")
92
+ kwargs = {"residual": R}
93
+
94
+ for flow_func in flow_funcs:
95
+ kwargs["flow_func"] = flow_func
96
+ errmsg = f"Assertion failed in function: {flow_func.__name__}"
97
+ flow_value = nx.maximum_flow_value(G, 1, 2, **kwargs)
98
+ assert flow_value == 5 * (N - 1), errmsg
99
+
100
+ def test_pyramid(self):
101
+ N = 10
102
+ # N = 100 # this gives a graph with 5051 nodes
103
+ G = gen_pyramid(N)
104
+ R = build_residual_network(G, "capacity")
105
+ kwargs = {"residual": R}
106
+
107
+ for flow_func in flow_funcs:
108
+ kwargs["flow_func"] = flow_func
109
+ errmsg = f"Assertion failed in function: {flow_func.__name__}"
110
+ flow_value = nx.maximum_flow_value(G, (0, 0), "t", **kwargs)
111
+ assert flow_value == pytest.approx(1.0, abs=1e-7)
112
+
113
+ def test_gl1(self):
114
+ G = read_graph("gl1")
115
+ s = 1
116
+ t = len(G)
117
+ R = build_residual_network(G, "capacity")
118
+ kwargs = {"residual": R}
119
+
120
+ # do one flow_func to save time
121
+ flow_func = flow_funcs[0]
122
+ validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs), flow_func)
123
+
124
+ # for flow_func in flow_funcs:
125
+ # validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs),
126
+ # flow_func)
127
+
128
+ @pytest.mark.slow
129
+ def test_gw1(self):
130
+ G = read_graph("gw1")
131
+ s = 1
132
+ t = len(G)
133
+ R = build_residual_network(G, "capacity")
134
+ kwargs = {"residual": R}
135
+
136
+ for flow_func in flow_funcs:
137
+ validate_flows(G, s, t, 1202018, flow_func(G, s, t, **kwargs), flow_func)
138
+
139
+ def test_wlm3(self):
140
+ G = read_graph("wlm3")
141
+ s = 1
142
+ t = len(G)
143
+ R = build_residual_network(G, "capacity")
144
+ kwargs = {"residual": R}
145
+
146
+ # do one flow_func to save time
147
+ flow_func = flow_funcs[0]
148
+ validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs), flow_func)
149
+
150
+ # for flow_func in flow_funcs:
151
+ # validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs),
152
+ # flow_func)
153
+
154
+ def test_preflow_push_global_relabel(self):
155
+ G = read_graph("gw1")
156
+ R = preflow_push(G, 1, len(G), global_relabel_freq=50)
157
+ assert R.graph["flow_value"] == 1202018
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from networkx.algorithms.link_analysis.hits_alg import *
2
+ from networkx.algorithms.link_analysis.pagerank_alg import *
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/__pycache__/pagerank_alg.cpython-311.pyc ADDED
Binary file (22.2 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/hits_alg.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Hubs and authorities analysis of graph structure.
2
+ """
3
+ import networkx as nx
4
+
5
+ __all__ = ["hits"]
6
+
7
+
8
+ @nx._dispatch
9
+ def hits(G, max_iter=100, tol=1.0e-8, nstart=None, normalized=True):
10
+ """Returns HITS hubs and authorities values for nodes.
11
+
12
+ The HITS algorithm computes two numbers for a node.
13
+ Authorities estimates the node value based on the incoming links.
14
+ Hubs estimates the node value based on outgoing links.
15
+
16
+ Parameters
17
+ ----------
18
+ G : graph
19
+ A NetworkX graph
20
+
21
+ max_iter : integer, optional
22
+ Maximum number of iterations in power method.
23
+
24
+ tol : float, optional
25
+ Error tolerance used to check convergence in power method iteration.
26
+
27
+ nstart : dictionary, optional
28
+ Starting value of each node for power method iteration.
29
+
30
+ normalized : bool (default=True)
31
+ Normalize results by the sum of all of the values.
32
+
33
+ Returns
34
+ -------
35
+ (hubs,authorities) : two-tuple of dictionaries
36
+ Two dictionaries keyed by node containing the hub and authority
37
+ values.
38
+
39
+ Raises
40
+ ------
41
+ PowerIterationFailedConvergence
42
+ If the algorithm fails to converge to the specified tolerance
43
+ within the specified number of iterations of the power iteration
44
+ method.
45
+
46
+ Examples
47
+ --------
48
+ >>> G = nx.path_graph(4)
49
+ >>> h, a = nx.hits(G)
50
+
51
+ Notes
52
+ -----
53
+ The eigenvector calculation is done by the power iteration method
54
+ and has no guarantee of convergence. The iteration will stop
55
+ after max_iter iterations or an error tolerance of
56
+ number_of_nodes(G)*tol has been reached.
57
+
58
+ The HITS algorithm was designed for directed graphs but this
59
+ algorithm does not check if the input graph is directed and will
60
+ execute on undirected graphs.
61
+
62
+ References
63
+ ----------
64
+ .. [1] A. Langville and C. Meyer,
65
+ "A survey of eigenvector methods of web information retrieval."
66
+ http://citeseer.ist.psu.edu/713792.html
67
+ .. [2] Jon Kleinberg,
68
+ Authoritative sources in a hyperlinked environment
69
+ Journal of the ACM 46 (5): 604-32, 1999.
70
+ doi:10.1145/324133.324140.
71
+ http://www.cs.cornell.edu/home/kleinber/auth.pdf.
72
+ """
73
+ import numpy as np
74
+ import scipy as sp
75
+
76
+ if len(G) == 0:
77
+ return {}, {}
78
+ A = nx.adjacency_matrix(G, nodelist=list(G), dtype=float)
79
+
80
+ if nstart is None:
81
+ _, _, vt = sp.sparse.linalg.svds(A, k=1, maxiter=max_iter, tol=tol)
82
+ else:
83
+ nstart = np.array(list(nstart.values()))
84
+ _, _, vt = sp.sparse.linalg.svds(A, k=1, v0=nstart, maxiter=max_iter, tol=tol)
85
+
86
+ a = vt.flatten().real
87
+ h = A @ a
88
+ if normalized:
89
+ h /= h.sum()
90
+ a /= a.sum()
91
+ hubs = dict(zip(G, map(float, h)))
92
+ authorities = dict(zip(G, map(float, a)))
93
+ return hubs, authorities
94
+
95
+
96
+ def _hits_python(G, max_iter=100, tol=1.0e-8, nstart=None, normalized=True):
97
+ if isinstance(G, (nx.MultiGraph, nx.MultiDiGraph)):
98
+ raise Exception("hits() not defined for graphs with multiedges.")
99
+ if len(G) == 0:
100
+ return {}, {}
101
+ # choose fixed starting vector if not given
102
+ if nstart is None:
103
+ h = dict.fromkeys(G, 1.0 / G.number_of_nodes())
104
+ else:
105
+ h = nstart
106
+ # normalize starting vector
107
+ s = 1.0 / sum(h.values())
108
+ for k in h:
109
+ h[k] *= s
110
+ for _ in range(max_iter): # power iteration: make up to max_iter iterations
111
+ hlast = h
112
+ h = dict.fromkeys(hlast.keys(), 0)
113
+ a = dict.fromkeys(hlast.keys(), 0)
114
+ # this "matrix multiply" looks odd because it is
115
+ # doing a left multiply a^T=hlast^T*G
116
+ for n in h:
117
+ for nbr in G[n]:
118
+ a[nbr] += hlast[n] * G[n][nbr].get("weight", 1)
119
+ # now multiply h=Ga
120
+ for n in h:
121
+ for nbr in G[n]:
122
+ h[n] += a[nbr] * G[n][nbr].get("weight", 1)
123
+ # normalize vector
124
+ s = 1.0 / max(h.values())
125
+ for n in h:
126
+ h[n] *= s
127
+ # normalize vector
128
+ s = 1.0 / max(a.values())
129
+ for n in a:
130
+ a[n] *= s
131
+ # check convergence, l1 norm
132
+ err = sum(abs(h[n] - hlast[n]) for n in h)
133
+ if err < tol:
134
+ break
135
+ else:
136
+ raise nx.PowerIterationFailedConvergence(max_iter)
137
+ if normalized:
138
+ s = 1.0 / sum(a.values())
139
+ for n in a:
140
+ a[n] *= s
141
+ s = 1.0 / sum(h.values())
142
+ for n in h:
143
+ h[n] *= s
144
+ return h, a
145
+
146
+
147
+ def _hits_numpy(G, normalized=True):
148
+ """Returns HITS hubs and authorities values for nodes.
149
+
150
+ The HITS algorithm computes two numbers for a node.
151
+ Authorities estimates the node value based on the incoming links.
152
+ Hubs estimates the node value based on outgoing links.
153
+
154
+ Parameters
155
+ ----------
156
+ G : graph
157
+ A NetworkX graph
158
+
159
+ normalized : bool (default=True)
160
+ Normalize results by the sum of all of the values.
161
+
162
+ Returns
163
+ -------
164
+ (hubs,authorities) : two-tuple of dictionaries
165
+ Two dictionaries keyed by node containing the hub and authority
166
+ values.
167
+
168
+ Examples
169
+ --------
170
+ >>> G = nx.path_graph(4)
171
+
172
+ The `hubs` and `authorities` are given by the eigenvectors corresponding to the
173
+ maximum eigenvalues of the hubs_matrix and the authority_matrix, respectively.
174
+
175
+ The ``hubs`` and ``authority`` matrices are computed from the adjacency
176
+ matrix:
177
+
178
+ >>> adj_ary = nx.to_numpy_array(G)
179
+ >>> hubs_matrix = adj_ary @ adj_ary.T
180
+ >>> authority_matrix = adj_ary.T @ adj_ary
181
+
182
+ `_hits_numpy` maps the eigenvector corresponding to the maximum eigenvalue
183
+ of the respective matrices to the nodes in `G`:
184
+
185
+ >>> from networkx.algorithms.link_analysis.hits_alg import _hits_numpy
186
+ >>> hubs, authority = _hits_numpy(G)
187
+
188
+ Notes
189
+ -----
190
+ The eigenvector calculation uses NumPy's interface to LAPACK.
191
+
192
+ The HITS algorithm was designed for directed graphs but this
193
+ algorithm does not check if the input graph is directed and will
194
+ execute on undirected graphs.
195
+
196
+ References
197
+ ----------
198
+ .. [1] A. Langville and C. Meyer,
199
+ "A survey of eigenvector methods of web information retrieval."
200
+ http://citeseer.ist.psu.edu/713792.html
201
+ .. [2] Jon Kleinberg,
202
+ Authoritative sources in a hyperlinked environment
203
+ Journal of the ACM 46 (5): 604-32, 1999.
204
+ doi:10.1145/324133.324140.
205
+ http://www.cs.cornell.edu/home/kleinber/auth.pdf.
206
+ """
207
+ import numpy as np
208
+
209
+ if len(G) == 0:
210
+ return {}, {}
211
+ adj_ary = nx.to_numpy_array(G)
212
+ # Hub matrix
213
+ H = adj_ary @ adj_ary.T
214
+ e, ev = np.linalg.eig(H)
215
+ h = ev[:, np.argmax(e)] # eigenvector corresponding to the maximum eigenvalue
216
+ # Authority matrix
217
+ A = adj_ary.T @ adj_ary
218
+ e, ev = np.linalg.eig(A)
219
+ a = ev[:, np.argmax(e)] # eigenvector corresponding to the maximum eigenvalue
220
+ if normalized:
221
+ h /= h.sum()
222
+ a /= a.sum()
223
+ else:
224
+ h /= h.max()
225
+ a /= a.max()
226
+ hubs = dict(zip(G, map(float, h)))
227
+ authorities = dict(zip(G, map(float, a)))
228
+ return hubs, authorities
229
+
230
+
231
+ def _hits_scipy(G, max_iter=100, tol=1.0e-6, nstart=None, normalized=True):
232
+ """Returns HITS hubs and authorities values for nodes.
233
+
234
+
235
+ The HITS algorithm computes two numbers for a node.
236
+ Authorities estimates the node value based on the incoming links.
237
+ Hubs estimates the node value based on outgoing links.
238
+
239
+ Parameters
240
+ ----------
241
+ G : graph
242
+ A NetworkX graph
243
+
244
+ max_iter : integer, optional
245
+ Maximum number of iterations in power method.
246
+
247
+ tol : float, optional
248
+ Error tolerance used to check convergence in power method iteration.
249
+
250
+ nstart : dictionary, optional
251
+ Starting value of each node for power method iteration.
252
+
253
+ normalized : bool (default=True)
254
+ Normalize results by the sum of all of the values.
255
+
256
+ Returns
257
+ -------
258
+ (hubs,authorities) : two-tuple of dictionaries
259
+ Two dictionaries keyed by node containing the hub and authority
260
+ values.
261
+
262
+ Examples
263
+ --------
264
+ >>> from networkx.algorithms.link_analysis.hits_alg import _hits_scipy
265
+ >>> G = nx.path_graph(4)
266
+ >>> h, a = _hits_scipy(G)
267
+
268
+ Notes
269
+ -----
270
+ This implementation uses SciPy sparse matrices.
271
+
272
+ The eigenvector calculation is done by the power iteration method
273
+ and has no guarantee of convergence. The iteration will stop
274
+ after max_iter iterations or an error tolerance of
275
+ number_of_nodes(G)*tol has been reached.
276
+
277
+ The HITS algorithm was designed for directed graphs but this
278
+ algorithm does not check if the input graph is directed and will
279
+ execute on undirected graphs.
280
+
281
+ Raises
282
+ ------
283
+ PowerIterationFailedConvergence
284
+ If the algorithm fails to converge to the specified tolerance
285
+ within the specified number of iterations of the power iteration
286
+ method.
287
+
288
+ References
289
+ ----------
290
+ .. [1] A. Langville and C. Meyer,
291
+ "A survey of eigenvector methods of web information retrieval."
292
+ http://citeseer.ist.psu.edu/713792.html
293
+ .. [2] Jon Kleinberg,
294
+ Authoritative sources in a hyperlinked environment
295
+ Journal of the ACM 46 (5): 604-632, 1999.
296
+ doi:10.1145/324133.324140.
297
+ http://www.cs.cornell.edu/home/kleinber/auth.pdf.
298
+ """
299
+ import numpy as np
300
+
301
+ if len(G) == 0:
302
+ return {}, {}
303
+ A = nx.to_scipy_sparse_array(G, nodelist=list(G))
304
+ (n, _) = A.shape # should be square
305
+ ATA = A.T @ A # authority matrix
306
+ # choose fixed starting vector if not given
307
+ if nstart is None:
308
+ x = np.ones((n, 1)) / n
309
+ else:
310
+ x = np.array([nstart.get(n, 0) for n in list(G)], dtype=float)
311
+ x /= x.sum()
312
+
313
+ # power iteration on authority matrix
314
+ i = 0
315
+ while True:
316
+ xlast = x
317
+ x = ATA @ x
318
+ x /= x.max()
319
+ # check convergence, l1 norm
320
+ err = np.absolute(x - xlast).sum()
321
+ if err < tol:
322
+ break
323
+ if i > max_iter:
324
+ raise nx.PowerIterationFailedConvergence(max_iter)
325
+ i += 1
326
+
327
+ a = x.flatten()
328
+ h = A @ a
329
+ if normalized:
330
+ h /= h.sum()
331
+ a /= a.sum()
332
+ hubs = dict(zip(G, map(float, h)))
333
+ authorities = dict(zip(G, map(float, a)))
334
+ return hubs, authorities
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/pagerank_alg.py ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PageRank analysis of graph structure. """
2
+ from warnings import warn
3
+
4
+ import networkx as nx
5
+
6
+ __all__ = ["pagerank", "google_matrix"]
7
+
8
+
9
+ @nx._dispatch(edge_attrs="weight")
10
+ def pagerank(
11
+ G,
12
+ alpha=0.85,
13
+ personalization=None,
14
+ max_iter=100,
15
+ tol=1.0e-6,
16
+ nstart=None,
17
+ weight="weight",
18
+ dangling=None,
19
+ ):
20
+ """Returns the PageRank of the nodes in the graph.
21
+
22
+ PageRank computes a ranking of the nodes in the graph G based on
23
+ the structure of the incoming links. It was originally designed as
24
+ an algorithm to rank web pages.
25
+
26
+ Parameters
27
+ ----------
28
+ G : graph
29
+ A NetworkX graph. Undirected graphs will be converted to a directed
30
+ graph with two directed edges for each undirected edge.
31
+
32
+ alpha : float, optional
33
+ Damping parameter for PageRank, default=0.85.
34
+
35
+ personalization: dict, optional
36
+ The "personalization vector" consisting of a dictionary with a
37
+ key some subset of graph nodes and personalization value each of those.
38
+ At least one personalization value must be non-zero.
39
+ If not specified, a nodes personalization value will be zero.
40
+ By default, a uniform distribution is used.
41
+
42
+ max_iter : integer, optional
43
+ Maximum number of iterations in power method eigenvalue solver.
44
+
45
+ tol : float, optional
46
+ Error tolerance used to check convergence in power method solver.
47
+ The iteration will stop after a tolerance of ``len(G) * tol`` is reached.
48
+
49
+ nstart : dictionary, optional
50
+ Starting value of PageRank iteration for each node.
51
+
52
+ weight : key, optional
53
+ Edge data key to use as weight. If None weights are set to 1.
54
+
55
+ dangling: dict, optional
56
+ The outedges to be assigned to any "dangling" nodes, i.e., nodes without
57
+ any outedges. The dict key is the node the outedge points to and the dict
58
+ value is the weight of that outedge. By default, dangling nodes are given
59
+ outedges according to the personalization vector (uniform if not
60
+ specified). This must be selected to result in an irreducible transition
61
+ matrix (see notes under google_matrix). It may be common to have the
62
+ dangling dict to be the same as the personalization dict.
63
+
64
+
65
+ Returns
66
+ -------
67
+ pagerank : dictionary
68
+ Dictionary of nodes with PageRank as value
69
+
70
+ Examples
71
+ --------
72
+ >>> G = nx.DiGraph(nx.path_graph(4))
73
+ >>> pr = nx.pagerank(G, alpha=0.9)
74
+
75
+ Notes
76
+ -----
77
+ The eigenvector calculation is done by the power iteration method
78
+ and has no guarantee of convergence. The iteration will stop after
79
+ an error tolerance of ``len(G) * tol`` has been reached. If the
80
+ number of iterations exceed `max_iter`, a
81
+ :exc:`networkx.exception.PowerIterationFailedConvergence` exception
82
+ is raised.
83
+
84
+ The PageRank algorithm was designed for directed graphs but this
85
+ algorithm does not check if the input graph is directed and will
86
+ execute on undirected graphs by converting each edge in the
87
+ directed graph to two edges.
88
+
89
+ See Also
90
+ --------
91
+ google_matrix
92
+
93
+ Raises
94
+ ------
95
+ PowerIterationFailedConvergence
96
+ If the algorithm fails to converge to the specified tolerance
97
+ within the specified number of iterations of the power iteration
98
+ method.
99
+
100
+ References
101
+ ----------
102
+ .. [1] A. Langville and C. Meyer,
103
+ "A survey of eigenvector methods of web information retrieval."
104
+ http://citeseer.ist.psu.edu/713792.html
105
+ .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry,
106
+ The PageRank citation ranking: Bringing order to the Web. 1999
107
+ http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf
108
+
109
+ """
110
+ return _pagerank_scipy(
111
+ G, alpha, personalization, max_iter, tol, nstart, weight, dangling
112
+ )
113
+
114
+
115
+ def _pagerank_python(
116
+ G,
117
+ alpha=0.85,
118
+ personalization=None,
119
+ max_iter=100,
120
+ tol=1.0e-6,
121
+ nstart=None,
122
+ weight="weight",
123
+ dangling=None,
124
+ ):
125
+ if len(G) == 0:
126
+ return {}
127
+
128
+ D = G.to_directed()
129
+
130
+ # Create a copy in (right) stochastic form
131
+ W = nx.stochastic_graph(D, weight=weight)
132
+ N = W.number_of_nodes()
133
+
134
+ # Choose fixed starting vector if not given
135
+ if nstart is None:
136
+ x = dict.fromkeys(W, 1.0 / N)
137
+ else:
138
+ # Normalized nstart vector
139
+ s = sum(nstart.values())
140
+ x = {k: v / s for k, v in nstart.items()}
141
+
142
+ if personalization is None:
143
+ # Assign uniform personalization vector if not given
144
+ p = dict.fromkeys(W, 1.0 / N)
145
+ else:
146
+ s = sum(personalization.values())
147
+ p = {k: v / s for k, v in personalization.items()}
148
+
149
+ if dangling is None:
150
+ # Use personalization vector if dangling vector not specified
151
+ dangling_weights = p
152
+ else:
153
+ s = sum(dangling.values())
154
+ dangling_weights = {k: v / s for k, v in dangling.items()}
155
+ dangling_nodes = [n for n in W if W.out_degree(n, weight=weight) == 0.0]
156
+
157
+ # power iteration: make up to max_iter iterations
158
+ for _ in range(max_iter):
159
+ xlast = x
160
+ x = dict.fromkeys(xlast.keys(), 0)
161
+ danglesum = alpha * sum(xlast[n] for n in dangling_nodes)
162
+ for n in x:
163
+ # this matrix multiply looks odd because it is
164
+ # doing a left multiply x^T=xlast^T*W
165
+ for _, nbr, wt in W.edges(n, data=weight):
166
+ x[nbr] += alpha * xlast[n] * wt
167
+ x[n] += danglesum * dangling_weights.get(n, 0) + (1.0 - alpha) * p.get(n, 0)
168
+ # check convergence, l1 norm
169
+ err = sum(abs(x[n] - xlast[n]) for n in x)
170
+ if err < N * tol:
171
+ return x
172
+ raise nx.PowerIterationFailedConvergence(max_iter)
173
+
174
+
175
+ @nx._dispatch(edge_attrs="weight")
176
+ def google_matrix(
177
+ G, alpha=0.85, personalization=None, nodelist=None, weight="weight", dangling=None
178
+ ):
179
+ """Returns the Google matrix of the graph.
180
+
181
+ Parameters
182
+ ----------
183
+ G : graph
184
+ A NetworkX graph. Undirected graphs will be converted to a directed
185
+ graph with two directed edges for each undirected edge.
186
+
187
+ alpha : float
188
+ The damping factor.
189
+
190
+ personalization: dict, optional
191
+ The "personalization vector" consisting of a dictionary with a
192
+ key some subset of graph nodes and personalization value each of those.
193
+ At least one personalization value must be non-zero.
194
+ If not specified, a nodes personalization value will be zero.
195
+ By default, a uniform distribution is used.
196
+
197
+ nodelist : list, optional
198
+ The rows and columns are ordered according to the nodes in nodelist.
199
+ If nodelist is None, then the ordering is produced by G.nodes().
200
+
201
+ weight : key, optional
202
+ Edge data key to use as weight. If None weights are set to 1.
203
+
204
+ dangling: dict, optional
205
+ The outedges to be assigned to any "dangling" nodes, i.e., nodes without
206
+ any outedges. The dict key is the node the outedge points to and the dict
207
+ value is the weight of that outedge. By default, dangling nodes are given
208
+ outedges according to the personalization vector (uniform if not
209
+ specified) This must be selected to result in an irreducible transition
210
+ matrix (see notes below). It may be common to have the dangling dict to
211
+ be the same as the personalization dict.
212
+
213
+ Returns
214
+ -------
215
+ A : 2D NumPy ndarray
216
+ Google matrix of the graph
217
+
218
+ Notes
219
+ -----
220
+ The array returned represents the transition matrix that describes the
221
+ Markov chain used in PageRank. For PageRank to converge to a unique
222
+ solution (i.e., a unique stationary distribution in a Markov chain), the
223
+ transition matrix must be irreducible. In other words, it must be that
224
+ there exists a path between every pair of nodes in the graph, or else there
225
+ is the potential of "rank sinks."
226
+
227
+ This implementation works with Multi(Di)Graphs. For multigraphs the
228
+ weight between two nodes is set to be the sum of all edge weights
229
+ between those nodes.
230
+
231
+ See Also
232
+ --------
233
+ pagerank
234
+ """
235
+ import numpy as np
236
+
237
+ if nodelist is None:
238
+ nodelist = list(G)
239
+
240
+ A = nx.to_numpy_array(G, nodelist=nodelist, weight=weight)
241
+ N = len(G)
242
+ if N == 0:
243
+ return A
244
+
245
+ # Personalization vector
246
+ if personalization is None:
247
+ p = np.repeat(1.0 / N, N)
248
+ else:
249
+ p = np.array([personalization.get(n, 0) for n in nodelist], dtype=float)
250
+ if p.sum() == 0:
251
+ raise ZeroDivisionError
252
+ p /= p.sum()
253
+
254
+ # Dangling nodes
255
+ if dangling is None:
256
+ dangling_weights = p
257
+ else:
258
+ # Convert the dangling dictionary into an array in nodelist order
259
+ dangling_weights = np.array([dangling.get(n, 0) for n in nodelist], dtype=float)
260
+ dangling_weights /= dangling_weights.sum()
261
+ dangling_nodes = np.where(A.sum(axis=1) == 0)[0]
262
+
263
+ # Assign dangling_weights to any dangling nodes (nodes with no out links)
264
+ A[dangling_nodes] = dangling_weights
265
+
266
+ A /= A.sum(axis=1)[:, np.newaxis] # Normalize rows to sum to 1
267
+
268
+ return alpha * A + (1 - alpha) * p
269
+
270
+
271
+ def _pagerank_numpy(
272
+ G, alpha=0.85, personalization=None, weight="weight", dangling=None
273
+ ):
274
+ """Returns the PageRank of the nodes in the graph.
275
+
276
+ PageRank computes a ranking of the nodes in the graph G based on
277
+ the structure of the incoming links. It was originally designed as
278
+ an algorithm to rank web pages.
279
+
280
+ Parameters
281
+ ----------
282
+ G : graph
283
+ A NetworkX graph. Undirected graphs will be converted to a directed
284
+ graph with two directed edges for each undirected edge.
285
+
286
+ alpha : float, optional
287
+ Damping parameter for PageRank, default=0.85.
288
+
289
+ personalization: dict, optional
290
+ The "personalization vector" consisting of a dictionary with a
291
+ key some subset of graph nodes and personalization value each of those.
292
+ At least one personalization value must be non-zero.
293
+ If not specified, a nodes personalization value will be zero.
294
+ By default, a uniform distribution is used.
295
+
296
+ weight : key, optional
297
+ Edge data key to use as weight. If None weights are set to 1.
298
+
299
+ dangling: dict, optional
300
+ The outedges to be assigned to any "dangling" nodes, i.e., nodes without
301
+ any outedges. The dict key is the node the outedge points to and the dict
302
+ value is the weight of that outedge. By default, dangling nodes are given
303
+ outedges according to the personalization vector (uniform if not
304
+ specified) This must be selected to result in an irreducible transition
305
+ matrix (see notes under google_matrix). It may be common to have the
306
+ dangling dict to be the same as the personalization dict.
307
+
308
+ Returns
309
+ -------
310
+ pagerank : dictionary
311
+ Dictionary of nodes with PageRank as value.
312
+
313
+ Examples
314
+ --------
315
+ >>> from networkx.algorithms.link_analysis.pagerank_alg import _pagerank_numpy
316
+ >>> G = nx.DiGraph(nx.path_graph(4))
317
+ >>> pr = _pagerank_numpy(G, alpha=0.9)
318
+
319
+ Notes
320
+ -----
321
+ The eigenvector calculation uses NumPy's interface to the LAPACK
322
+ eigenvalue solvers. This will be the fastest and most accurate
323
+ for small graphs.
324
+
325
+ This implementation works with Multi(Di)Graphs. For multigraphs the
326
+ weight between two nodes is set to be the sum of all edge weights
327
+ between those nodes.
328
+
329
+ See Also
330
+ --------
331
+ pagerank, google_matrix
332
+
333
+ References
334
+ ----------
335
+ .. [1] A. Langville and C. Meyer,
336
+ "A survey of eigenvector methods of web information retrieval."
337
+ http://citeseer.ist.psu.edu/713792.html
338
+ .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry,
339
+ The PageRank citation ranking: Bringing order to the Web. 1999
340
+ http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf
341
+ """
342
+ import numpy as np
343
+
344
+ if len(G) == 0:
345
+ return {}
346
+ M = google_matrix(
347
+ G, alpha, personalization=personalization, weight=weight, dangling=dangling
348
+ )
349
+ # use numpy LAPACK solver
350
+ eigenvalues, eigenvectors = np.linalg.eig(M.T)
351
+ ind = np.argmax(eigenvalues)
352
+ # eigenvector of largest eigenvalue is at ind, normalized
353
+ largest = np.array(eigenvectors[:, ind]).flatten().real
354
+ norm = largest.sum()
355
+ return dict(zip(G, map(float, largest / norm)))
356
+
357
+
358
+ def _pagerank_scipy(
359
+ G,
360
+ alpha=0.85,
361
+ personalization=None,
362
+ max_iter=100,
363
+ tol=1.0e-6,
364
+ nstart=None,
365
+ weight="weight",
366
+ dangling=None,
367
+ ):
368
+ """Returns the PageRank of the nodes in the graph.
369
+
370
+ PageRank computes a ranking of the nodes in the graph G based on
371
+ the structure of the incoming links. It was originally designed as
372
+ an algorithm to rank web pages.
373
+
374
+ Parameters
375
+ ----------
376
+ G : graph
377
+ A NetworkX graph. Undirected graphs will be converted to a directed
378
+ graph with two directed edges for each undirected edge.
379
+
380
+ alpha : float, optional
381
+ Damping parameter for PageRank, default=0.85.
382
+
383
+ personalization: dict, optional
384
+ The "personalization vector" consisting of a dictionary with a
385
+ key some subset of graph nodes and personalization value each of those.
386
+ At least one personalization value must be non-zero.
387
+ If not specified, a nodes personalization value will be zero.
388
+ By default, a uniform distribution is used.
389
+
390
+ max_iter : integer, optional
391
+ Maximum number of iterations in power method eigenvalue solver.
392
+
393
+ tol : float, optional
394
+ Error tolerance used to check convergence in power method solver.
395
+ The iteration will stop after a tolerance of ``len(G) * tol`` is reached.
396
+
397
+ nstart : dictionary, optional
398
+ Starting value of PageRank iteration for each node.
399
+
400
+ weight : key, optional
401
+ Edge data key to use as weight. If None weights are set to 1.
402
+
403
+ dangling: dict, optional
404
+ The outedges to be assigned to any "dangling" nodes, i.e., nodes without
405
+ any outedges. The dict key is the node the outedge points to and the dict
406
+ value is the weight of that outedge. By default, dangling nodes are given
407
+ outedges according to the personalization vector (uniform if not
408
+ specified) This must be selected to result in an irreducible transition
409
+ matrix (see notes under google_matrix). It may be common to have the
410
+ dangling dict to be the same as the personalization dict.
411
+
412
+ Returns
413
+ -------
414
+ pagerank : dictionary
415
+ Dictionary of nodes with PageRank as value
416
+
417
+ Examples
418
+ --------
419
+ >>> from networkx.algorithms.link_analysis.pagerank_alg import _pagerank_scipy
420
+ >>> G = nx.DiGraph(nx.path_graph(4))
421
+ >>> pr = _pagerank_scipy(G, alpha=0.9)
422
+
423
+ Notes
424
+ -----
425
+ The eigenvector calculation uses power iteration with a SciPy
426
+ sparse matrix representation.
427
+
428
+ This implementation works with Multi(Di)Graphs. For multigraphs the
429
+ weight between two nodes is set to be the sum of all edge weights
430
+ between those nodes.
431
+
432
+ See Also
433
+ --------
434
+ pagerank
435
+
436
+ Raises
437
+ ------
438
+ PowerIterationFailedConvergence
439
+ If the algorithm fails to converge to the specified tolerance
440
+ within the specified number of iterations of the power iteration
441
+ method.
442
+
443
+ References
444
+ ----------
445
+ .. [1] A. Langville and C. Meyer,
446
+ "A survey of eigenvector methods of web information retrieval."
447
+ http://citeseer.ist.psu.edu/713792.html
448
+ .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry,
449
+ The PageRank citation ranking: Bringing order to the Web. 1999
450
+ http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf
451
+ """
452
+ import numpy as np
453
+ import scipy as sp
454
+
455
+ N = len(G)
456
+ if N == 0:
457
+ return {}
458
+
459
+ nodelist = list(G)
460
+ A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, dtype=float)
461
+ S = A.sum(axis=1)
462
+ S[S != 0] = 1.0 / S[S != 0]
463
+ # TODO: csr_array
464
+ Q = sp.sparse.csr_array(sp.sparse.spdiags(S.T, 0, *A.shape))
465
+ A = Q @ A
466
+
467
+ # initial vector
468
+ if nstart is None:
469
+ x = np.repeat(1.0 / N, N)
470
+ else:
471
+ x = np.array([nstart.get(n, 0) for n in nodelist], dtype=float)
472
+ x /= x.sum()
473
+
474
+ # Personalization vector
475
+ if personalization is None:
476
+ p = np.repeat(1.0 / N, N)
477
+ else:
478
+ p = np.array([personalization.get(n, 0) for n in nodelist], dtype=float)
479
+ if p.sum() == 0:
480
+ raise ZeroDivisionError
481
+ p /= p.sum()
482
+ # Dangling nodes
483
+ if dangling is None:
484
+ dangling_weights = p
485
+ else:
486
+ # Convert the dangling dictionary into an array in nodelist order
487
+ dangling_weights = np.array([dangling.get(n, 0) for n in nodelist], dtype=float)
488
+ dangling_weights /= dangling_weights.sum()
489
+ is_dangling = np.where(S == 0)[0]
490
+
491
+ # power iteration: make up to max_iter iterations
492
+ for _ in range(max_iter):
493
+ xlast = x
494
+ x = alpha * (x @ A + sum(x[is_dangling]) * dangling_weights) + (1 - alpha) * p
495
+ # check convergence, l1 norm
496
+ err = np.absolute(x - xlast).sum()
497
+ if err < N * tol:
498
+ return dict(zip(nodelist, map(float, x)))
499
+ raise nx.PowerIterationFailedConvergence(max_iter)
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (240 Bytes). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/__pycache__/test_hits.cpython-311.pyc ADDED
Binary file (6.4 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/link_analysis/tests/test_pagerank.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+ from networkx.classes.tests import dispatch_interface
7
+
8
+ np = pytest.importorskip("numpy")
9
+ pytest.importorskip("scipy")
10
+
11
+ from networkx.algorithms.link_analysis.pagerank_alg import (
12
+ _pagerank_numpy,
13
+ _pagerank_python,
14
+ _pagerank_scipy,
15
+ )
16
+
17
+ # Example from
18
+ # A. Langville and C. Meyer, "A survey of eigenvector methods of web
19
+ # information retrieval." http://citeseer.ist.psu.edu/713792.html
20
+
21
+
22
+ class TestPageRank:
23
+ @classmethod
24
+ def setup_class(cls):
25
+ G = nx.DiGraph()
26
+ edges = [
27
+ (1, 2),
28
+ (1, 3),
29
+ # 2 is a dangling node
30
+ (3, 1),
31
+ (3, 2),
32
+ (3, 5),
33
+ (4, 5),
34
+ (4, 6),
35
+ (5, 4),
36
+ (5, 6),
37
+ (6, 4),
38
+ ]
39
+ G.add_edges_from(edges)
40
+ cls.G = G
41
+ cls.G.pagerank = dict(
42
+ zip(
43
+ sorted(G),
44
+ [
45
+ 0.03721197,
46
+ 0.05395735,
47
+ 0.04150565,
48
+ 0.37508082,
49
+ 0.20599833,
50
+ 0.28624589,
51
+ ],
52
+ )
53
+ )
54
+ cls.dangling_node_index = 1
55
+ cls.dangling_edges = {1: 2, 2: 3, 3: 0, 4: 0, 5: 0, 6: 0}
56
+ cls.G.dangling_pagerank = dict(
57
+ zip(
58
+ sorted(G),
59
+ [0.10844518, 0.18618601, 0.0710892, 0.2683668, 0.15919783, 0.20671497],
60
+ )
61
+ )
62
+
63
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python))
64
+ def test_pagerank(self, alg):
65
+ G = self.G
66
+ p = alg(G, alpha=0.9, tol=1.0e-08)
67
+ for n in G:
68
+ assert p[n] == pytest.approx(G.pagerank[n], abs=1e-4)
69
+
70
+ nstart = {n: random.random() for n in G}
71
+ p = alg(G, alpha=0.9, tol=1.0e-08, nstart=nstart)
72
+ for n in G:
73
+ assert p[n] == pytest.approx(G.pagerank[n], abs=1e-4)
74
+
75
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python))
76
+ def test_pagerank_max_iter(self, alg):
77
+ with pytest.raises(nx.PowerIterationFailedConvergence):
78
+ alg(self.G, max_iter=0)
79
+
80
+ def test_numpy_pagerank(self):
81
+ G = self.G
82
+ p = _pagerank_numpy(G, alpha=0.9)
83
+ for n in G:
84
+ assert p[n] == pytest.approx(G.pagerank[n], abs=1e-4)
85
+
86
+ # This additionally tests the @nx._dispatch mechanism, treating
87
+ # nx.google_matrix as if it were a re-implementation from another package
88
+ @pytest.mark.parametrize("wrapper", [lambda x: x, dispatch_interface.convert])
89
+ def test_google_matrix(self, wrapper):
90
+ G = wrapper(self.G)
91
+ M = nx.google_matrix(G, alpha=0.9, nodelist=sorted(G))
92
+ _, ev = np.linalg.eig(M.T)
93
+ p = ev[:, 0] / ev[:, 0].sum()
94
+ for a, b in zip(p, self.G.pagerank.values()):
95
+ assert a == pytest.approx(b, abs=1e-7)
96
+
97
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python, _pagerank_numpy))
98
+ def test_personalization(self, alg):
99
+ G = nx.complete_graph(4)
100
+ personalize = {0: 1, 1: 1, 2: 4, 3: 4}
101
+ answer = {
102
+ 0: 0.23246732615667579,
103
+ 1: 0.23246732615667579,
104
+ 2: 0.267532673843324,
105
+ 3: 0.2675326738433241,
106
+ }
107
+ p = alg(G, alpha=0.85, personalization=personalize)
108
+ for n in G:
109
+ assert p[n] == pytest.approx(answer[n], abs=1e-4)
110
+
111
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python, nx.google_matrix))
112
+ def test_zero_personalization_vector(self, alg):
113
+ G = nx.complete_graph(4)
114
+ personalize = {0: 0, 1: 0, 2: 0, 3: 0}
115
+ pytest.raises(ZeroDivisionError, alg, G, personalization=personalize)
116
+
117
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python))
118
+ def test_one_nonzero_personalization_value(self, alg):
119
+ G = nx.complete_graph(4)
120
+ personalize = {0: 0, 1: 0, 2: 0, 3: 1}
121
+ answer = {
122
+ 0: 0.22077931820379187,
123
+ 1: 0.22077931820379187,
124
+ 2: 0.22077931820379187,
125
+ 3: 0.3376620453886241,
126
+ }
127
+ p = alg(G, alpha=0.85, personalization=personalize)
128
+ for n in G:
129
+ assert p[n] == pytest.approx(answer[n], abs=1e-4)
130
+
131
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python))
132
+ def test_incomplete_personalization(self, alg):
133
+ G = nx.complete_graph(4)
134
+ personalize = {3: 1}
135
+ answer = {
136
+ 0: 0.22077931820379187,
137
+ 1: 0.22077931820379187,
138
+ 2: 0.22077931820379187,
139
+ 3: 0.3376620453886241,
140
+ }
141
+ p = alg(G, alpha=0.85, personalization=personalize)
142
+ for n in G:
143
+ assert p[n] == pytest.approx(answer[n], abs=1e-4)
144
+
145
+ def test_dangling_matrix(self):
146
+ """
147
+ Tests that the google_matrix doesn't change except for the dangling
148
+ nodes.
149
+ """
150
+ G = self.G
151
+ dangling = self.dangling_edges
152
+ dangling_sum = sum(dangling.values())
153
+ M1 = nx.google_matrix(G, personalization=dangling)
154
+ M2 = nx.google_matrix(G, personalization=dangling, dangling=dangling)
155
+ for i in range(len(G)):
156
+ for j in range(len(G)):
157
+ if i == self.dangling_node_index and (j + 1) in dangling:
158
+ assert M2[i, j] == pytest.approx(
159
+ dangling[j + 1] / dangling_sum, abs=1e-4
160
+ )
161
+ else:
162
+ assert M2[i, j] == pytest.approx(M1[i, j], abs=1e-4)
163
+
164
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python, _pagerank_numpy))
165
+ def test_dangling_pagerank(self, alg):
166
+ pr = alg(self.G, dangling=self.dangling_edges)
167
+ for n in self.G:
168
+ assert pr[n] == pytest.approx(self.G.dangling_pagerank[n], abs=1e-4)
169
+
170
+ def test_empty(self):
171
+ G = nx.Graph()
172
+ assert nx.pagerank(G) == {}
173
+ assert _pagerank_python(G) == {}
174
+ assert _pagerank_numpy(G) == {}
175
+ assert nx.google_matrix(G).shape == (0, 0)
176
+
177
+ @pytest.mark.parametrize("alg", (nx.pagerank, _pagerank_python))
178
+ def test_multigraph(self, alg):
179
+ G = nx.MultiGraph()
180
+ G.add_edges_from([(1, 2), (1, 2), (1, 2), (2, 3), (2, 3), ("3", 3), ("3", 3)])
181
+ answer = {
182
+ 1: 0.21066048614468322,
183
+ 2: 0.3395308825985378,
184
+ 3: 0.28933951385531687,
185
+ "3": 0.16046911740146227,
186
+ }
187
+ p = alg(G)
188
+ for n in G:
189
+ assert p[n] == pytest.approx(answer[n], abs=1e-4)
190
+
191
+
192
+ class TestPageRankScipy(TestPageRank):
193
+ def test_scipy_pagerank(self):
194
+ G = self.G
195
+ p = _pagerank_scipy(G, alpha=0.9, tol=1.0e-08)
196
+ for n in G:
197
+ assert p[n] == pytest.approx(G.pagerank[n], abs=1e-4)
198
+ personalize = {n: random.random() for n in G}
199
+ p = _pagerank_scipy(G, alpha=0.9, tol=1.0e-08, personalization=personalize)
200
+
201
+ nstart = {n: random.random() for n in G}
202
+ p = _pagerank_scipy(G, alpha=0.9, tol=1.0e-08, nstart=nstart)
203
+ for n in G:
204
+ assert p[n] == pytest.approx(G.pagerank[n], abs=1e-4)
205
+
206
+ def test_scipy_pagerank_max_iter(self):
207
+ with pytest.raises(nx.PowerIterationFailedConvergence):
208
+ _pagerank_scipy(self.G, max_iter=0)
209
+
210
+ def test_dangling_scipy_pagerank(self):
211
+ pr = _pagerank_scipy(self.G, dangling=self.dangling_edges)
212
+ for n in self.G:
213
+ assert pr[n] == pytest.approx(self.G.dangling_pagerank[n], abs=1e-4)
214
+
215
+ def test_empty_scipy(self):
216
+ G = nx.Graph()
217
+ assert _pagerank_scipy(G) == {}
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/minors/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Subpackages related to graph-minor problems.
3
+
4
+ In graph theory, an undirected graph H is called a minor of the graph G if H
5
+ can be formed from G by deleting edges and vertices and by contracting edges
6
+ [1]_.
7
+
8
+ References
9
+ ----------
10
+ .. [1] https://en.wikipedia.org/wiki/Graph_minor
11
+ """
12
+
13
+ from networkx.algorithms.minors.contraction import (
14
+ contracted_edge,
15
+ contracted_nodes,
16
+ equivalence_classes,
17
+ identified_nodes,
18
+ quotient_graph,
19
+ )
20
+
21
+ __all__ = [
22
+ "contracted_edge",
23
+ "contracted_nodes",
24
+ "equivalence_classes",
25
+ "identified_nodes",
26
+ "quotient_graph",
27
+ ]
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/non_randomness.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ r""" Computation of graph non-randomness
2
+ """
3
+
4
+ import math
5
+
6
+ import networkx as nx
7
+ from networkx.utils import not_implemented_for
8
+
9
+ __all__ = ["non_randomness"]
10
+
11
+
12
+ @not_implemented_for("directed")
13
+ @not_implemented_for("multigraph")
14
+ @nx._dispatch(edge_attrs="weight")
15
+ def non_randomness(G, k=None, weight="weight"):
16
+ """Compute the non-randomness of graph G.
17
+
18
+ The first returned value nr is the sum of non-randomness values of all
19
+ edges within the graph (where the non-randomness of an edge tends to be
20
+ small when the two nodes linked by that edge are from two different
21
+ communities).
22
+
23
+ The second computed value nr_rd is a relative measure that indicates
24
+ to what extent graph G is different from random graphs in terms
25
+ of probability. When it is close to 0, the graph tends to be more
26
+ likely generated by an Erdos Renyi model.
27
+
28
+ Parameters
29
+ ----------
30
+ G : NetworkX graph
31
+ Graph must be symmetric, connected, and without self-loops.
32
+
33
+ k : int
34
+ The number of communities in G.
35
+ If k is not set, the function will use a default community
36
+ detection algorithm to set it.
37
+
38
+ weight : string or None, optional (default=None)
39
+ The name of an edge attribute that holds the numerical value used
40
+ as a weight. If None, then each edge has weight 1, i.e., the graph is
41
+ binary.
42
+
43
+ Returns
44
+ -------
45
+ non-randomness : (float, float) tuple
46
+ Non-randomness, Relative non-randomness w.r.t.
47
+ Erdos Renyi random graphs.
48
+
49
+ Raises
50
+ ------
51
+ NetworkXException
52
+ if the input graph is not connected.
53
+ NetworkXError
54
+ if the input graph contains self-loops.
55
+
56
+ Examples
57
+ --------
58
+ >>> G = nx.karate_club_graph()
59
+ >>> nr, nr_rd = nx.non_randomness(G, 2)
60
+ >>> nr, nr_rd = nx.non_randomness(G, 2, 'weight')
61
+
62
+ Notes
63
+ -----
64
+ This computes Eq. (4.4) and (4.5) in Ref. [1]_.
65
+
66
+ If a weight field is passed, this algorithm will use the eigenvalues
67
+ of the weighted adjacency matrix to compute Eq. (4.4) and (4.5).
68
+
69
+ References
70
+ ----------
71
+ .. [1] Xiaowei Ying and Xintao Wu,
72
+ On Randomness Measures for Social Networks,
73
+ SIAM International Conference on Data Mining. 2009
74
+ """
75
+ import numpy as np
76
+
77
+ if not nx.is_connected(G):
78
+ raise nx.NetworkXException("Non connected graph.")
79
+ if len(list(nx.selfloop_edges(G))) > 0:
80
+ raise nx.NetworkXError("Graph must not contain self-loops")
81
+
82
+ if k is None:
83
+ k = len(tuple(nx.community.label_propagation_communities(G)))
84
+
85
+ # eq. 4.4
86
+ eigenvalues = np.linalg.eigvals(nx.to_numpy_array(G, weight=weight))
87
+ nr = np.real(np.sum(eigenvalues[:k]))
88
+
89
+ n = G.number_of_nodes()
90
+ m = G.number_of_edges()
91
+ p = (2 * k * m) / (n * (n - k))
92
+
93
+ # eq. 4.5
94
+ nr_rd = (nr - ((n - 2 * k) * p + k)) / math.sqrt(2 * k * p * (1 - p))
95
+
96
+ return nr, nr_rd
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from networkx.algorithms.operators.all import *
2
+ from networkx.algorithms.operators.binary import *
3
+ from networkx.algorithms.operators.product import *
4
+ from networkx.algorithms.operators.unary import *
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/all.py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Operations on many graphs.
2
+ """
3
+ from itertools import chain, repeat
4
+
5
+ import networkx as nx
6
+
7
+ __all__ = ["union_all", "compose_all", "disjoint_union_all", "intersection_all"]
8
+
9
+
10
+ @nx._dispatch(graphs="[graphs]", preserve_all_attrs=True)
11
+ def union_all(graphs, rename=()):
12
+ """Returns the union of all graphs.
13
+
14
+ The graphs must be disjoint, otherwise an exception is raised.
15
+
16
+ Parameters
17
+ ----------
18
+ graphs : iterable
19
+ Iterable of NetworkX graphs
20
+
21
+ rename : iterable , optional
22
+ Node names of graphs can be changed by specifying the tuple
23
+ rename=('G-','H-') (for example). Node "u" in G is then renamed
24
+ "G-u" and "v" in H is renamed "H-v". Infinite generators (like itertools.count)
25
+ are also supported.
26
+
27
+ Returns
28
+ -------
29
+ U : a graph with the same type as the first graph in list
30
+
31
+ Raises
32
+ ------
33
+ ValueError
34
+ If `graphs` is an empty list.
35
+
36
+ NetworkXError
37
+ In case of mixed type graphs, like MultiGraph and Graph, or directed and undirected graphs.
38
+
39
+ Notes
40
+ -----
41
+ For operating on mixed type graphs, they should be converted to the same type.
42
+ >>> G = nx.Graph()
43
+ >>> H = nx.DiGraph()
44
+ >>> GH = union_all([nx.DiGraph(G), H])
45
+
46
+ To force a disjoint union with node relabeling, use
47
+ disjoint_union_all(G,H) or convert_node_labels_to integers().
48
+
49
+ Graph, edge, and node attributes are propagated to the union graph.
50
+ If a graph attribute is present in multiple graphs, then the value
51
+ from the last graph in the list with that attribute is used.
52
+
53
+ Examples
54
+ --------
55
+ >>> G1 = nx.Graph([(1, 2), (2, 3)])
56
+ >>> G2 = nx.Graph([(4, 5), (5, 6)])
57
+ >>> result_graph = nx.union_all([G1, G2])
58
+ >>> result_graph.nodes()
59
+ NodeView((1, 2, 3, 4, 5, 6))
60
+ >>> result_graph.edges()
61
+ EdgeView([(1, 2), (2, 3), (4, 5), (5, 6)])
62
+
63
+ See Also
64
+ --------
65
+ union
66
+ disjoint_union_all
67
+ """
68
+ R = None
69
+ seen_nodes = set()
70
+
71
+ # rename graph to obtain disjoint node labels
72
+ def add_prefix(graph, prefix):
73
+ if prefix is None:
74
+ return graph
75
+
76
+ def label(x):
77
+ return f"{prefix}{x}"
78
+
79
+ return nx.relabel_nodes(graph, label)
80
+
81
+ rename = chain(rename, repeat(None))
82
+ graphs = (add_prefix(G, name) for G, name in zip(graphs, rename))
83
+
84
+ for i, G in enumerate(graphs):
85
+ G_nodes_set = set(G.nodes)
86
+ if i == 0:
87
+ # Union is the same type as first graph
88
+ R = G.__class__()
89
+ elif G.is_directed() != R.is_directed():
90
+ raise nx.NetworkXError("All graphs must be directed or undirected.")
91
+ elif G.is_multigraph() != R.is_multigraph():
92
+ raise nx.NetworkXError("All graphs must be graphs or multigraphs.")
93
+ elif not seen_nodes.isdisjoint(G_nodes_set):
94
+ raise nx.NetworkXError(
95
+ "The node sets of the graphs are not disjoint.",
96
+ "Use appropriate rename"
97
+ "=(G1prefix,G2prefix,...,GNprefix)"
98
+ "or use disjoint_union(G1,G2,...,GN).",
99
+ )
100
+
101
+ seen_nodes |= G_nodes_set
102
+ R.graph.update(G.graph)
103
+ R.add_nodes_from(G.nodes(data=True))
104
+ R.add_edges_from(
105
+ G.edges(keys=True, data=True) if G.is_multigraph() else G.edges(data=True)
106
+ )
107
+
108
+ if R is None:
109
+ raise ValueError("cannot apply union_all to an empty list")
110
+
111
+ return R
112
+
113
+
114
+ @nx._dispatch(graphs="[graphs]", preserve_all_attrs=True)
115
+ def disjoint_union_all(graphs):
116
+ """Returns the disjoint union of all graphs.
117
+
118
+ This operation forces distinct integer node labels starting with 0
119
+ for the first graph in the list and numbering consecutively.
120
+
121
+ Parameters
122
+ ----------
123
+ graphs : iterable
124
+ Iterable of NetworkX graphs
125
+
126
+ Returns
127
+ -------
128
+ U : A graph with the same type as the first graph in list
129
+
130
+ Raises
131
+ ------
132
+ ValueError
133
+ If `graphs` is an empty list.
134
+
135
+ NetworkXError
136
+ In case of mixed type graphs, like MultiGraph and Graph, or directed and undirected graphs.
137
+
138
+ Examples
139
+ --------
140
+ >>> G1 = nx.Graph([(1, 2), (2, 3)])
141
+ >>> G2 = nx.Graph([(4, 5), (5, 6)])
142
+ >>> U = nx.disjoint_union_all([G1, G2])
143
+ >>> list(U.nodes())
144
+ [0, 1, 2, 3, 4, 5]
145
+ >>> list(U.edges())
146
+ [(0, 1), (1, 2), (3, 4), (4, 5)]
147
+
148
+ Notes
149
+ -----
150
+ For operating on mixed type graphs, they should be converted to the same type.
151
+
152
+ Graph, edge, and node attributes are propagated to the union graph.
153
+ If a graph attribute is present in multiple graphs, then the value
154
+ from the last graph in the list with that attribute is used.
155
+ """
156
+
157
+ def yield_relabeled(graphs):
158
+ first_label = 0
159
+ for G in graphs:
160
+ yield nx.convert_node_labels_to_integers(G, first_label=first_label)
161
+ first_label += len(G)
162
+
163
+ R = union_all(yield_relabeled(graphs))
164
+
165
+ return R
166
+
167
+
168
+ @nx._dispatch(graphs="[graphs]", preserve_all_attrs=True)
169
+ def compose_all(graphs):
170
+ """Returns the composition of all graphs.
171
+
172
+ Composition is the simple union of the node sets and edge sets.
173
+ The node sets of the supplied graphs need not be disjoint.
174
+
175
+ Parameters
176
+ ----------
177
+ graphs : iterable
178
+ Iterable of NetworkX graphs
179
+
180
+ Returns
181
+ -------
182
+ C : A graph with the same type as the first graph in list
183
+
184
+ Raises
185
+ ------
186
+ ValueError
187
+ If `graphs` is an empty list.
188
+
189
+ NetworkXError
190
+ In case of mixed type graphs, like MultiGraph and Graph, or directed and undirected graphs.
191
+
192
+ Examples
193
+ --------
194
+ >>> G1 = nx.Graph([(1, 2), (2, 3)])
195
+ >>> G2 = nx.Graph([(3, 4), (5, 6)])
196
+ >>> C = nx.compose_all([G1, G2])
197
+ >>> list(C.nodes())
198
+ [1, 2, 3, 4, 5, 6]
199
+ >>> list(C.edges())
200
+ [(1, 2), (2, 3), (3, 4), (5, 6)]
201
+
202
+ Notes
203
+ -----
204
+ For operating on mixed type graphs, they should be converted to the same type.
205
+
206
+ Graph, edge, and node attributes are propagated to the union graph.
207
+ If a graph attribute is present in multiple graphs, then the value
208
+ from the last graph in the list with that attribute is used.
209
+ """
210
+ R = None
211
+
212
+ # add graph attributes, H attributes take precedent over G attributes
213
+ for i, G in enumerate(graphs):
214
+ if i == 0:
215
+ # create new graph
216
+ R = G.__class__()
217
+ elif G.is_directed() != R.is_directed():
218
+ raise nx.NetworkXError("All graphs must be directed or undirected.")
219
+ elif G.is_multigraph() != R.is_multigraph():
220
+ raise nx.NetworkXError("All graphs must be graphs or multigraphs.")
221
+
222
+ R.graph.update(G.graph)
223
+ R.add_nodes_from(G.nodes(data=True))
224
+ R.add_edges_from(
225
+ G.edges(keys=True, data=True) if G.is_multigraph() else G.edges(data=True)
226
+ )
227
+
228
+ if R is None:
229
+ raise ValueError("cannot apply compose_all to an empty list")
230
+
231
+ return R
232
+
233
+
234
+ @nx._dispatch(graphs="[graphs]")
235
+ def intersection_all(graphs):
236
+ """Returns a new graph that contains only the nodes and the edges that exist in
237
+ all graphs.
238
+
239
+ Parameters
240
+ ----------
241
+ graphs : iterable
242
+ Iterable of NetworkX graphs
243
+
244
+ Returns
245
+ -------
246
+ R : A new graph with the same type as the first graph in list
247
+
248
+ Raises
249
+ ------
250
+ ValueError
251
+ If `graphs` is an empty list.
252
+
253
+ NetworkXError
254
+ In case of mixed type graphs, like MultiGraph and Graph, or directed and undirected graphs.
255
+
256
+ Notes
257
+ -----
258
+ For operating on mixed type graphs, they should be converted to the same type.
259
+
260
+ Attributes from the graph, nodes, and edges are not copied to the new
261
+ graph.
262
+
263
+ The resulting graph can be updated with attributes if desired. For example, code which adds the minimum attribute for each node across all graphs could work.
264
+ >>> g = nx.Graph()
265
+ >>> g.add_node(0, capacity=4)
266
+ >>> g.add_node(1, capacity=3)
267
+ >>> g.add_edge(0, 1)
268
+
269
+ >>> h = g.copy()
270
+ >>> h.nodes[0]["capacity"] = 2
271
+
272
+ >>> gh = nx.intersection_all([g, h])
273
+
274
+ >>> new_node_attr = {n: min(*(anyG.nodes[n].get('capacity', float('inf')) for anyG in [g, h])) for n in gh}
275
+ >>> nx.set_node_attributes(gh, new_node_attr, 'new_capacity')
276
+ >>> gh.nodes(data=True)
277
+ NodeDataView({0: {'new_capacity': 2}, 1: {'new_capacity': 3}})
278
+
279
+ Examples
280
+ --------
281
+ >>> G1 = nx.Graph([(1, 2), (2, 3)])
282
+ >>> G2 = nx.Graph([(2, 3), (3, 4)])
283
+ >>> R = nx.intersection_all([G1, G2])
284
+ >>> list(R.nodes())
285
+ [2, 3]
286
+ >>> list(R.edges())
287
+ [(2, 3)]
288
+
289
+ """
290
+ R = None
291
+
292
+ for i, G in enumerate(graphs):
293
+ G_nodes_set = set(G.nodes)
294
+ G_edges_set = set(G.edges)
295
+ if not G.is_directed():
296
+ if G.is_multigraph():
297
+ G_edges_set.update((v, u, k) for u, v, k in list(G_edges_set))
298
+ else:
299
+ G_edges_set.update((v, u) for u, v in list(G_edges_set))
300
+ if i == 0:
301
+ # create new graph
302
+ R = G.__class__()
303
+ node_intersection = G_nodes_set
304
+ edge_intersection = G_edges_set
305
+ elif G.is_directed() != R.is_directed():
306
+ raise nx.NetworkXError("All graphs must be directed or undirected.")
307
+ elif G.is_multigraph() != R.is_multigraph():
308
+ raise nx.NetworkXError("All graphs must be graphs or multigraphs.")
309
+ else:
310
+ node_intersection &= G_nodes_set
311
+ edge_intersection &= G_edges_set
312
+
313
+ if R is None:
314
+ raise ValueError("cannot apply intersection_all to an empty list")
315
+
316
+ R.add_nodes_from(node_intersection)
317
+ R.add_edges_from(edge_intersection)
318
+
319
+ return R
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/product.py ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Graph products.
3
+ """
4
+ from itertools import product
5
+
6
+ import networkx as nx
7
+ from networkx.utils import not_implemented_for
8
+
9
+ __all__ = [
10
+ "tensor_product",
11
+ "cartesian_product",
12
+ "lexicographic_product",
13
+ "strong_product",
14
+ "power",
15
+ "rooted_product",
16
+ "corona_product",
17
+ ]
18
+ _G_H = {"G": 0, "H": 1}
19
+
20
+
21
+ def _dict_product(d1, d2):
22
+ return {k: (d1.get(k), d2.get(k)) for k in set(d1) | set(d2)}
23
+
24
+
25
+ # Generators for producing graph products
26
+ def _node_product(G, H):
27
+ for u, v in product(G, H):
28
+ yield ((u, v), _dict_product(G.nodes[u], H.nodes[v]))
29
+
30
+
31
+ def _directed_edges_cross_edges(G, H):
32
+ if not G.is_multigraph() and not H.is_multigraph():
33
+ for u, v, c in G.edges(data=True):
34
+ for x, y, d in H.edges(data=True):
35
+ yield (u, x), (v, y), _dict_product(c, d)
36
+ if not G.is_multigraph() and H.is_multigraph():
37
+ for u, v, c in G.edges(data=True):
38
+ for x, y, k, d in H.edges(data=True, keys=True):
39
+ yield (u, x), (v, y), k, _dict_product(c, d)
40
+ if G.is_multigraph() and not H.is_multigraph():
41
+ for u, v, k, c in G.edges(data=True, keys=True):
42
+ for x, y, d in H.edges(data=True):
43
+ yield (u, x), (v, y), k, _dict_product(c, d)
44
+ if G.is_multigraph() and H.is_multigraph():
45
+ for u, v, j, c in G.edges(data=True, keys=True):
46
+ for x, y, k, d in H.edges(data=True, keys=True):
47
+ yield (u, x), (v, y), (j, k), _dict_product(c, d)
48
+
49
+
50
+ def _undirected_edges_cross_edges(G, H):
51
+ if not G.is_multigraph() and not H.is_multigraph():
52
+ for u, v, c in G.edges(data=True):
53
+ for x, y, d in H.edges(data=True):
54
+ yield (v, x), (u, y), _dict_product(c, d)
55
+ if not G.is_multigraph() and H.is_multigraph():
56
+ for u, v, c in G.edges(data=True):
57
+ for x, y, k, d in H.edges(data=True, keys=True):
58
+ yield (v, x), (u, y), k, _dict_product(c, d)
59
+ if G.is_multigraph() and not H.is_multigraph():
60
+ for u, v, k, c in G.edges(data=True, keys=True):
61
+ for x, y, d in H.edges(data=True):
62
+ yield (v, x), (u, y), k, _dict_product(c, d)
63
+ if G.is_multigraph() and H.is_multigraph():
64
+ for u, v, j, c in G.edges(data=True, keys=True):
65
+ for x, y, k, d in H.edges(data=True, keys=True):
66
+ yield (v, x), (u, y), (j, k), _dict_product(c, d)
67
+
68
+
69
+ def _edges_cross_nodes(G, H):
70
+ if G.is_multigraph():
71
+ for u, v, k, d in G.edges(data=True, keys=True):
72
+ for x in H:
73
+ yield (u, x), (v, x), k, d
74
+ else:
75
+ for u, v, d in G.edges(data=True):
76
+ for x in H:
77
+ if H.is_multigraph():
78
+ yield (u, x), (v, x), None, d
79
+ else:
80
+ yield (u, x), (v, x), d
81
+
82
+
83
+ def _nodes_cross_edges(G, H):
84
+ if H.is_multigraph():
85
+ for x in G:
86
+ for u, v, k, d in H.edges(data=True, keys=True):
87
+ yield (x, u), (x, v), k, d
88
+ else:
89
+ for x in G:
90
+ for u, v, d in H.edges(data=True):
91
+ if G.is_multigraph():
92
+ yield (x, u), (x, v), None, d
93
+ else:
94
+ yield (x, u), (x, v), d
95
+
96
+
97
+ def _edges_cross_nodes_and_nodes(G, H):
98
+ if G.is_multigraph():
99
+ for u, v, k, d in G.edges(data=True, keys=True):
100
+ for x in H:
101
+ for y in H:
102
+ yield (u, x), (v, y), k, d
103
+ else:
104
+ for u, v, d in G.edges(data=True):
105
+ for x in H:
106
+ for y in H:
107
+ if H.is_multigraph():
108
+ yield (u, x), (v, y), None, d
109
+ else:
110
+ yield (u, x), (v, y), d
111
+
112
+
113
+ def _init_product_graph(G, H):
114
+ if G.is_directed() != H.is_directed():
115
+ msg = "G and H must be both directed or both undirected"
116
+ raise nx.NetworkXError(msg)
117
+ if G.is_multigraph() or H.is_multigraph():
118
+ GH = nx.MultiGraph()
119
+ else:
120
+ GH = nx.Graph()
121
+ if G.is_directed():
122
+ GH = GH.to_directed()
123
+ return GH
124
+
125
+
126
+ @nx._dispatch(graphs=_G_H)
127
+ def tensor_product(G, H):
128
+ r"""Returns the tensor product of G and H.
129
+
130
+ The tensor product $P$ of the graphs $G$ and $H$ has a node set that
131
+ is the tensor product of the node sets, $V(P)=V(G) \times V(H)$.
132
+ $P$ has an edge $((u,v), (x,y))$ if and only if $(u,x)$ is an edge in $G$
133
+ and $(v,y)$ is an edge in $H$.
134
+
135
+ Tensor product is sometimes also referred to as the categorical product,
136
+ direct product, cardinal product or conjunction.
137
+
138
+
139
+ Parameters
140
+ ----------
141
+ G, H: graphs
142
+ Networkx graphs.
143
+
144
+ Returns
145
+ -------
146
+ P: NetworkX graph
147
+ The tensor product of G and H. P will be a multi-graph if either G
148
+ or H is a multi-graph, will be a directed if G and H are directed,
149
+ and undirected if G and H are undirected.
150
+
151
+ Raises
152
+ ------
153
+ NetworkXError
154
+ If G and H are not both directed or both undirected.
155
+
156
+ Notes
157
+ -----
158
+ Node attributes in P are two-tuple of the G and H node attributes.
159
+ Missing attributes are assigned None.
160
+
161
+ Examples
162
+ --------
163
+ >>> G = nx.Graph()
164
+ >>> H = nx.Graph()
165
+ >>> G.add_node(0, a1=True)
166
+ >>> H.add_node("a", a2="Spam")
167
+ >>> P = nx.tensor_product(G, H)
168
+ >>> list(P)
169
+ [(0, 'a')]
170
+
171
+ Edge attributes and edge keys (for multigraphs) are also copied to the
172
+ new product graph
173
+ """
174
+ GH = _init_product_graph(G, H)
175
+ GH.add_nodes_from(_node_product(G, H))
176
+ GH.add_edges_from(_directed_edges_cross_edges(G, H))
177
+ if not GH.is_directed():
178
+ GH.add_edges_from(_undirected_edges_cross_edges(G, H))
179
+ return GH
180
+
181
+
182
+ @nx._dispatch(graphs=_G_H)
183
+ def cartesian_product(G, H):
184
+ r"""Returns the Cartesian product of G and H.
185
+
186
+ The Cartesian product $P$ of the graphs $G$ and $H$ has a node set that
187
+ is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
188
+ $P$ has an edge $((u,v),(x,y))$ if and only if either $u$ is equal to $x$
189
+ and both $v$ and $y$ are adjacent in $H$ or if $v$ is equal to $y$ and
190
+ both $u$ and $x$ are adjacent in $G$.
191
+
192
+ Parameters
193
+ ----------
194
+ G, H: graphs
195
+ Networkx graphs.
196
+
197
+ Returns
198
+ -------
199
+ P: NetworkX graph
200
+ The Cartesian product of G and H. P will be a multi-graph if either G
201
+ or H is a multi-graph. Will be a directed if G and H are directed,
202
+ and undirected if G and H are undirected.
203
+
204
+ Raises
205
+ ------
206
+ NetworkXError
207
+ If G and H are not both directed or both undirected.
208
+
209
+ Notes
210
+ -----
211
+ Node attributes in P are two-tuple of the G and H node attributes.
212
+ Missing attributes are assigned None.
213
+
214
+ Examples
215
+ --------
216
+ >>> G = nx.Graph()
217
+ >>> H = nx.Graph()
218
+ >>> G.add_node(0, a1=True)
219
+ >>> H.add_node("a", a2="Spam")
220
+ >>> P = nx.cartesian_product(G, H)
221
+ >>> list(P)
222
+ [(0, 'a')]
223
+
224
+ Edge attributes and edge keys (for multigraphs) are also copied to the
225
+ new product graph
226
+ """
227
+ GH = _init_product_graph(G, H)
228
+ GH.add_nodes_from(_node_product(G, H))
229
+ GH.add_edges_from(_edges_cross_nodes(G, H))
230
+ GH.add_edges_from(_nodes_cross_edges(G, H))
231
+ return GH
232
+
233
+
234
+ @nx._dispatch(graphs=_G_H)
235
+ def lexicographic_product(G, H):
236
+ r"""Returns the lexicographic product of G and H.
237
+
238
+ The lexicographical product $P$ of the graphs $G$ and $H$ has a node set
239
+ that is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
240
+ $P$ has an edge $((u,v), (x,y))$ if and only if $(u,v)$ is an edge in $G$
241
+ or $u==v$ and $(x,y)$ is an edge in $H$.
242
+
243
+ Parameters
244
+ ----------
245
+ G, H: graphs
246
+ Networkx graphs.
247
+
248
+ Returns
249
+ -------
250
+ P: NetworkX graph
251
+ The Cartesian product of G and H. P will be a multi-graph if either G
252
+ or H is a multi-graph. Will be a directed if G and H are directed,
253
+ and undirected if G and H are undirected.
254
+
255
+ Raises
256
+ ------
257
+ NetworkXError
258
+ If G and H are not both directed or both undirected.
259
+
260
+ Notes
261
+ -----
262
+ Node attributes in P are two-tuple of the G and H node attributes.
263
+ Missing attributes are assigned None.
264
+
265
+ Examples
266
+ --------
267
+ >>> G = nx.Graph()
268
+ >>> H = nx.Graph()
269
+ >>> G.add_node(0, a1=True)
270
+ >>> H.add_node("a", a2="Spam")
271
+ >>> P = nx.lexicographic_product(G, H)
272
+ >>> list(P)
273
+ [(0, 'a')]
274
+
275
+ Edge attributes and edge keys (for multigraphs) are also copied to the
276
+ new product graph
277
+ """
278
+ GH = _init_product_graph(G, H)
279
+ GH.add_nodes_from(_node_product(G, H))
280
+ # Edges in G regardless of H designation
281
+ GH.add_edges_from(_edges_cross_nodes_and_nodes(G, H))
282
+ # For each x in G, only if there is an edge in H
283
+ GH.add_edges_from(_nodes_cross_edges(G, H))
284
+ return GH
285
+
286
+
287
+ @nx._dispatch(graphs=_G_H)
288
+ def strong_product(G, H):
289
+ r"""Returns the strong product of G and H.
290
+
291
+ The strong product $P$ of the graphs $G$ and $H$ has a node set that
292
+ is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
293
+ $P$ has an edge $((u,v), (x,y))$ if and only if
294
+ $u==v$ and $(x,y)$ is an edge in $H$, or
295
+ $x==y$ and $(u,v)$ is an edge in $G$, or
296
+ $(u,v)$ is an edge in $G$ and $(x,y)$ is an edge in $H$.
297
+
298
+ Parameters
299
+ ----------
300
+ G, H: graphs
301
+ Networkx graphs.
302
+
303
+ Returns
304
+ -------
305
+ P: NetworkX graph
306
+ The Cartesian product of G and H. P will be a multi-graph if either G
307
+ or H is a multi-graph. Will be a directed if G and H are directed,
308
+ and undirected if G and H are undirected.
309
+
310
+ Raises
311
+ ------
312
+ NetworkXError
313
+ If G and H are not both directed or both undirected.
314
+
315
+ Notes
316
+ -----
317
+ Node attributes in P are two-tuple of the G and H node attributes.
318
+ Missing attributes are assigned None.
319
+
320
+ Examples
321
+ --------
322
+ >>> G = nx.Graph()
323
+ >>> H = nx.Graph()
324
+ >>> G.add_node(0, a1=True)
325
+ >>> H.add_node("a", a2="Spam")
326
+ >>> P = nx.strong_product(G, H)
327
+ >>> list(P)
328
+ [(0, 'a')]
329
+
330
+ Edge attributes and edge keys (for multigraphs) are also copied to the
331
+ new product graph
332
+ """
333
+ GH = _init_product_graph(G, H)
334
+ GH.add_nodes_from(_node_product(G, H))
335
+ GH.add_edges_from(_nodes_cross_edges(G, H))
336
+ GH.add_edges_from(_edges_cross_nodes(G, H))
337
+ GH.add_edges_from(_directed_edges_cross_edges(G, H))
338
+ if not GH.is_directed():
339
+ GH.add_edges_from(_undirected_edges_cross_edges(G, H))
340
+ return GH
341
+
342
+
343
+ @not_implemented_for("directed")
344
+ @not_implemented_for("multigraph")
345
+ @nx._dispatch
346
+ def power(G, k):
347
+ """Returns the specified power of a graph.
348
+
349
+ The $k$th power of a simple graph $G$, denoted $G^k$, is a
350
+ graph on the same set of nodes in which two distinct nodes $u$ and
351
+ $v$ are adjacent in $G^k$ if and only if the shortest path
352
+ distance between $u$ and $v$ in $G$ is at most $k$.
353
+
354
+ Parameters
355
+ ----------
356
+ G : graph
357
+ A NetworkX simple graph object.
358
+
359
+ k : positive integer
360
+ The power to which to raise the graph `G`.
361
+
362
+ Returns
363
+ -------
364
+ NetworkX simple graph
365
+ `G` to the power `k`.
366
+
367
+ Raises
368
+ ------
369
+ ValueError
370
+ If the exponent `k` is not positive.
371
+
372
+ NetworkXNotImplemented
373
+ If `G` is not a simple graph.
374
+
375
+ Examples
376
+ --------
377
+ The number of edges will never decrease when taking successive
378
+ powers:
379
+
380
+ >>> G = nx.path_graph(4)
381
+ >>> list(nx.power(G, 2).edges)
382
+ [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]
383
+ >>> list(nx.power(G, 3).edges)
384
+ [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
385
+
386
+ The `k` th power of a cycle graph on *n* nodes is the complete graph
387
+ on *n* nodes, if `k` is at least ``n // 2``:
388
+
389
+ >>> G = nx.cycle_graph(5)
390
+ >>> H = nx.complete_graph(5)
391
+ >>> nx.is_isomorphic(nx.power(G, 2), H)
392
+ True
393
+ >>> G = nx.cycle_graph(8)
394
+ >>> H = nx.complete_graph(8)
395
+ >>> nx.is_isomorphic(nx.power(G, 4), H)
396
+ True
397
+
398
+ References
399
+ ----------
400
+ .. [1] J. A. Bondy, U. S. R. Murty, *Graph Theory*. Springer, 2008.
401
+
402
+ Notes
403
+ -----
404
+ This definition of "power graph" comes from Exercise 3.1.6 of
405
+ *Graph Theory* by Bondy and Murty [1]_.
406
+
407
+ """
408
+ if k <= 0:
409
+ raise ValueError("k must be a positive integer")
410
+ H = nx.Graph()
411
+ H.add_nodes_from(G)
412
+ # update BFS code to ignore self loops.
413
+ for n in G:
414
+ seen = {} # level (number of hops) when seen in BFS
415
+ level = 1 # the current level
416
+ nextlevel = G[n]
417
+ while nextlevel:
418
+ thislevel = nextlevel # advance to next level
419
+ nextlevel = {} # and start a new list (fringe)
420
+ for v in thislevel:
421
+ if v == n: # avoid self loop
422
+ continue
423
+ if v not in seen:
424
+ seen[v] = level # set the level of vertex v
425
+ nextlevel.update(G[v]) # add neighbors of v
426
+ if k <= level:
427
+ break
428
+ level += 1
429
+ H.add_edges_from((n, nbr) for nbr in seen)
430
+ return H
431
+
432
+
433
+ @not_implemented_for("multigraph")
434
+ @nx._dispatch(graphs=_G_H)
435
+ def rooted_product(G, H, root):
436
+ """Return the rooted product of graphs G and H rooted at root in H.
437
+
438
+ A new graph is constructed representing the rooted product of
439
+ the inputted graphs, G and H, with a root in H.
440
+ A rooted product duplicates H for each nodes in G with the root
441
+ of H corresponding to the node in G. Nodes are renamed as the direct
442
+ product of G and H. The result is a subgraph of the cartesian product.
443
+
444
+ Parameters
445
+ ----------
446
+ G,H : graph
447
+ A NetworkX graph
448
+ root : node
449
+ A node in H
450
+
451
+ Returns
452
+ -------
453
+ R : The rooted product of G and H with a specified root in H
454
+
455
+ Notes
456
+ -----
457
+ The nodes of R are the Cartesian Product of the nodes of G and H.
458
+ The nodes of G and H are not relabeled.
459
+ """
460
+ if root not in H:
461
+ raise nx.NetworkXError("root must be a vertex in H")
462
+
463
+ R = nx.Graph()
464
+ R.add_nodes_from(product(G, H))
465
+
466
+ R.add_edges_from(((e[0], root), (e[1], root)) for e in G.edges())
467
+ R.add_edges_from(((g, e[0]), (g, e[1])) for g in G for e in H.edges())
468
+
469
+ return R
470
+
471
+
472
+ @not_implemented_for("directed")
473
+ @not_implemented_for("multigraph")
474
+ @nx._dispatch(graphs=_G_H)
475
+ def corona_product(G, H):
476
+ r"""Returns the Corona product of G and H.
477
+
478
+ The corona product of $G$ and $H$ is the graph $C = G \circ H$ obtained by
479
+ taking one copy of $G$, called the center graph, $|V(G)|$ copies of $H$,
480
+ called the outer graph, and making the $i$-th vertex of $G$ adjacent to
481
+ every vertex of the $i$-th copy of $H$, where $1 ≤ i ≤ |V(G)|$.
482
+
483
+ Parameters
484
+ ----------
485
+ G, H: NetworkX graphs
486
+ The graphs to take the carona product of.
487
+ `G` is the center graph and `H` is the outer graph
488
+
489
+ Returns
490
+ -------
491
+ C: NetworkX graph
492
+ The Corona product of G and H.
493
+
494
+ Raises
495
+ ------
496
+ NetworkXError
497
+ If G and H are not both directed or both undirected.
498
+
499
+ Examples
500
+ --------
501
+ >>> G = nx.cycle_graph(4)
502
+ >>> H = nx.path_graph(2)
503
+ >>> C = nx.corona_product(G, H)
504
+ >>> list(C)
505
+ [0, 1, 2, 3, (0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)]
506
+ >>> print(C)
507
+ Graph with 12 nodes and 16 edges
508
+
509
+ References
510
+ ----------
511
+ [1] M. Tavakoli, F. Rahbarnia, and A. R. Ashrafi,
512
+ "Studying the corona product of graphs under some graph invariants,"
513
+ Transactions on Combinatorics, vol. 3, no. 3, pp. 43–49, Sep. 2014,
514
+ doi: 10.22108/toc.2014.5542.
515
+ [2] A. Faraji, "Corona Product in Graph Theory," Ali Faraji, May 11, 2021.
516
+ https://blog.alifaraji.ir/math/graph-theory/corona-product.html (accessed Dec. 07, 2021).
517
+ """
518
+ GH = _init_product_graph(G, H)
519
+ GH.add_nodes_from(G)
520
+ GH.add_edges_from(G.edges)
521
+
522
+ for G_node in G:
523
+ # copy nodes of H in GH, call it H_i
524
+ GH.add_nodes_from((G_node, v) for v in H)
525
+
526
+ # copy edges of H_i based on H
527
+ GH.add_edges_from(
528
+ ((G_node, e0), (G_node, e1), d) for e0, e1, d in H.edges.data()
529
+ )
530
+
531
+ # creating new edges between H_i and a G's node
532
+ GH.add_edges_from((G_node, (G_node, H_node)) for H_node in H)
533
+
534
+ return GH
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/operators/tests/__pycache__/test_all.cpython-311.pyc ADDED
Binary file (22.5 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/swap.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Swap edges in a graph.
2
+ """
3
+
4
+ import math
5
+
6
+ import networkx as nx
7
+ from networkx.utils import py_random_state
8
+
9
+ __all__ = ["double_edge_swap", "connected_double_edge_swap", "directed_edge_swap"]
10
+
11
+
12
+ @nx.utils.not_implemented_for("undirected")
13
+ @py_random_state(3)
14
+ @nx._dispatch
15
+ def directed_edge_swap(G, *, nswap=1, max_tries=100, seed=None):
16
+ """Swap three edges in a directed graph while keeping the node degrees fixed.
17
+
18
+ A directed edge swap swaps three edges such that a -> b -> c -> d becomes
19
+ a -> c -> b -> d. This pattern of swapping allows all possible states with the
20
+ same in- and out-degree distribution in a directed graph to be reached.
21
+
22
+ If the swap would create parallel edges (e.g. if a -> c already existed in the
23
+ previous example), another attempt is made to find a suitable trio of edges.
24
+
25
+ Parameters
26
+ ----------
27
+ G : DiGraph
28
+ A directed graph
29
+
30
+ nswap : integer (optional, default=1)
31
+ Number of three-edge (directed) swaps to perform
32
+
33
+ max_tries : integer (optional, default=100)
34
+ Maximum number of attempts to swap edges
35
+
36
+ seed : integer, random_state, or None (default)
37
+ Indicator of random number generation state.
38
+ See :ref:`Randomness<randomness>`.
39
+
40
+ Returns
41
+ -------
42
+ G : DiGraph
43
+ The graph after the edges are swapped.
44
+
45
+ Raises
46
+ ------
47
+ NetworkXError
48
+ If `G` is not directed, or
49
+ If nswap > max_tries, or
50
+ If there are fewer than 4 nodes or 3 edges in `G`.
51
+ NetworkXAlgorithmError
52
+ If the number of swap attempts exceeds `max_tries` before `nswap` swaps are made
53
+
54
+ Notes
55
+ -----
56
+ Does not enforce any connectivity constraints.
57
+
58
+ The graph G is modified in place.
59
+
60
+ References
61
+ ----------
62
+ .. [1] Erdős, Péter L., et al. “A Simple Havel-Hakimi Type Algorithm to Realize
63
+ Graphical Degree Sequences of Directed Graphs.” ArXiv:0905.4913 [Math],
64
+ Jan. 2010. https://doi.org/10.48550/arXiv.0905.4913.
65
+ Published 2010 in Elec. J. Combinatorics (17(1)). R66.
66
+ http://www.combinatorics.org/Volume_17/PDF/v17i1r66.pdf
67
+ .. [2] “Combinatorics - Reaching All Possible Simple Directed Graphs with a given
68
+ Degree Sequence with 2-Edge Swaps.” Mathematics Stack Exchange,
69
+ https://math.stackexchange.com/questions/22272/. Accessed 30 May 2022.
70
+ """
71
+ if nswap > max_tries:
72
+ raise nx.NetworkXError("Number of swaps > number of tries allowed.")
73
+ if len(G) < 4:
74
+ raise nx.NetworkXError("DiGraph has fewer than four nodes.")
75
+ if len(G.edges) < 3:
76
+ raise nx.NetworkXError("DiGraph has fewer than 3 edges")
77
+
78
+ # Instead of choosing uniformly at random from a generated edge list,
79
+ # this algorithm chooses nonuniformly from the set of nodes with
80
+ # probability weighted by degree.
81
+ tries = 0
82
+ swapcount = 0
83
+ keys, degrees = zip(*G.degree()) # keys, degree
84
+ cdf = nx.utils.cumulative_distribution(degrees) # cdf of degree
85
+ discrete_sequence = nx.utils.discrete_sequence
86
+
87
+ while swapcount < nswap:
88
+ # choose source node index from discrete distribution
89
+ start_index = discrete_sequence(1, cdistribution=cdf, seed=seed)[0]
90
+ start = keys[start_index]
91
+ tries += 1
92
+
93
+ if tries > max_tries:
94
+ msg = f"Maximum number of swap attempts ({tries}) exceeded before desired swaps achieved ({nswap})."
95
+ raise nx.NetworkXAlgorithmError(msg)
96
+
97
+ # If the given node doesn't have any out edges, then there isn't anything to swap
98
+ if G.out_degree(start) == 0:
99
+ continue
100
+ second = seed.choice(list(G.succ[start]))
101
+ if start == second:
102
+ continue
103
+
104
+ if G.out_degree(second) == 0:
105
+ continue
106
+ third = seed.choice(list(G.succ[second]))
107
+ if second == third:
108
+ continue
109
+
110
+ if G.out_degree(third) == 0:
111
+ continue
112
+ fourth = seed.choice(list(G.succ[third]))
113
+ if third == fourth:
114
+ continue
115
+
116
+ if (
117
+ third not in G.succ[start]
118
+ and fourth not in G.succ[second]
119
+ and second not in G.succ[third]
120
+ ):
121
+ # Swap nodes
122
+ G.add_edge(start, third)
123
+ G.add_edge(third, second)
124
+ G.add_edge(second, fourth)
125
+ G.remove_edge(start, second)
126
+ G.remove_edge(second, third)
127
+ G.remove_edge(third, fourth)
128
+ swapcount += 1
129
+
130
+ return G
131
+
132
+
133
+ @py_random_state(3)
134
+ @nx._dispatch
135
+ def double_edge_swap(G, nswap=1, max_tries=100, seed=None):
136
+ """Swap two edges in the graph while keeping the node degrees fixed.
137
+
138
+ A double-edge swap removes two randomly chosen edges u-v and x-y
139
+ and creates the new edges u-x and v-y::
140
+
141
+ u--v u v
142
+ becomes | |
143
+ x--y x y
144
+
145
+ If either the edge u-x or v-y already exist no swap is performed
146
+ and another attempt is made to find a suitable edge pair.
147
+
148
+ Parameters
149
+ ----------
150
+ G : graph
151
+ An undirected graph
152
+
153
+ nswap : integer (optional, default=1)
154
+ Number of double-edge swaps to perform
155
+
156
+ max_tries : integer (optional)
157
+ Maximum number of attempts to swap edges
158
+
159
+ seed : integer, random_state, or None (default)
160
+ Indicator of random number generation state.
161
+ See :ref:`Randomness<randomness>`.
162
+
163
+ Returns
164
+ -------
165
+ G : graph
166
+ The graph after double edge swaps.
167
+
168
+ Raises
169
+ ------
170
+ NetworkXError
171
+ If `G` is directed, or
172
+ If `nswap` > `max_tries`, or
173
+ If there are fewer than 4 nodes or 2 edges in `G`.
174
+ NetworkXAlgorithmError
175
+ If the number of swap attempts exceeds `max_tries` before `nswap` swaps are made
176
+
177
+ Notes
178
+ -----
179
+ Does not enforce any connectivity constraints.
180
+
181
+ The graph G is modified in place.
182
+ """
183
+ if G.is_directed():
184
+ raise nx.NetworkXError(
185
+ "double_edge_swap() not defined for directed graphs. Use directed_edge_swap instead."
186
+ )
187
+ if nswap > max_tries:
188
+ raise nx.NetworkXError("Number of swaps > number of tries allowed.")
189
+ if len(G) < 4:
190
+ raise nx.NetworkXError("Graph has fewer than four nodes.")
191
+ if len(G.edges) < 2:
192
+ raise nx.NetworkXError("Graph has fewer than 2 edges")
193
+ # Instead of choosing uniformly at random from a generated edge list,
194
+ # this algorithm chooses nonuniformly from the set of nodes with
195
+ # probability weighted by degree.
196
+ n = 0
197
+ swapcount = 0
198
+ keys, degrees = zip(*G.degree()) # keys, degree
199
+ cdf = nx.utils.cumulative_distribution(degrees) # cdf of degree
200
+ discrete_sequence = nx.utils.discrete_sequence
201
+ while swapcount < nswap:
202
+ # if random.random() < 0.5: continue # trick to avoid periodicities?
203
+ # pick two random edges without creating edge list
204
+ # choose source node indices from discrete distribution
205
+ (ui, xi) = discrete_sequence(2, cdistribution=cdf, seed=seed)
206
+ if ui == xi:
207
+ continue # same source, skip
208
+ u = keys[ui] # convert index to label
209
+ x = keys[xi]
210
+ # choose target uniformly from neighbors
211
+ v = seed.choice(list(G[u]))
212
+ y = seed.choice(list(G[x]))
213
+ if v == y:
214
+ continue # same target, skip
215
+ if (x not in G[u]) and (y not in G[v]): # don't create parallel edges
216
+ G.add_edge(u, x)
217
+ G.add_edge(v, y)
218
+ G.remove_edge(u, v)
219
+ G.remove_edge(x, y)
220
+ swapcount += 1
221
+ if n >= max_tries:
222
+ e = (
223
+ f"Maximum number of swap attempts ({n}) exceeded "
224
+ f"before desired swaps achieved ({nswap})."
225
+ )
226
+ raise nx.NetworkXAlgorithmError(e)
227
+ n += 1
228
+ return G
229
+
230
+
231
+ @py_random_state(3)
232
+ @nx._dispatch
233
+ def connected_double_edge_swap(G, nswap=1, _window_threshold=3, seed=None):
234
+ """Attempts the specified number of double-edge swaps in the graph `G`.
235
+
236
+ A double-edge swap removes two randomly chosen edges `(u, v)` and `(x,
237
+ y)` and creates the new edges `(u, x)` and `(v, y)`::
238
+
239
+ u--v u v
240
+ becomes | |
241
+ x--y x y
242
+
243
+ If either `(u, x)` or `(v, y)` already exist, then no swap is performed
244
+ so the actual number of swapped edges is always *at most* `nswap`.
245
+
246
+ Parameters
247
+ ----------
248
+ G : graph
249
+ An undirected graph
250
+
251
+ nswap : integer (optional, default=1)
252
+ Number of double-edge swaps to perform
253
+
254
+ _window_threshold : integer
255
+
256
+ The window size below which connectedness of the graph will be checked
257
+ after each swap.
258
+
259
+ The "window" in this function is a dynamically updated integer that
260
+ represents the number of swap attempts to make before checking if the
261
+ graph remains connected. It is an optimization used to decrease the
262
+ running time of the algorithm in exchange for increased complexity of
263
+ implementation.
264
+
265
+ If the window size is below this threshold, then the algorithm checks
266
+ after each swap if the graph remains connected by checking if there is a
267
+ path joining the two nodes whose edge was just removed. If the window
268
+ size is above this threshold, then the algorithm performs do all the
269
+ swaps in the window and only then check if the graph is still connected.
270
+
271
+ seed : integer, random_state, or None (default)
272
+ Indicator of random number generation state.
273
+ See :ref:`Randomness<randomness>`.
274
+
275
+ Returns
276
+ -------
277
+ int
278
+ The number of successful swaps
279
+
280
+ Raises
281
+ ------
282
+
283
+ NetworkXError
284
+
285
+ If the input graph is not connected, or if the graph has fewer than four
286
+ nodes.
287
+
288
+ Notes
289
+ -----
290
+
291
+ The initial graph `G` must be connected, and the resulting graph is
292
+ connected. The graph `G` is modified in place.
293
+
294
+ References
295
+ ----------
296
+ .. [1] C. Gkantsidis and M. Mihail and E. Zegura,
297
+ The Markov chain simulation method for generating connected
298
+ power law random graphs, 2003.
299
+ http://citeseer.ist.psu.edu/gkantsidis03markov.html
300
+ """
301
+ if not nx.is_connected(G):
302
+ raise nx.NetworkXError("Graph not connected")
303
+ if len(G) < 4:
304
+ raise nx.NetworkXError("Graph has fewer than four nodes.")
305
+ n = 0
306
+ swapcount = 0
307
+ deg = G.degree()
308
+ # Label key for nodes
309
+ dk = [n for n, d in G.degree()]
310
+ cdf = nx.utils.cumulative_distribution([d for n, d in G.degree()])
311
+ discrete_sequence = nx.utils.discrete_sequence
312
+ window = 1
313
+ while n < nswap:
314
+ wcount = 0
315
+ swapped = []
316
+ # If the window is small, we just check each time whether the graph is
317
+ # connected by checking if the nodes that were just separated are still
318
+ # connected.
319
+ if window < _window_threshold:
320
+ # This Boolean keeps track of whether there was a failure or not.
321
+ fail = False
322
+ while wcount < window and n < nswap:
323
+ # Pick two random edges without creating the edge list. Choose
324
+ # source nodes from the discrete degree distribution.
325
+ (ui, xi) = discrete_sequence(2, cdistribution=cdf, seed=seed)
326
+ # If the source nodes are the same, skip this pair.
327
+ if ui == xi:
328
+ continue
329
+ # Convert an index to a node label.
330
+ u = dk[ui]
331
+ x = dk[xi]
332
+ # Choose targets uniformly from neighbors.
333
+ v = seed.choice(list(G.neighbors(u)))
334
+ y = seed.choice(list(G.neighbors(x)))
335
+ # If the target nodes are the same, skip this pair.
336
+ if v == y:
337
+ continue
338
+ if x not in G[u] and y not in G[v]:
339
+ G.remove_edge(u, v)
340
+ G.remove_edge(x, y)
341
+ G.add_edge(u, x)
342
+ G.add_edge(v, y)
343
+ swapped.append((u, v, x, y))
344
+ swapcount += 1
345
+ n += 1
346
+ # If G remains connected...
347
+ if nx.has_path(G, u, v):
348
+ wcount += 1
349
+ # Otherwise, undo the changes.
350
+ else:
351
+ G.add_edge(u, v)
352
+ G.add_edge(x, y)
353
+ G.remove_edge(u, x)
354
+ G.remove_edge(v, y)
355
+ swapcount -= 1
356
+ fail = True
357
+ # If one of the swaps failed, reduce the window size.
358
+ if fail:
359
+ window = math.ceil(window / 2)
360
+ else:
361
+ window += 1
362
+ # If the window is large, then there is a good chance that a bunch of
363
+ # swaps will work. It's quicker to do all those swaps first and then
364
+ # check if the graph remains connected.
365
+ else:
366
+ while wcount < window and n < nswap:
367
+ # Pick two random edges without creating the edge list. Choose
368
+ # source nodes from the discrete degree distribution.
369
+ (ui, xi) = discrete_sequence(2, cdistribution=cdf, seed=seed)
370
+ # If the source nodes are the same, skip this pair.
371
+ if ui == xi:
372
+ continue
373
+ # Convert an index to a node label.
374
+ u = dk[ui]
375
+ x = dk[xi]
376
+ # Choose targets uniformly from neighbors.
377
+ v = seed.choice(list(G.neighbors(u)))
378
+ y = seed.choice(list(G.neighbors(x)))
379
+ # If the target nodes are the same, skip this pair.
380
+ if v == y:
381
+ continue
382
+ if x not in G[u] and y not in G[v]:
383
+ G.remove_edge(u, v)
384
+ G.remove_edge(x, y)
385
+ G.add_edge(u, x)
386
+ G.add_edge(v, y)
387
+ swapped.append((u, v, x, y))
388
+ swapcount += 1
389
+ n += 1
390
+ wcount += 1
391
+ # If the graph remains connected, increase the window size.
392
+ if nx.is_connected(G):
393
+ window += 1
394
+ # Otherwise, undo the changes from the previous window and decrease
395
+ # the window size.
396
+ else:
397
+ while swapped:
398
+ (u, v, x, y) = swapped.pop()
399
+ G.add_edge(u, v)
400
+ G.add_edge(x, y)
401
+ G.remove_edge(u, x)
402
+ G.remove_edge(v, y)
403
+ swapcount -= 1
404
+ window = math.ceil(window / 2)
405
+ return swapcount
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .graph import Graph
2
+ from .digraph import DiGraph
3
+ from .multigraph import MultiGraph
4
+ from .multidigraph import MultiDiGraph
5
+
6
+ from .function import *
7
+ from .graphviews import subgraph_view, reverse_view
8
+
9
+ from networkx.classes import filters
10
+
11
+ from networkx.classes import coreviews
12
+ from networkx.classes import graphviews
13
+ from networkx.classes import reportviews
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/digraph.cpython-311.pyc ADDED
Binary file (54.4 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/graphviews.cpython-311.pyc ADDED
Binary file (10 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/multidigraph.cpython-311.pyc ADDED
Binary file (41.2 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/__pycache__/multigraph.cpython-311.pyc ADDED
Binary file (52.8 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/reportviews.py ADDED
@@ -0,0 +1,1431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ View Classes provide node, edge and degree "views" of a graph.
3
+
4
+ Views for nodes, edges and degree are provided for all base graph classes.
5
+ A view means a read-only object that is quick to create, automatically
6
+ updated when the graph changes, and provides basic access like `n in V`,
7
+ `for n in V`, `V[n]` and sometimes set operations.
8
+
9
+ The views are read-only iterable containers that are updated as the
10
+ graph is updated. As with dicts, the graph should not be updated
11
+ while iterating through the view. Views can be iterated multiple times.
12
+
13
+ Edge and Node views also allow data attribute lookup.
14
+ The resulting attribute dict is writable as `G.edges[3, 4]['color']='red'`
15
+ Degree views allow lookup of degree values for single nodes.
16
+ Weighted degree is supported with the `weight` argument.
17
+
18
+ NodeView
19
+ ========
20
+
21
+ `V = G.nodes` (or `V = G.nodes()`) allows `len(V)`, `n in V`, set
22
+ operations e.g. "G.nodes & H.nodes", and `dd = G.nodes[n]`, where
23
+ `dd` is the node data dict. Iteration is over the nodes by default.
24
+
25
+ NodeDataView
26
+ ============
27
+
28
+ To iterate over (node, data) pairs, use arguments to `G.nodes()`
29
+ to create a DataView e.g. `DV = G.nodes(data='color', default='red')`.
30
+ The DataView iterates as `for n, color in DV` and allows
31
+ `(n, 'red') in DV`. Using `DV = G.nodes(data=True)`, the DataViews
32
+ use the full datadict in writeable form also allowing contain testing as
33
+ `(n, {'color': 'red'}) in VD`. DataViews allow set operations when
34
+ data attributes are hashable.
35
+
36
+ DegreeView
37
+ ==========
38
+
39
+ `V = G.degree` allows iteration over (node, degree) pairs as well
40
+ as lookup: `deg=V[n]`. There are many flavors of DegreeView
41
+ for In/Out/Directed/Multi. For Directed Graphs, `G.degree`
42
+ counts both in and out going edges. `G.out_degree` and
43
+ `G.in_degree` count only specific directions.
44
+ Weighted degree using edge data attributes is provide via
45
+ `V = G.degree(weight='attr_name')` where any string with the
46
+ attribute name can be used. `weight=None` is the default.
47
+ No set operations are implemented for degrees, use NodeView.
48
+
49
+ The argument `nbunch` restricts iteration to nodes in nbunch.
50
+ The DegreeView can still lookup any node even if nbunch is specified.
51
+
52
+ EdgeView
53
+ ========
54
+
55
+ `V = G.edges` or `V = G.edges()` allows iteration over edges as well as
56
+ `e in V`, set operations and edge data lookup `dd = G.edges[2, 3]`.
57
+ Iteration is over 2-tuples `(u, v)` for Graph/DiGraph. For multigraphs
58
+ edges 3-tuples `(u, v, key)` are the default but 2-tuples can be obtained
59
+ via `V = G.edges(keys=False)`.
60
+
61
+ Set operations for directed graphs treat the edges as a set of 2-tuples.
62
+ For undirected graphs, 2-tuples are not a unique representation of edges.
63
+ So long as the set being compared to contains unique representations
64
+ of its edges, the set operations will act as expected. If the other
65
+ set contains both `(0, 1)` and `(1, 0)` however, the result of set
66
+ operations may contain both representations of the same edge.
67
+
68
+ EdgeDataView
69
+ ============
70
+
71
+ Edge data can be reported using an EdgeDataView typically created
72
+ by calling an EdgeView: `DV = G.edges(data='weight', default=1)`.
73
+ The EdgeDataView allows iteration over edge tuples, membership checking
74
+ but no set operations.
75
+
76
+ Iteration depends on `data` and `default` and for multigraph `keys`
77
+ If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
78
+ If `data is True` iterate over 3-tuples `(u, v, datadict)`.
79
+ Otherwise iterate over `(u, v, datadict.get(data, default))`.
80
+ For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key`
81
+ to create 3-tuples and 4-tuples.
82
+
83
+ The argument `nbunch` restricts edges to those incident to nodes in nbunch.
84
+ """
85
+ from collections.abc import Mapping, Set
86
+
87
+ import networkx as nx
88
+
89
+ __all__ = [
90
+ "NodeView",
91
+ "NodeDataView",
92
+ "EdgeView",
93
+ "OutEdgeView",
94
+ "InEdgeView",
95
+ "EdgeDataView",
96
+ "OutEdgeDataView",
97
+ "InEdgeDataView",
98
+ "MultiEdgeView",
99
+ "OutMultiEdgeView",
100
+ "InMultiEdgeView",
101
+ "MultiEdgeDataView",
102
+ "OutMultiEdgeDataView",
103
+ "InMultiEdgeDataView",
104
+ "DegreeView",
105
+ "DiDegreeView",
106
+ "InDegreeView",
107
+ "OutDegreeView",
108
+ "MultiDegreeView",
109
+ "DiMultiDegreeView",
110
+ "InMultiDegreeView",
111
+ "OutMultiDegreeView",
112
+ ]
113
+
114
+
115
+ # NodeViews
116
+ class NodeView(Mapping, Set):
117
+ """A NodeView class to act as G.nodes for a NetworkX Graph
118
+
119
+ Set operations act on the nodes without considering data.
120
+ Iteration is over nodes. Node data can be looked up like a dict.
121
+ Use NodeDataView to iterate over node data or to specify a data
122
+ attribute for lookup. NodeDataView is created by calling the NodeView.
123
+
124
+ Parameters
125
+ ----------
126
+ graph : NetworkX graph-like class
127
+
128
+ Examples
129
+ --------
130
+ >>> G = nx.path_graph(3)
131
+ >>> NV = G.nodes()
132
+ >>> 2 in NV
133
+ True
134
+ >>> for n in NV:
135
+ ... print(n)
136
+ 0
137
+ 1
138
+ 2
139
+ >>> assert NV & {1, 2, 3} == {1, 2}
140
+
141
+ >>> G.add_node(2, color="blue")
142
+ >>> NV[2]
143
+ {'color': 'blue'}
144
+ >>> G.add_node(8, color="red")
145
+ >>> NDV = G.nodes(data=True)
146
+ >>> (2, NV[2]) in NDV
147
+ True
148
+ >>> for n, dd in NDV:
149
+ ... print((n, dd.get("color", "aqua")))
150
+ (0, 'aqua')
151
+ (1, 'aqua')
152
+ (2, 'blue')
153
+ (8, 'red')
154
+ >>> NDV[2] == NV[2]
155
+ True
156
+
157
+ >>> NVdata = G.nodes(data="color", default="aqua")
158
+ >>> (2, NVdata[2]) in NVdata
159
+ True
160
+ >>> for n, dd in NVdata:
161
+ ... print((n, dd))
162
+ (0, 'aqua')
163
+ (1, 'aqua')
164
+ (2, 'blue')
165
+ (8, 'red')
166
+ >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict
167
+ False
168
+ """
169
+
170
+ __slots__ = ("_nodes",)
171
+
172
+ def __getstate__(self):
173
+ return {"_nodes": self._nodes}
174
+
175
+ def __setstate__(self, state):
176
+ self._nodes = state["_nodes"]
177
+
178
+ def __init__(self, graph):
179
+ self._nodes = graph._node
180
+
181
+ # Mapping methods
182
+ def __len__(self):
183
+ return len(self._nodes)
184
+
185
+ def __iter__(self):
186
+ return iter(self._nodes)
187
+
188
+ def __getitem__(self, n):
189
+ if isinstance(n, slice):
190
+ raise nx.NetworkXError(
191
+ f"{type(self).__name__} does not support slicing, "
192
+ f"try list(G.nodes)[{n.start}:{n.stop}:{n.step}]"
193
+ )
194
+ return self._nodes[n]
195
+
196
+ # Set methods
197
+ def __contains__(self, n):
198
+ return n in self._nodes
199
+
200
+ @classmethod
201
+ def _from_iterable(cls, it):
202
+ return set(it)
203
+
204
+ # DataView method
205
+ def __call__(self, data=False, default=None):
206
+ if data is False:
207
+ return self
208
+ return NodeDataView(self._nodes, data, default)
209
+
210
+ def data(self, data=True, default=None):
211
+ """
212
+ Return a read-only view of node data.
213
+
214
+ Parameters
215
+ ----------
216
+ data : bool or node data key, default=True
217
+ If ``data=True`` (the default), return a `NodeDataView` object that
218
+ maps each node to *all* of its attributes. `data` may also be an
219
+ arbitrary key, in which case the `NodeDataView` maps each node to
220
+ the value for the keyed attribute. In this case, if a node does
221
+ not have the `data` attribute, the `default` value is used.
222
+ default : object, default=None
223
+ The value used when a node does not have a specific attribute.
224
+
225
+ Returns
226
+ -------
227
+ NodeDataView
228
+ The layout of the returned NodeDataView depends on the value of the
229
+ `data` parameter.
230
+
231
+ Notes
232
+ -----
233
+ If ``data=False``, returns a `NodeView` object without data.
234
+
235
+ See Also
236
+ --------
237
+ NodeDataView
238
+
239
+ Examples
240
+ --------
241
+ >>> G = nx.Graph()
242
+ >>> G.add_nodes_from([
243
+ ... (0, {"color": "red", "weight": 10}),
244
+ ... (1, {"color": "blue"}),
245
+ ... (2, {"color": "yellow", "weight": 2})
246
+ ... ])
247
+
248
+ Accessing node data with ``data=True`` (the default) returns a
249
+ NodeDataView mapping each node to all of its attributes:
250
+
251
+ >>> G.nodes.data()
252
+ NodeDataView({0: {'color': 'red', 'weight': 10}, 1: {'color': 'blue'}, 2: {'color': 'yellow', 'weight': 2}})
253
+
254
+ If `data` represents a key in the node attribute dict, a NodeDataView mapping
255
+ the nodes to the value for that specific key is returned:
256
+
257
+ >>> G.nodes.data("color")
258
+ NodeDataView({0: 'red', 1: 'blue', 2: 'yellow'}, data='color')
259
+
260
+ If a specific key is not found in an attribute dict, the value specified
261
+ by `default` is returned:
262
+
263
+ >>> G.nodes.data("weight", default=-999)
264
+ NodeDataView({0: 10, 1: -999, 2: 2}, data='weight')
265
+
266
+ Note that there is no check that the `data` key is in any of the
267
+ node attribute dictionaries:
268
+
269
+ >>> G.nodes.data("height")
270
+ NodeDataView({0: None, 1: None, 2: None}, data='height')
271
+ """
272
+ if data is False:
273
+ return self
274
+ return NodeDataView(self._nodes, data, default)
275
+
276
+ def __str__(self):
277
+ return str(list(self))
278
+
279
+ def __repr__(self):
280
+ return f"{self.__class__.__name__}({tuple(self)})"
281
+
282
+
283
+ class NodeDataView(Set):
284
+ """A DataView class for nodes of a NetworkX Graph
285
+
286
+ The main use for this class is to iterate through node-data pairs.
287
+ The data can be the entire data-dictionary for each node, or it
288
+ can be a specific attribute (with default) for each node.
289
+ Set operations are enabled with NodeDataView, but don't work in
290
+ cases where the data is not hashable. Use with caution.
291
+ Typically, set operations on nodes use NodeView, not NodeDataView.
292
+ That is, they use `G.nodes` instead of `G.nodes(data='foo')`.
293
+
294
+ Parameters
295
+ ==========
296
+ graph : NetworkX graph-like class
297
+ data : bool or string (default=False)
298
+ default : object (default=None)
299
+ """
300
+
301
+ __slots__ = ("_nodes", "_data", "_default")
302
+
303
+ def __getstate__(self):
304
+ return {"_nodes": self._nodes, "_data": self._data, "_default": self._default}
305
+
306
+ def __setstate__(self, state):
307
+ self._nodes = state["_nodes"]
308
+ self._data = state["_data"]
309
+ self._default = state["_default"]
310
+
311
+ def __init__(self, nodedict, data=False, default=None):
312
+ self._nodes = nodedict
313
+ self._data = data
314
+ self._default = default
315
+
316
+ @classmethod
317
+ def _from_iterable(cls, it):
318
+ try:
319
+ return set(it)
320
+ except TypeError as err:
321
+ if "unhashable" in str(err):
322
+ msg = " : Could be b/c data=True or your values are unhashable"
323
+ raise TypeError(str(err) + msg) from err
324
+ raise
325
+
326
+ def __len__(self):
327
+ return len(self._nodes)
328
+
329
+ def __iter__(self):
330
+ data = self._data
331
+ if data is False:
332
+ return iter(self._nodes)
333
+ if data is True:
334
+ return iter(self._nodes.items())
335
+ return (
336
+ (n, dd[data] if data in dd else self._default)
337
+ for n, dd in self._nodes.items()
338
+ )
339
+
340
+ def __contains__(self, n):
341
+ try:
342
+ node_in = n in self._nodes
343
+ except TypeError:
344
+ n, d = n
345
+ return n in self._nodes and self[n] == d
346
+ if node_in is True:
347
+ return node_in
348
+ try:
349
+ n, d = n
350
+ except (TypeError, ValueError):
351
+ return False
352
+ return n in self._nodes and self[n] == d
353
+
354
+ def __getitem__(self, n):
355
+ if isinstance(n, slice):
356
+ raise nx.NetworkXError(
357
+ f"{type(self).__name__} does not support slicing, "
358
+ f"try list(G.nodes.data())[{n.start}:{n.stop}:{n.step}]"
359
+ )
360
+ ddict = self._nodes[n]
361
+ data = self._data
362
+ if data is False or data is True:
363
+ return ddict
364
+ return ddict[data] if data in ddict else self._default
365
+
366
+ def __str__(self):
367
+ return str(list(self))
368
+
369
+ def __repr__(self):
370
+ name = self.__class__.__name__
371
+ if self._data is False:
372
+ return f"{name}({tuple(self)})"
373
+ if self._data is True:
374
+ return f"{name}({dict(self)})"
375
+ return f"{name}({dict(self)}, data={self._data!r})"
376
+
377
+
378
+ # DegreeViews
379
+ class DiDegreeView:
380
+ """A View class for degree of nodes in a NetworkX Graph
381
+
382
+ The functionality is like dict.items() with (node, degree) pairs.
383
+ Additional functionality includes read-only lookup of node degree,
384
+ and calling with optional features nbunch (for only a subset of nodes)
385
+ and weight (use edge weights to compute degree).
386
+
387
+ Parameters
388
+ ==========
389
+ graph : NetworkX graph-like class
390
+ nbunch : node, container of nodes, or None meaning all nodes (default=None)
391
+ weight : bool or string (default=None)
392
+
393
+ Notes
394
+ -----
395
+ DegreeView can still lookup any node even if nbunch is specified.
396
+
397
+ Examples
398
+ --------
399
+ >>> G = nx.path_graph(3)
400
+ >>> DV = G.degree()
401
+ >>> assert DV[2] == 1
402
+ >>> assert sum(deg for n, deg in DV) == 4
403
+
404
+ >>> DVweight = G.degree(weight="span")
405
+ >>> G.add_edge(1, 2, span=34)
406
+ >>> DVweight[2]
407
+ 34
408
+ >>> DVweight[0] # default edge weight is 1
409
+ 1
410
+ >>> sum(span for n, span in DVweight) # sum weighted degrees
411
+ 70
412
+
413
+ >>> DVnbunch = G.degree(nbunch=(1, 2))
414
+ >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
415
+ """
416
+
417
+ def __init__(self, G, nbunch=None, weight=None):
418
+ self._graph = G
419
+ self._succ = G._succ if hasattr(G, "_succ") else G._adj
420
+ self._pred = G._pred if hasattr(G, "_pred") else G._adj
421
+ self._nodes = self._succ if nbunch is None else list(G.nbunch_iter(nbunch))
422
+ self._weight = weight
423
+
424
+ def __call__(self, nbunch=None, weight=None):
425
+ if nbunch is None:
426
+ if weight == self._weight:
427
+ return self
428
+ return self.__class__(self._graph, None, weight)
429
+ try:
430
+ if nbunch in self._nodes:
431
+ if weight == self._weight:
432
+ return self[nbunch]
433
+ return self.__class__(self._graph, None, weight)[nbunch]
434
+ except TypeError:
435
+ pass
436
+ return self.__class__(self._graph, nbunch, weight)
437
+
438
+ def __getitem__(self, n):
439
+ weight = self._weight
440
+ succs = self._succ[n]
441
+ preds = self._pred[n]
442
+ if weight is None:
443
+ return len(succs) + len(preds)
444
+ return sum(dd.get(weight, 1) for dd in succs.values()) + sum(
445
+ dd.get(weight, 1) for dd in preds.values()
446
+ )
447
+
448
+ def __iter__(self):
449
+ weight = self._weight
450
+ if weight is None:
451
+ for n in self._nodes:
452
+ succs = self._succ[n]
453
+ preds = self._pred[n]
454
+ yield (n, len(succs) + len(preds))
455
+ else:
456
+ for n in self._nodes:
457
+ succs = self._succ[n]
458
+ preds = self._pred[n]
459
+ deg = sum(dd.get(weight, 1) for dd in succs.values()) + sum(
460
+ dd.get(weight, 1) for dd in preds.values()
461
+ )
462
+ yield (n, deg)
463
+
464
+ def __len__(self):
465
+ return len(self._nodes)
466
+
467
+ def __str__(self):
468
+ return str(list(self))
469
+
470
+ def __repr__(self):
471
+ return f"{self.__class__.__name__}({dict(self)})"
472
+
473
+
474
+ class DegreeView(DiDegreeView):
475
+ """A DegreeView class to act as G.degree for a NetworkX Graph
476
+
477
+ Typical usage focuses on iteration over `(node, degree)` pairs.
478
+ The degree is by default the number of edges incident to the node.
479
+ Optional argument `weight` enables weighted degree using the edge
480
+ attribute named in the `weight` argument. Reporting and iteration
481
+ can also be restricted to a subset of nodes using `nbunch`.
482
+
483
+ Additional functionality include node lookup so that `G.degree[n]`
484
+ reported the (possibly weighted) degree of node `n`. Calling the
485
+ view creates a view with different arguments `nbunch` or `weight`.
486
+
487
+ Parameters
488
+ ==========
489
+ graph : NetworkX graph-like class
490
+ nbunch : node, container of nodes, or None meaning all nodes (default=None)
491
+ weight : string or None (default=None)
492
+
493
+ Notes
494
+ -----
495
+ DegreeView can still lookup any node even if nbunch is specified.
496
+
497
+ Examples
498
+ --------
499
+ >>> G = nx.path_graph(3)
500
+ >>> DV = G.degree()
501
+ >>> assert DV[2] == 1
502
+ >>> assert G.degree[2] == 1
503
+ >>> assert sum(deg for n, deg in DV) == 4
504
+
505
+ >>> DVweight = G.degree(weight="span")
506
+ >>> G.add_edge(1, 2, span=34)
507
+ >>> DVweight[2]
508
+ 34
509
+ >>> DVweight[0] # default edge weight is 1
510
+ 1
511
+ >>> sum(span for n, span in DVweight) # sum weighted degrees
512
+ 70
513
+
514
+ >>> DVnbunch = G.degree(nbunch=(1, 2))
515
+ >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
516
+ """
517
+
518
+ def __getitem__(self, n):
519
+ weight = self._weight
520
+ nbrs = self._succ[n]
521
+ if weight is None:
522
+ return len(nbrs) + (n in nbrs)
523
+ return sum(dd.get(weight, 1) for dd in nbrs.values()) + (
524
+ n in nbrs and nbrs[n].get(weight, 1)
525
+ )
526
+
527
+ def __iter__(self):
528
+ weight = self._weight
529
+ if weight is None:
530
+ for n in self._nodes:
531
+ nbrs = self._succ[n]
532
+ yield (n, len(nbrs) + (n in nbrs))
533
+ else:
534
+ for n in self._nodes:
535
+ nbrs = self._succ[n]
536
+ deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + (
537
+ n in nbrs and nbrs[n].get(weight, 1)
538
+ )
539
+ yield (n, deg)
540
+
541
+
542
+ class OutDegreeView(DiDegreeView):
543
+ """A DegreeView class to report out_degree for a DiGraph; See DegreeView"""
544
+
545
+ def __getitem__(self, n):
546
+ weight = self._weight
547
+ nbrs = self._succ[n]
548
+ if self._weight is None:
549
+ return len(nbrs)
550
+ return sum(dd.get(self._weight, 1) for dd in nbrs.values())
551
+
552
+ def __iter__(self):
553
+ weight = self._weight
554
+ if weight is None:
555
+ for n in self._nodes:
556
+ succs = self._succ[n]
557
+ yield (n, len(succs))
558
+ else:
559
+ for n in self._nodes:
560
+ succs = self._succ[n]
561
+ deg = sum(dd.get(weight, 1) for dd in succs.values())
562
+ yield (n, deg)
563
+
564
+
565
+ class InDegreeView(DiDegreeView):
566
+ """A DegreeView class to report in_degree for a DiGraph; See DegreeView"""
567
+
568
+ def __getitem__(self, n):
569
+ weight = self._weight
570
+ nbrs = self._pred[n]
571
+ if weight is None:
572
+ return len(nbrs)
573
+ return sum(dd.get(weight, 1) for dd in nbrs.values())
574
+
575
+ def __iter__(self):
576
+ weight = self._weight
577
+ if weight is None:
578
+ for n in self._nodes:
579
+ preds = self._pred[n]
580
+ yield (n, len(preds))
581
+ else:
582
+ for n in self._nodes:
583
+ preds = self._pred[n]
584
+ deg = sum(dd.get(weight, 1) for dd in preds.values())
585
+ yield (n, deg)
586
+
587
+
588
+ class MultiDegreeView(DiDegreeView):
589
+ """A DegreeView class for undirected multigraphs; See DegreeView"""
590
+
591
+ def __getitem__(self, n):
592
+ weight = self._weight
593
+ nbrs = self._succ[n]
594
+ if weight is None:
595
+ return sum(len(keys) for keys in nbrs.values()) + (
596
+ n in nbrs and len(nbrs[n])
597
+ )
598
+ # edge weighted graph - degree is sum of nbr edge weights
599
+ deg = sum(
600
+ d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
601
+ )
602
+ if n in nbrs:
603
+ deg += sum(d.get(weight, 1) for d in nbrs[n].values())
604
+ return deg
605
+
606
+ def __iter__(self):
607
+ weight = self._weight
608
+ if weight is None:
609
+ for n in self._nodes:
610
+ nbrs = self._succ[n]
611
+ deg = sum(len(keys) for keys in nbrs.values()) + (
612
+ n in nbrs and len(nbrs[n])
613
+ )
614
+ yield (n, deg)
615
+ else:
616
+ for n in self._nodes:
617
+ nbrs = self._succ[n]
618
+ deg = sum(
619
+ d.get(weight, 1)
620
+ for key_dict in nbrs.values()
621
+ for d in key_dict.values()
622
+ )
623
+ if n in nbrs:
624
+ deg += sum(d.get(weight, 1) for d in nbrs[n].values())
625
+ yield (n, deg)
626
+
627
+
628
+ class DiMultiDegreeView(DiDegreeView):
629
+ """A DegreeView class for MultiDiGraph; See DegreeView"""
630
+
631
+ def __getitem__(self, n):
632
+ weight = self._weight
633
+ succs = self._succ[n]
634
+ preds = self._pred[n]
635
+ if weight is None:
636
+ return sum(len(keys) for keys in succs.values()) + sum(
637
+ len(keys) for keys in preds.values()
638
+ )
639
+ # edge weighted graph - degree is sum of nbr edge weights
640
+ deg = sum(
641
+ d.get(weight, 1) for key_dict in succs.values() for d in key_dict.values()
642
+ ) + sum(
643
+ d.get(weight, 1) for key_dict in preds.values() for d in key_dict.values()
644
+ )
645
+ return deg
646
+
647
+ def __iter__(self):
648
+ weight = self._weight
649
+ if weight is None:
650
+ for n in self._nodes:
651
+ succs = self._succ[n]
652
+ preds = self._pred[n]
653
+ deg = sum(len(keys) for keys in succs.values()) + sum(
654
+ len(keys) for keys in preds.values()
655
+ )
656
+ yield (n, deg)
657
+ else:
658
+ for n in self._nodes:
659
+ succs = self._succ[n]
660
+ preds = self._pred[n]
661
+ deg = sum(
662
+ d.get(weight, 1)
663
+ for key_dict in succs.values()
664
+ for d in key_dict.values()
665
+ ) + sum(
666
+ d.get(weight, 1)
667
+ for key_dict in preds.values()
668
+ for d in key_dict.values()
669
+ )
670
+ yield (n, deg)
671
+
672
+
673
+ class InMultiDegreeView(DiDegreeView):
674
+ """A DegreeView class for inward degree of MultiDiGraph; See DegreeView"""
675
+
676
+ def __getitem__(self, n):
677
+ weight = self._weight
678
+ nbrs = self._pred[n]
679
+ if weight is None:
680
+ return sum(len(data) for data in nbrs.values())
681
+ # edge weighted graph - degree is sum of nbr edge weights
682
+ return sum(
683
+ d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
684
+ )
685
+
686
+ def __iter__(self):
687
+ weight = self._weight
688
+ if weight is None:
689
+ for n in self._nodes:
690
+ nbrs = self._pred[n]
691
+ deg = sum(len(data) for data in nbrs.values())
692
+ yield (n, deg)
693
+ else:
694
+ for n in self._nodes:
695
+ nbrs = self._pred[n]
696
+ deg = sum(
697
+ d.get(weight, 1)
698
+ for key_dict in nbrs.values()
699
+ for d in key_dict.values()
700
+ )
701
+ yield (n, deg)
702
+
703
+
704
+ class OutMultiDegreeView(DiDegreeView):
705
+ """A DegreeView class for outward degree of MultiDiGraph; See DegreeView"""
706
+
707
+ def __getitem__(self, n):
708
+ weight = self._weight
709
+ nbrs = self._succ[n]
710
+ if weight is None:
711
+ return sum(len(data) for data in nbrs.values())
712
+ # edge weighted graph - degree is sum of nbr edge weights
713
+ return sum(
714
+ d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
715
+ )
716
+
717
+ def __iter__(self):
718
+ weight = self._weight
719
+ if weight is None:
720
+ for n in self._nodes:
721
+ nbrs = self._succ[n]
722
+ deg = sum(len(data) for data in nbrs.values())
723
+ yield (n, deg)
724
+ else:
725
+ for n in self._nodes:
726
+ nbrs = self._succ[n]
727
+ deg = sum(
728
+ d.get(weight, 1)
729
+ for key_dict in nbrs.values()
730
+ for d in key_dict.values()
731
+ )
732
+ yield (n, deg)
733
+
734
+
735
+ # EdgeDataViews
736
+ class OutEdgeDataView:
737
+ """EdgeDataView for outward edges of DiGraph; See EdgeDataView"""
738
+
739
+ __slots__ = (
740
+ "_viewer",
741
+ "_nbunch",
742
+ "_data",
743
+ "_default",
744
+ "_adjdict",
745
+ "_nodes_nbrs",
746
+ "_report",
747
+ )
748
+
749
+ def __getstate__(self):
750
+ return {
751
+ "viewer": self._viewer,
752
+ "nbunch": self._nbunch,
753
+ "data": self._data,
754
+ "default": self._default,
755
+ }
756
+
757
+ def __setstate__(self, state):
758
+ self.__init__(**state)
759
+
760
+ def __init__(self, viewer, nbunch=None, data=False, *, default=None):
761
+ self._viewer = viewer
762
+ adjdict = self._adjdict = viewer._adjdict
763
+ if nbunch is None:
764
+ self._nodes_nbrs = adjdict.items
765
+ else:
766
+ # dict retains order of nodes but acts like a set
767
+ nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
768
+ self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
769
+ self._nbunch = nbunch
770
+ self._data = data
771
+ self._default = default
772
+ # Set _report based on data and default
773
+ if data is True:
774
+ self._report = lambda n, nbr, dd: (n, nbr, dd)
775
+ elif data is False:
776
+ self._report = lambda n, nbr, dd: (n, nbr)
777
+ else: # data is attribute name
778
+ self._report = (
779
+ lambda n, nbr, dd: (n, nbr, dd[data])
780
+ if data in dd
781
+ else (n, nbr, default)
782
+ )
783
+
784
+ def __len__(self):
785
+ return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
786
+
787
+ def __iter__(self):
788
+ return (
789
+ self._report(n, nbr, dd)
790
+ for n, nbrs in self._nodes_nbrs()
791
+ for nbr, dd in nbrs.items()
792
+ )
793
+
794
+ def __contains__(self, e):
795
+ u, v = e[:2]
796
+ if self._nbunch is not None and u not in self._nbunch:
797
+ return False # this edge doesn't start in nbunch
798
+ try:
799
+ ddict = self._adjdict[u][v]
800
+ except KeyError:
801
+ return False
802
+ return e == self._report(u, v, ddict)
803
+
804
+ def __str__(self):
805
+ return str(list(self))
806
+
807
+ def __repr__(self):
808
+ return f"{self.__class__.__name__}({list(self)})"
809
+
810
+
811
+ class EdgeDataView(OutEdgeDataView):
812
+ """A EdgeDataView class for edges of Graph
813
+
814
+ This view is primarily used to iterate over the edges reporting
815
+ edges as node-tuples with edge data optionally reported. The
816
+ argument `nbunch` allows restriction to edges incident to nodes
817
+ in that container/singleton. The default (nbunch=None)
818
+ reports all edges. The arguments `data` and `default` control
819
+ what edge data is reported. The default `data is False` reports
820
+ only node-tuples for each edge. If `data is True` the entire edge
821
+ data dict is returned. Otherwise `data` is assumed to hold the name
822
+ of the edge attribute to report with default `default` if that
823
+ edge attribute is not present.
824
+
825
+ Parameters
826
+ ----------
827
+ nbunch : container of nodes, node or None (default None)
828
+ data : False, True or string (default False)
829
+ default : default value (default None)
830
+
831
+ Examples
832
+ --------
833
+ >>> G = nx.path_graph(3)
834
+ >>> G.add_edge(1, 2, foo="bar")
835
+ >>> list(G.edges(data="foo", default="biz"))
836
+ [(0, 1, 'biz'), (1, 2, 'bar')]
837
+ >>> assert (0, 1, "biz") in G.edges(data="foo", default="biz")
838
+ """
839
+
840
+ __slots__ = ()
841
+
842
+ def __len__(self):
843
+ return sum(1 for e in self)
844
+
845
+ def __iter__(self):
846
+ seen = {}
847
+ for n, nbrs in self._nodes_nbrs():
848
+ for nbr, dd in nbrs.items():
849
+ if nbr not in seen:
850
+ yield self._report(n, nbr, dd)
851
+ seen[n] = 1
852
+ del seen
853
+
854
+ def __contains__(self, e):
855
+ u, v = e[:2]
856
+ if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
857
+ return False # this edge doesn't start and it doesn't end in nbunch
858
+ try:
859
+ ddict = self._adjdict[u][v]
860
+ except KeyError:
861
+ return False
862
+ return e == self._report(u, v, ddict)
863
+
864
+
865
+ class InEdgeDataView(OutEdgeDataView):
866
+ """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView"""
867
+
868
+ __slots__ = ()
869
+
870
+ def __iter__(self):
871
+ return (
872
+ self._report(nbr, n, dd)
873
+ for n, nbrs in self._nodes_nbrs()
874
+ for nbr, dd in nbrs.items()
875
+ )
876
+
877
+ def __contains__(self, e):
878
+ u, v = e[:2]
879
+ if self._nbunch is not None and v not in self._nbunch:
880
+ return False # this edge doesn't end in nbunch
881
+ try:
882
+ ddict = self._adjdict[v][u]
883
+ except KeyError:
884
+ return False
885
+ return e == self._report(u, v, ddict)
886
+
887
+
888
+ class OutMultiEdgeDataView(OutEdgeDataView):
889
+ """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView"""
890
+
891
+ __slots__ = ("keys",)
892
+
893
+ def __getstate__(self):
894
+ return {
895
+ "viewer": self._viewer,
896
+ "nbunch": self._nbunch,
897
+ "keys": self.keys,
898
+ "data": self._data,
899
+ "default": self._default,
900
+ }
901
+
902
+ def __setstate__(self, state):
903
+ self.__init__(**state)
904
+
905
+ def __init__(self, viewer, nbunch=None, data=False, *, default=None, keys=False):
906
+ self._viewer = viewer
907
+ adjdict = self._adjdict = viewer._adjdict
908
+ self.keys = keys
909
+ if nbunch is None:
910
+ self._nodes_nbrs = adjdict.items
911
+ else:
912
+ # dict retains order of nodes but acts like a set
913
+ nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
914
+ self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
915
+ self._nbunch = nbunch
916
+ self._data = data
917
+ self._default = default
918
+ # Set _report based on data and default
919
+ if data is True:
920
+ if keys is True:
921
+ self._report = lambda n, nbr, k, dd: (n, nbr, k, dd)
922
+ else:
923
+ self._report = lambda n, nbr, k, dd: (n, nbr, dd)
924
+ elif data is False:
925
+ if keys is True:
926
+ self._report = lambda n, nbr, k, dd: (n, nbr, k)
927
+ else:
928
+ self._report = lambda n, nbr, k, dd: (n, nbr)
929
+ else: # data is attribute name
930
+ if keys is True:
931
+ self._report = (
932
+ lambda n, nbr, k, dd: (n, nbr, k, dd[data])
933
+ if data in dd
934
+ else (n, nbr, k, default)
935
+ )
936
+ else:
937
+ self._report = (
938
+ lambda n, nbr, k, dd: (n, nbr, dd[data])
939
+ if data in dd
940
+ else (n, nbr, default)
941
+ )
942
+
943
+ def __len__(self):
944
+ return sum(1 for e in self)
945
+
946
+ def __iter__(self):
947
+ return (
948
+ self._report(n, nbr, k, dd)
949
+ for n, nbrs in self._nodes_nbrs()
950
+ for nbr, kd in nbrs.items()
951
+ for k, dd in kd.items()
952
+ )
953
+
954
+ def __contains__(self, e):
955
+ u, v = e[:2]
956
+ if self._nbunch is not None and u not in self._nbunch:
957
+ return False # this edge doesn't start in nbunch
958
+ try:
959
+ kdict = self._adjdict[u][v]
960
+ except KeyError:
961
+ return False
962
+ if self.keys is True:
963
+ k = e[2]
964
+ try:
965
+ dd = kdict[k]
966
+ except KeyError:
967
+ return False
968
+ return e == self._report(u, v, k, dd)
969
+ return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
970
+
971
+
972
+ class MultiEdgeDataView(OutMultiEdgeDataView):
973
+ """An EdgeDataView class for edges of MultiGraph; See EdgeDataView"""
974
+
975
+ __slots__ = ()
976
+
977
+ def __iter__(self):
978
+ seen = {}
979
+ for n, nbrs in self._nodes_nbrs():
980
+ for nbr, kd in nbrs.items():
981
+ if nbr not in seen:
982
+ for k, dd in kd.items():
983
+ yield self._report(n, nbr, k, dd)
984
+ seen[n] = 1
985
+ del seen
986
+
987
+ def __contains__(self, e):
988
+ u, v = e[:2]
989
+ if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
990
+ return False # this edge doesn't start and doesn't end in nbunch
991
+ try:
992
+ kdict = self._adjdict[u][v]
993
+ except KeyError:
994
+ try:
995
+ kdict = self._adjdict[v][u]
996
+ except KeyError:
997
+ return False
998
+ if self.keys is True:
999
+ k = e[2]
1000
+ try:
1001
+ dd = kdict[k]
1002
+ except KeyError:
1003
+ return False
1004
+ return e == self._report(u, v, k, dd)
1005
+ return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
1006
+
1007
+
1008
+ class InMultiEdgeDataView(OutMultiEdgeDataView):
1009
+ """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView"""
1010
+
1011
+ __slots__ = ()
1012
+
1013
+ def __iter__(self):
1014
+ return (
1015
+ self._report(nbr, n, k, dd)
1016
+ for n, nbrs in self._nodes_nbrs()
1017
+ for nbr, kd in nbrs.items()
1018
+ for k, dd in kd.items()
1019
+ )
1020
+
1021
+ def __contains__(self, e):
1022
+ u, v = e[:2]
1023
+ if self._nbunch is not None and v not in self._nbunch:
1024
+ return False # this edge doesn't end in nbunch
1025
+ try:
1026
+ kdict = self._adjdict[v][u]
1027
+ except KeyError:
1028
+ return False
1029
+ if self.keys is True:
1030
+ k = e[2]
1031
+ dd = kdict[k]
1032
+ return e == self._report(u, v, k, dd)
1033
+ return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
1034
+
1035
+
1036
+ # EdgeViews have set operations and no data reported
1037
+ class OutEdgeView(Set, Mapping):
1038
+ """A EdgeView class for outward edges of a DiGraph"""
1039
+
1040
+ __slots__ = ("_adjdict", "_graph", "_nodes_nbrs")
1041
+
1042
+ def __getstate__(self):
1043
+ return {"_graph": self._graph, "_adjdict": self._adjdict}
1044
+
1045
+ def __setstate__(self, state):
1046
+ self._graph = state["_graph"]
1047
+ self._adjdict = state["_adjdict"]
1048
+ self._nodes_nbrs = self._adjdict.items
1049
+
1050
+ @classmethod
1051
+ def _from_iterable(cls, it):
1052
+ return set(it)
1053
+
1054
+ dataview = OutEdgeDataView
1055
+
1056
+ def __init__(self, G):
1057
+ self._graph = G
1058
+ self._adjdict = G._succ if hasattr(G, "succ") else G._adj
1059
+ self._nodes_nbrs = self._adjdict.items
1060
+
1061
+ # Set methods
1062
+ def __len__(self):
1063
+ return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
1064
+
1065
+ def __iter__(self):
1066
+ for n, nbrs in self._nodes_nbrs():
1067
+ for nbr in nbrs:
1068
+ yield (n, nbr)
1069
+
1070
+ def __contains__(self, e):
1071
+ try:
1072
+ u, v = e
1073
+ return v in self._adjdict[u]
1074
+ except KeyError:
1075
+ return False
1076
+
1077
+ # Mapping Methods
1078
+ def __getitem__(self, e):
1079
+ if isinstance(e, slice):
1080
+ raise nx.NetworkXError(
1081
+ f"{type(self).__name__} does not support slicing, "
1082
+ f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
1083
+ )
1084
+ u, v = e
1085
+ return self._adjdict[u][v]
1086
+
1087
+ # EdgeDataView methods
1088
+ def __call__(self, nbunch=None, data=False, *, default=None):
1089
+ if nbunch is None and data is False:
1090
+ return self
1091
+ return self.dataview(self, nbunch, data, default=default)
1092
+
1093
+ def data(self, data=True, default=None, nbunch=None):
1094
+ """
1095
+ Return a read-only view of edge data.
1096
+
1097
+ Parameters
1098
+ ----------
1099
+ data : bool or edge attribute key
1100
+ If ``data=True``, then the data view maps each edge to a dictionary
1101
+ containing all of its attributes. If `data` is a key in the edge
1102
+ dictionary, then the data view maps each edge to its value for
1103
+ the keyed attribute. In this case, if the edge doesn't have the
1104
+ attribute, the `default` value is returned.
1105
+ default : object, default=None
1106
+ The value used when an edge does not have a specific attribute
1107
+ nbunch : container of nodes, optional (default=None)
1108
+ Allows restriction to edges only involving certain nodes. All edges
1109
+ are considered by default.
1110
+
1111
+ Returns
1112
+ -------
1113
+ dataview
1114
+ Returns an `EdgeDataView` for undirected Graphs, `OutEdgeDataView`
1115
+ for DiGraphs, `MultiEdgeDataView` for MultiGraphs and
1116
+ `OutMultiEdgeDataView` for MultiDiGraphs.
1117
+
1118
+ Notes
1119
+ -----
1120
+ If ``data=False``, returns an `EdgeView` without any edge data.
1121
+
1122
+ See Also
1123
+ --------
1124
+ EdgeDataView
1125
+ OutEdgeDataView
1126
+ MultiEdgeDataView
1127
+ OutMultiEdgeDataView
1128
+
1129
+ Examples
1130
+ --------
1131
+ >>> G = nx.Graph()
1132
+ >>> G.add_edges_from([
1133
+ ... (0, 1, {"dist": 3, "capacity": 20}),
1134
+ ... (1, 2, {"dist": 4}),
1135
+ ... (2, 0, {"dist": 5})
1136
+ ... ])
1137
+
1138
+ Accessing edge data with ``data=True`` (the default) returns an
1139
+ edge data view object listing each edge with all of its attributes:
1140
+
1141
+ >>> G.edges.data()
1142
+ EdgeDataView([(0, 1, {'dist': 3, 'capacity': 20}), (0, 2, {'dist': 5}), (1, 2, {'dist': 4})])
1143
+
1144
+ If `data` represents a key in the edge attribute dict, a dataview listing
1145
+ each edge with its value for that specific key is returned:
1146
+
1147
+ >>> G.edges.data("dist")
1148
+ EdgeDataView([(0, 1, 3), (0, 2, 5), (1, 2, 4)])
1149
+
1150
+ `nbunch` can be used to limit the edges:
1151
+
1152
+ >>> G.edges.data("dist", nbunch=[0])
1153
+ EdgeDataView([(0, 1, 3), (0, 2, 5)])
1154
+
1155
+ If a specific key is not found in an edge attribute dict, the value
1156
+ specified by `default` is used:
1157
+
1158
+ >>> G.edges.data("capacity")
1159
+ EdgeDataView([(0, 1, 20), (0, 2, None), (1, 2, None)])
1160
+
1161
+ Note that there is no check that the `data` key is present in any of
1162
+ the edge attribute dictionaries:
1163
+
1164
+ >>> G.edges.data("speed")
1165
+ EdgeDataView([(0, 1, None), (0, 2, None), (1, 2, None)])
1166
+ """
1167
+ if nbunch is None and data is False:
1168
+ return self
1169
+ return self.dataview(self, nbunch, data, default=default)
1170
+
1171
+ # String Methods
1172
+ def __str__(self):
1173
+ return str(list(self))
1174
+
1175
+ def __repr__(self):
1176
+ return f"{self.__class__.__name__}({list(self)})"
1177
+
1178
+
1179
+ class EdgeView(OutEdgeView):
1180
+ """A EdgeView class for edges of a Graph
1181
+
1182
+ This densely packed View allows iteration over edges, data lookup
1183
+ like a dict and set operations on edges represented by node-tuples.
1184
+ In addition, edge data can be controlled by calling this object
1185
+ possibly creating an EdgeDataView. Typically edges are iterated over
1186
+ and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples
1187
+ for multigraphs. Those edge representations can also be using to
1188
+ lookup the data dict for any edge. Set operations also are available
1189
+ where those tuples are the elements of the set.
1190
+ Calling this object with optional arguments `data`, `default` and `keys`
1191
+ controls the form of the tuple (see EdgeDataView). Optional argument
1192
+ `nbunch` allows restriction to edges only involving certain nodes.
1193
+
1194
+ If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
1195
+ If `data is True` iterate over 3-tuples `(u, v, datadict)`.
1196
+ Otherwise iterate over `(u, v, datadict.get(data, default))`.
1197
+ For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above.
1198
+
1199
+ Parameters
1200
+ ==========
1201
+ graph : NetworkX graph-like class
1202
+ nbunch : (default= all nodes in graph) only report edges with these nodes
1203
+ keys : (only for MultiGraph. default=False) report edge key in tuple
1204
+ data : bool or string (default=False) see above
1205
+ default : object (default=None)
1206
+
1207
+ Examples
1208
+ ========
1209
+ >>> G = nx.path_graph(4)
1210
+ >>> EV = G.edges()
1211
+ >>> (2, 3) in EV
1212
+ True
1213
+ >>> for u, v in EV:
1214
+ ... print((u, v))
1215
+ (0, 1)
1216
+ (1, 2)
1217
+ (2, 3)
1218
+ >>> assert EV & {(1, 2), (3, 4)} == {(1, 2)}
1219
+
1220
+ >>> EVdata = G.edges(data="color", default="aqua")
1221
+ >>> G.add_edge(2, 3, color="blue")
1222
+ >>> assert (2, 3, "blue") in EVdata
1223
+ >>> for u, v, c in EVdata:
1224
+ ... print(f"({u}, {v}) has color: {c}")
1225
+ (0, 1) has color: aqua
1226
+ (1, 2) has color: aqua
1227
+ (2, 3) has color: blue
1228
+
1229
+ >>> EVnbunch = G.edges(nbunch=2)
1230
+ >>> assert (2, 3) in EVnbunch
1231
+ >>> assert (0, 1) not in EVnbunch
1232
+ >>> for u, v in EVnbunch:
1233
+ ... assert u == 2 or v == 2
1234
+
1235
+ >>> MG = nx.path_graph(4, create_using=nx.MultiGraph)
1236
+ >>> EVmulti = MG.edges(keys=True)
1237
+ >>> (2, 3, 0) in EVmulti
1238
+ True
1239
+ >>> (2, 3) in EVmulti # 2-tuples work even when keys is True
1240
+ True
1241
+ >>> key = MG.add_edge(2, 3)
1242
+ >>> for u, v, k in EVmulti:
1243
+ ... print((u, v, k))
1244
+ (0, 1, 0)
1245
+ (1, 2, 0)
1246
+ (2, 3, 0)
1247
+ (2, 3, 1)
1248
+ """
1249
+
1250
+ __slots__ = ()
1251
+
1252
+ dataview = EdgeDataView
1253
+
1254
+ def __len__(self):
1255
+ num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs())
1256
+ return sum(num_nbrs) // 2
1257
+
1258
+ def __iter__(self):
1259
+ seen = {}
1260
+ for n, nbrs in self._nodes_nbrs():
1261
+ for nbr in list(nbrs):
1262
+ if nbr not in seen:
1263
+ yield (n, nbr)
1264
+ seen[n] = 1
1265
+ del seen
1266
+
1267
+ def __contains__(self, e):
1268
+ try:
1269
+ u, v = e[:2]
1270
+ return v in self._adjdict[u] or u in self._adjdict[v]
1271
+ except (KeyError, ValueError):
1272
+ return False
1273
+
1274
+
1275
+ class InEdgeView(OutEdgeView):
1276
+ """A EdgeView class for inward edges of a DiGraph"""
1277
+
1278
+ __slots__ = ()
1279
+
1280
+ def __setstate__(self, state):
1281
+ self._graph = state["_graph"]
1282
+ self._adjdict = state["_adjdict"]
1283
+ self._nodes_nbrs = self._adjdict.items
1284
+
1285
+ dataview = InEdgeDataView
1286
+
1287
+ def __init__(self, G):
1288
+ self._graph = G
1289
+ self._adjdict = G._pred if hasattr(G, "pred") else G._adj
1290
+ self._nodes_nbrs = self._adjdict.items
1291
+
1292
+ def __iter__(self):
1293
+ for n, nbrs in self._nodes_nbrs():
1294
+ for nbr in nbrs:
1295
+ yield (nbr, n)
1296
+
1297
+ def __contains__(self, e):
1298
+ try:
1299
+ u, v = e
1300
+ return u in self._adjdict[v]
1301
+ except KeyError:
1302
+ return False
1303
+
1304
+ def __getitem__(self, e):
1305
+ if isinstance(e, slice):
1306
+ raise nx.NetworkXError(
1307
+ f"{type(self).__name__} does not support slicing, "
1308
+ f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
1309
+ )
1310
+ u, v = e
1311
+ return self._adjdict[v][u]
1312
+
1313
+
1314
+ class OutMultiEdgeView(OutEdgeView):
1315
+ """A EdgeView class for outward edges of a MultiDiGraph"""
1316
+
1317
+ __slots__ = ()
1318
+
1319
+ dataview = OutMultiEdgeDataView
1320
+
1321
+ def __len__(self):
1322
+ return sum(
1323
+ len(kdict) for n, nbrs in self._nodes_nbrs() for nbr, kdict in nbrs.items()
1324
+ )
1325
+
1326
+ def __iter__(self):
1327
+ for n, nbrs in self._nodes_nbrs():
1328
+ for nbr, kdict in nbrs.items():
1329
+ for key in kdict:
1330
+ yield (n, nbr, key)
1331
+
1332
+ def __contains__(self, e):
1333
+ N = len(e)
1334
+ if N == 3:
1335
+ u, v, k = e
1336
+ elif N == 2:
1337
+ u, v = e
1338
+ k = 0
1339
+ else:
1340
+ raise ValueError("MultiEdge must have length 2 or 3")
1341
+ try:
1342
+ return k in self._adjdict[u][v]
1343
+ except KeyError:
1344
+ return False
1345
+
1346
+ def __getitem__(self, e):
1347
+ if isinstance(e, slice):
1348
+ raise nx.NetworkXError(
1349
+ f"{type(self).__name__} does not support slicing, "
1350
+ f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
1351
+ )
1352
+ u, v, k = e
1353
+ return self._adjdict[u][v][k]
1354
+
1355
+ def __call__(self, nbunch=None, data=False, *, default=None, keys=False):
1356
+ if nbunch is None and data is False and keys is True:
1357
+ return self
1358
+ return self.dataview(self, nbunch, data, default=default, keys=keys)
1359
+
1360
+ def data(self, data=True, default=None, nbunch=None, keys=False):
1361
+ if nbunch is None and data is False and keys is True:
1362
+ return self
1363
+ return self.dataview(self, nbunch, data, default=default, keys=keys)
1364
+
1365
+
1366
+ class MultiEdgeView(OutMultiEdgeView):
1367
+ """A EdgeView class for edges of a MultiGraph"""
1368
+
1369
+ __slots__ = ()
1370
+
1371
+ dataview = MultiEdgeDataView
1372
+
1373
+ def __len__(self):
1374
+ return sum(1 for e in self)
1375
+
1376
+ def __iter__(self):
1377
+ seen = {}
1378
+ for n, nbrs in self._nodes_nbrs():
1379
+ for nbr, kd in nbrs.items():
1380
+ if nbr not in seen:
1381
+ for k, dd in kd.items():
1382
+ yield (n, nbr, k)
1383
+ seen[n] = 1
1384
+ del seen
1385
+
1386
+
1387
+ class InMultiEdgeView(OutMultiEdgeView):
1388
+ """A EdgeView class for inward edges of a MultiDiGraph"""
1389
+
1390
+ __slots__ = ()
1391
+
1392
+ def __setstate__(self, state):
1393
+ self._graph = state["_graph"]
1394
+ self._adjdict = state["_adjdict"]
1395
+ self._nodes_nbrs = self._adjdict.items
1396
+
1397
+ dataview = InMultiEdgeDataView
1398
+
1399
+ def __init__(self, G):
1400
+ self._graph = G
1401
+ self._adjdict = G._pred if hasattr(G, "pred") else G._adj
1402
+ self._nodes_nbrs = self._adjdict.items
1403
+
1404
+ def __iter__(self):
1405
+ for n, nbrs in self._nodes_nbrs():
1406
+ for nbr, kdict in nbrs.items():
1407
+ for key in kdict:
1408
+ yield (nbr, n, key)
1409
+
1410
+ def __contains__(self, e):
1411
+ N = len(e)
1412
+ if N == 3:
1413
+ u, v, k = e
1414
+ elif N == 2:
1415
+ u, v = e
1416
+ k = 0
1417
+ else:
1418
+ raise ValueError("MultiEdge must have length 2 or 3")
1419
+ try:
1420
+ return k in self._adjdict[v][u]
1421
+ except KeyError:
1422
+ return False
1423
+
1424
+ def __getitem__(self, e):
1425
+ if isinstance(e, slice):
1426
+ raise nx.NetworkXError(
1427
+ f"{type(self).__name__} does not support slicing, "
1428
+ f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
1429
+ )
1430
+ u, v, k = e
1431
+ return self._adjdict[v][u][k]
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__init__.py ADDED
File without changes
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/dispatch_interface.cpython-311.pyc ADDED
Binary file (10.2 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/historical_tests.cpython-311.pyc ADDED
Binary file (31.9 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_digraph.cpython-311.pyc ADDED
Binary file (29.9 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_digraph_historical.cpython-311.pyc ADDED
Binary file (10.2 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_filters.cpython-311.pyc ADDED
Binary file (12.1 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_function.cpython-311.pyc ADDED
Binary file (52.6 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_graph.cpython-311.pyc ADDED
Binary file (68.3 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/__pycache__/test_subgraphviews.cpython-311.pyc ADDED
Binary file (26.4 kB). View file
 
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/dispatch_interface.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file contains utilities for testing the dispatching feature
2
+
3
+ # A full test of all dispatchable algorithms is performed by
4
+ # modifying the pytest invocation and setting an environment variable
5
+ # NETWORKX_TEST_BACKEND=nx-loopback pytest
6
+ # This is comprehensive, but only tests the `test_override_dispatch`
7
+ # function in networkx.classes.backends.
8
+
9
+ # To test the `_dispatch` function directly, several tests scattered throughout
10
+ # NetworkX have been augmented to test normal and dispatch mode.
11
+ # Searching for `dispatch_interface` should locate the specific tests.
12
+
13
+ import networkx as nx
14
+ from networkx import DiGraph, Graph, MultiDiGraph, MultiGraph, PlanarEmbedding
15
+ from networkx.classes.reportviews import NodeView
16
+
17
+
18
+ class LoopbackGraph(Graph):
19
+ __networkx_backend__ = "nx-loopback"
20
+
21
+
22
+ class LoopbackDiGraph(DiGraph):
23
+ __networkx_backend__ = "nx-loopback"
24
+
25
+
26
+ class LoopbackMultiGraph(MultiGraph):
27
+ __networkx_backend__ = "nx-loopback"
28
+
29
+
30
+ class LoopbackMultiDiGraph(MultiDiGraph):
31
+ __networkx_backend__ = "nx-loopback"
32
+
33
+
34
+ class LoopbackPlanarEmbedding(PlanarEmbedding):
35
+ __networkx_backend__ = "nx-loopback"
36
+
37
+
38
+ def convert(graph):
39
+ if isinstance(graph, PlanarEmbedding):
40
+ return LoopbackPlanarEmbedding(graph)
41
+ if isinstance(graph, MultiDiGraph):
42
+ return LoopbackMultiDiGraph(graph)
43
+ if isinstance(graph, MultiGraph):
44
+ return LoopbackMultiGraph(graph)
45
+ if isinstance(graph, DiGraph):
46
+ return LoopbackDiGraph(graph)
47
+ if isinstance(graph, Graph):
48
+ return LoopbackGraph(graph)
49
+ raise TypeError(f"Unsupported type of graph: {type(graph)}")
50
+
51
+
52
+ class LoopbackDispatcher:
53
+ def __getattr__(self, item):
54
+ try:
55
+ return nx.utils.backends._registered_algorithms[item].orig_func
56
+ except KeyError:
57
+ raise AttributeError(item) from None
58
+
59
+ @staticmethod
60
+ def convert_from_nx(
61
+ graph,
62
+ *,
63
+ edge_attrs=None,
64
+ node_attrs=None,
65
+ preserve_edge_attrs=None,
66
+ preserve_node_attrs=None,
67
+ preserve_graph_attrs=None,
68
+ name=None,
69
+ graph_name=None,
70
+ ):
71
+ if name in {
72
+ # Raise if input graph changes
73
+ "lexicographical_topological_sort",
74
+ "topological_generations",
75
+ "topological_sort",
76
+ # Sensitive tests (iteration order matters)
77
+ "dfs_labeled_edges",
78
+ }:
79
+ return graph
80
+ if isinstance(graph, NodeView):
81
+ # Convert to a Graph with only nodes (no edges)
82
+ new_graph = Graph()
83
+ new_graph.add_nodes_from(graph.items())
84
+ graph = new_graph
85
+ G = LoopbackGraph()
86
+ elif not isinstance(graph, Graph):
87
+ raise TypeError(
88
+ f"Bad type for graph argument {graph_name} in {name}: {type(graph)}"
89
+ )
90
+ elif graph.__class__ in {Graph, LoopbackGraph}:
91
+ G = LoopbackGraph()
92
+ elif graph.__class__ in {DiGraph, LoopbackDiGraph}:
93
+ G = LoopbackDiGraph()
94
+ elif graph.__class__ in {MultiGraph, LoopbackMultiGraph}:
95
+ G = LoopbackMultiGraph()
96
+ elif graph.__class__ in {MultiDiGraph, LoopbackMultiDiGraph}:
97
+ G = LoopbackMultiDiGraph()
98
+ elif graph.__class__ in {PlanarEmbedding, LoopbackPlanarEmbedding}:
99
+ G = LoopbackDiGraph() # or LoopbackPlanarEmbedding
100
+ else:
101
+ # It would be nice to be able to convert _AntiGraph to a regular Graph
102
+ # nx.algorithms.approximation.kcomponents._AntiGraph
103
+ # nx.algorithms.tree.branchings.MultiDiGraph_EdgeKey
104
+ # nx.classes.tests.test_multidigraph.MultiDiGraphSubClass
105
+ # nx.classes.tests.test_multigraph.MultiGraphSubClass
106
+ G = graph.__class__()
107
+
108
+ if preserve_graph_attrs:
109
+ G.graph.update(graph.graph)
110
+
111
+ if preserve_node_attrs:
112
+ G.add_nodes_from(graph.nodes(data=True))
113
+ elif node_attrs:
114
+ G.add_nodes_from(
115
+ (
116
+ node,
117
+ {
118
+ k: datadict.get(k, default)
119
+ for k, default in node_attrs.items()
120
+ if default is not None or k in datadict
121
+ },
122
+ )
123
+ for node, datadict in graph.nodes(data=True)
124
+ )
125
+ else:
126
+ G.add_nodes_from(graph)
127
+
128
+ if graph.is_multigraph():
129
+ if preserve_edge_attrs:
130
+ G.add_edges_from(
131
+ (u, v, key, datadict)
132
+ for u, nbrs in graph._adj.items()
133
+ for v, keydict in nbrs.items()
134
+ for key, datadict in keydict.items()
135
+ )
136
+ elif edge_attrs:
137
+ G.add_edges_from(
138
+ (
139
+ u,
140
+ v,
141
+ key,
142
+ {
143
+ k: datadict.get(k, default)
144
+ for k, default in edge_attrs.items()
145
+ if default is not None or k in datadict
146
+ },
147
+ )
148
+ for u, nbrs in graph._adj.items()
149
+ for v, keydict in nbrs.items()
150
+ for key, datadict in keydict.items()
151
+ )
152
+ else:
153
+ G.add_edges_from(
154
+ (u, v, key, {})
155
+ for u, nbrs in graph._adj.items()
156
+ for v, keydict in nbrs.items()
157
+ for key, datadict in keydict.items()
158
+ )
159
+ elif preserve_edge_attrs:
160
+ G.add_edges_from(graph.edges(data=True))
161
+ elif edge_attrs:
162
+ G.add_edges_from(
163
+ (
164
+ u,
165
+ v,
166
+ {
167
+ k: datadict.get(k, default)
168
+ for k, default in edge_attrs.items()
169
+ if default is not None or k in datadict
170
+ },
171
+ )
172
+ for u, v, datadict in graph.edges(data=True)
173
+ )
174
+ else:
175
+ G.add_edges_from(graph.edges)
176
+ return G
177
+
178
+ @staticmethod
179
+ def convert_to_nx(obj, *, name=None):
180
+ return obj
181
+
182
+ @staticmethod
183
+ def on_start_tests(items):
184
+ # Verify that items can be xfailed
185
+ for item in items:
186
+ assert hasattr(item, "add_marker")
187
+
188
+ def can_run(self, name, args, kwargs):
189
+ # It is unnecessary to define this function if algorithms are fully supported.
190
+ # We include it for illustration purposes.
191
+ return hasattr(self, name)
192
+
193
+
194
+ dispatcher = LoopbackDispatcher()
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_backends.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+
7
+ sp = pytest.importorskip("scipy")
8
+ pytest.importorskip("numpy")
9
+
10
+
11
+ def test_dispatch_kwds_vs_args():
12
+ G = nx.path_graph(4)
13
+ nx.pagerank(G)
14
+ nx.pagerank(G=G)
15
+ with pytest.raises(TypeError):
16
+ nx.pagerank()
17
+
18
+
19
+ def test_pickle():
20
+ for name, func in nx.utils.backends._registered_algorithms.items():
21
+ assert pickle.loads(pickle.dumps(func)) is func
22
+ assert pickle.loads(pickle.dumps(nx.inverse_line_graph)) is nx.inverse_line_graph
23
+
24
+
25
+ @pytest.mark.skipif(
26
+ "not nx._dispatch._automatic_backends "
27
+ "or nx._dispatch._automatic_backends[0] != 'nx-loopback'"
28
+ )
29
+ def test_graph_converter_needs_backend():
30
+ # When testing, `nx.from_scipy_sparse_array` will *always* call the backend
31
+ # implementation if it's implemented. If `backend=` isn't given, then the result
32
+ # will be converted back to NetworkX via `convert_to_nx`.
33
+ # If not testing, then calling `nx.from_scipy_sparse_array` w/o `backend=` will
34
+ # always call the original version. `backend=` is *required* to call the backend.
35
+ from networkx.classes.tests.dispatch_interface import (
36
+ LoopbackDispatcher,
37
+ LoopbackGraph,
38
+ )
39
+
40
+ A = sp.sparse.coo_array([[0, 3, 2], [3, 0, 1], [2, 1, 0]])
41
+
42
+ side_effects = []
43
+
44
+ def from_scipy_sparse_array(self, *args, **kwargs):
45
+ side_effects.append(1) # Just to prove this was called
46
+ return self.convert_from_nx(
47
+ self.__getattr__("from_scipy_sparse_array")(*args, **kwargs),
48
+ preserve_edge_attrs=None,
49
+ preserve_node_attrs=None,
50
+ preserve_graph_attrs=None,
51
+ )
52
+
53
+ @staticmethod
54
+ def convert_to_nx(obj, *, name=None):
55
+ if type(obj) is nx.Graph:
56
+ return obj
57
+ return nx.Graph(obj)
58
+
59
+ # *This mutates LoopbackDispatcher!*
60
+ orig_convert_to_nx = LoopbackDispatcher.convert_to_nx
61
+ LoopbackDispatcher.convert_to_nx = convert_to_nx
62
+ LoopbackDispatcher.from_scipy_sparse_array = from_scipy_sparse_array
63
+
64
+ try:
65
+ assert side_effects == []
66
+ assert type(nx.from_scipy_sparse_array(A)) is nx.Graph
67
+ assert side_effects == [1]
68
+ assert (
69
+ type(nx.from_scipy_sparse_array(A, backend="nx-loopback")) is LoopbackGraph
70
+ )
71
+ assert side_effects == [1, 1]
72
+ finally:
73
+ LoopbackDispatcher.convert_to_nx = staticmethod(orig_convert_to_nx)
74
+ del LoopbackDispatcher.from_scipy_sparse_array
75
+ with pytest.raises(ImportError, match="Unable to load"):
76
+ nx.from_scipy_sparse_array(A, backend="bad-backend-name")
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_coreviews.py ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+
7
+
8
+ class TestAtlasView:
9
+ # node->data
10
+ def setup_method(self):
11
+ self.d = {0: {"color": "blue", "weight": 1.2}, 1: {}, 2: {"color": 1}}
12
+ self.av = nx.classes.coreviews.AtlasView(self.d)
13
+
14
+ def test_pickle(self):
15
+ view = self.av
16
+ pview = pickle.loads(pickle.dumps(view, -1))
17
+ assert view == pview
18
+ assert view.__slots__ == pview.__slots__
19
+ pview = pickle.loads(pickle.dumps(view))
20
+ assert view == pview
21
+ assert view.__slots__ == pview.__slots__
22
+
23
+ def test_len(self):
24
+ assert len(self.av) == len(self.d)
25
+
26
+ def test_iter(self):
27
+ assert list(self.av) == list(self.d)
28
+
29
+ def test_getitem(self):
30
+ assert self.av[1] is self.d[1]
31
+ assert self.av[2]["color"] == 1
32
+ pytest.raises(KeyError, self.av.__getitem__, 3)
33
+
34
+ def test_copy(self):
35
+ avcopy = self.av.copy()
36
+ assert avcopy[0] == self.av[0]
37
+ assert avcopy == self.av
38
+ assert avcopy[0] is not self.av[0]
39
+ assert avcopy is not self.av
40
+ avcopy[5] = {}
41
+ assert avcopy != self.av
42
+
43
+ avcopy[0]["ht"] = 4
44
+ assert avcopy[0] != self.av[0]
45
+ self.av[0]["ht"] = 4
46
+ assert avcopy[0] == self.av[0]
47
+ del self.av[0]["ht"]
48
+
49
+ assert not hasattr(self.av, "__setitem__")
50
+
51
+ def test_items(self):
52
+ assert sorted(self.av.items()) == sorted(self.d.items())
53
+
54
+ def test_str(self):
55
+ out = str(self.d)
56
+ assert str(self.av) == out
57
+
58
+ def test_repr(self):
59
+ out = "AtlasView(" + str(self.d) + ")"
60
+ assert repr(self.av) == out
61
+
62
+
63
+ class TestAdjacencyView:
64
+ # node->nbr->data
65
+ def setup_method(self):
66
+ dd = {"color": "blue", "weight": 1.2}
67
+ self.nd = {0: dd, 1: {}, 2: {"color": 1}}
68
+ self.adj = {3: self.nd, 0: {3: dd}, 1: {}, 2: {3: {"color": 1}}}
69
+ self.adjview = nx.classes.coreviews.AdjacencyView(self.adj)
70
+
71
+ def test_pickle(self):
72
+ view = self.adjview
73
+ pview = pickle.loads(pickle.dumps(view, -1))
74
+ assert view == pview
75
+ assert view.__slots__ == pview.__slots__
76
+
77
+ def test_len(self):
78
+ assert len(self.adjview) == len(self.adj)
79
+
80
+ def test_iter(self):
81
+ assert list(self.adjview) == list(self.adj)
82
+
83
+ def test_getitem(self):
84
+ assert self.adjview[1] is not self.adj[1]
85
+ assert self.adjview[3][0] is self.adjview[0][3]
86
+ assert self.adjview[2][3]["color"] == 1
87
+ pytest.raises(KeyError, self.adjview.__getitem__, 4)
88
+
89
+ def test_copy(self):
90
+ avcopy = self.adjview.copy()
91
+ assert avcopy[0] == self.adjview[0]
92
+ assert avcopy[0] is not self.adjview[0]
93
+
94
+ avcopy[2][3]["ht"] = 4
95
+ assert avcopy[2] != self.adjview[2]
96
+ self.adjview[2][3]["ht"] = 4
97
+ assert avcopy[2] == self.adjview[2]
98
+ del self.adjview[2][3]["ht"]
99
+
100
+ assert not hasattr(self.adjview, "__setitem__")
101
+
102
+ def test_items(self):
103
+ view_items = sorted((n, dict(d)) for n, d in self.adjview.items())
104
+ assert view_items == sorted(self.adj.items())
105
+
106
+ def test_str(self):
107
+ out = str(dict(self.adj))
108
+ assert str(self.adjview) == out
109
+
110
+ def test_repr(self):
111
+ out = self.adjview.__class__.__name__ + "(" + str(self.adj) + ")"
112
+ assert repr(self.adjview) == out
113
+
114
+
115
+ class TestMultiAdjacencyView(TestAdjacencyView):
116
+ # node->nbr->key->data
117
+ def setup_method(self):
118
+ dd = {"color": "blue", "weight": 1.2}
119
+ self.kd = {0: dd, 1: {}, 2: {"color": 1}}
120
+ self.nd = {3: self.kd, 0: {3: dd}, 1: {0: {}}, 2: {3: {"color": 1}}}
121
+ self.adj = {3: self.nd, 0: {3: {3: dd}}, 1: {}, 2: {3: {8: {}}}}
122
+ self.adjview = nx.classes.coreviews.MultiAdjacencyView(self.adj)
123
+
124
+ def test_getitem(self):
125
+ assert self.adjview[1] is not self.adj[1]
126
+ assert self.adjview[3][0][3] is self.adjview[0][3][3]
127
+ assert self.adjview[3][2][3]["color"] == 1
128
+ pytest.raises(KeyError, self.adjview.__getitem__, 4)
129
+
130
+ def test_copy(self):
131
+ avcopy = self.adjview.copy()
132
+ assert avcopy[0] == self.adjview[0]
133
+ assert avcopy[0] is not self.adjview[0]
134
+
135
+ avcopy[2][3][8]["ht"] = 4
136
+ assert avcopy[2] != self.adjview[2]
137
+ self.adjview[2][3][8]["ht"] = 4
138
+ assert avcopy[2] == self.adjview[2]
139
+ del self.adjview[2][3][8]["ht"]
140
+
141
+ assert not hasattr(self.adjview, "__setitem__")
142
+
143
+
144
+ class TestUnionAtlas:
145
+ # node->data
146
+ def setup_method(self):
147
+ self.s = {0: {"color": "blue", "weight": 1.2}, 1: {}, 2: {"color": 1}}
148
+ self.p = {3: {"color": "blue", "weight": 1.2}, 4: {}, 2: {"watch": 2}}
149
+ self.av = nx.classes.coreviews.UnionAtlas(self.s, self.p)
150
+
151
+ def test_pickle(self):
152
+ view = self.av
153
+ pview = pickle.loads(pickle.dumps(view, -1))
154
+ assert view == pview
155
+ assert view.__slots__ == pview.__slots__
156
+
157
+ def test_len(self):
158
+ assert len(self.av) == len(self.s.keys() | self.p.keys()) == 5
159
+
160
+ def test_iter(self):
161
+ assert set(self.av) == set(self.s) | set(self.p)
162
+
163
+ def test_getitem(self):
164
+ assert self.av[0] is self.s[0]
165
+ assert self.av[4] is self.p[4]
166
+ assert self.av[2]["color"] == 1
167
+ pytest.raises(KeyError, self.av[2].__getitem__, "watch")
168
+ pytest.raises(KeyError, self.av.__getitem__, 8)
169
+
170
+ def test_copy(self):
171
+ avcopy = self.av.copy()
172
+ assert avcopy[0] == self.av[0]
173
+ assert avcopy[0] is not self.av[0]
174
+ assert avcopy is not self.av
175
+ avcopy[5] = {}
176
+ assert avcopy != self.av
177
+
178
+ avcopy[0]["ht"] = 4
179
+ assert avcopy[0] != self.av[0]
180
+ self.av[0]["ht"] = 4
181
+ assert avcopy[0] == self.av[0]
182
+ del self.av[0]["ht"]
183
+
184
+ assert not hasattr(self.av, "__setitem__")
185
+
186
+ def test_items(self):
187
+ expected = dict(self.p.items())
188
+ expected.update(self.s)
189
+ assert sorted(self.av.items()) == sorted(expected.items())
190
+
191
+ def test_str(self):
192
+ out = str(dict(self.av))
193
+ assert str(self.av) == out
194
+
195
+ def test_repr(self):
196
+ out = f"{self.av.__class__.__name__}({self.s}, {self.p})"
197
+ assert repr(self.av) == out
198
+
199
+
200
+ class TestUnionAdjacency:
201
+ # node->nbr->data
202
+ def setup_method(self):
203
+ dd = {"color": "blue", "weight": 1.2}
204
+ self.nd = {0: dd, 1: {}, 2: {"color": 1}}
205
+ self.s = {3: self.nd, 0: {}, 1: {}, 2: {3: {"color": 1}}}
206
+ self.p = {3: {}, 0: {3: dd}, 1: {0: {}}, 2: {1: {"color": 1}}}
207
+ self.adjview = nx.classes.coreviews.UnionAdjacency(self.s, self.p)
208
+
209
+ def test_pickle(self):
210
+ view = self.adjview
211
+ pview = pickle.loads(pickle.dumps(view, -1))
212
+ assert view == pview
213
+ assert view.__slots__ == pview.__slots__
214
+
215
+ def test_len(self):
216
+ assert len(self.adjview) == len(self.s)
217
+
218
+ def test_iter(self):
219
+ assert sorted(self.adjview) == sorted(self.s)
220
+
221
+ def test_getitem(self):
222
+ assert self.adjview[1] is not self.s[1]
223
+ assert self.adjview[3][0] is self.adjview[0][3]
224
+ assert self.adjview[2][3]["color"] == 1
225
+ pytest.raises(KeyError, self.adjview.__getitem__, 4)
226
+
227
+ def test_copy(self):
228
+ avcopy = self.adjview.copy()
229
+ assert avcopy[0] == self.adjview[0]
230
+ assert avcopy[0] is not self.adjview[0]
231
+
232
+ avcopy[2][3]["ht"] = 4
233
+ assert avcopy[2] != self.adjview[2]
234
+ self.adjview[2][3]["ht"] = 4
235
+ assert avcopy[2] == self.adjview[2]
236
+ del self.adjview[2][3]["ht"]
237
+
238
+ assert not hasattr(self.adjview, "__setitem__")
239
+
240
+ def test_str(self):
241
+ out = str(dict(self.adjview))
242
+ assert str(self.adjview) == out
243
+
244
+ def test_repr(self):
245
+ clsname = self.adjview.__class__.__name__
246
+ out = f"{clsname}({self.s}, {self.p})"
247
+ assert repr(self.adjview) == out
248
+
249
+
250
+ class TestUnionMultiInner(TestUnionAdjacency):
251
+ # nbr->key->data
252
+ def setup_method(self):
253
+ dd = {"color": "blue", "weight": 1.2}
254
+ self.kd = {7: {}, "ekey": {}, 9: {"color": 1}}
255
+ self.s = {3: self.kd, 0: {7: dd}, 1: {}, 2: {"key": {"color": 1}}}
256
+ self.p = {3: {}, 0: {3: dd}, 1: {}, 2: {1: {"span": 2}}}
257
+ self.adjview = nx.classes.coreviews.UnionMultiInner(self.s, self.p)
258
+
259
+ def test_len(self):
260
+ assert len(self.adjview) == len(self.s.keys() | self.p.keys()) == 4
261
+
262
+ def test_getitem(self):
263
+ assert self.adjview[1] is not self.s[1]
264
+ assert self.adjview[0][7] is self.adjview[0][3]
265
+ assert self.adjview[2]["key"]["color"] == 1
266
+ assert self.adjview[2][1]["span"] == 2
267
+ pytest.raises(KeyError, self.adjview.__getitem__, 4)
268
+ pytest.raises(KeyError, self.adjview[1].__getitem__, "key")
269
+
270
+ def test_copy(self):
271
+ avcopy = self.adjview.copy()
272
+ assert avcopy[0] == self.adjview[0]
273
+ assert avcopy[0] is not self.adjview[0]
274
+
275
+ avcopy[2][1]["width"] = 8
276
+ assert avcopy[2] != self.adjview[2]
277
+ self.adjview[2][1]["width"] = 8
278
+ assert avcopy[2] == self.adjview[2]
279
+ del self.adjview[2][1]["width"]
280
+
281
+ assert not hasattr(self.adjview, "__setitem__")
282
+ assert hasattr(avcopy, "__setitem__")
283
+
284
+
285
+ class TestUnionMultiAdjacency(TestUnionAdjacency):
286
+ # node->nbr->key->data
287
+ def setup_method(self):
288
+ dd = {"color": "blue", "weight": 1.2}
289
+ self.kd = {7: {}, 8: {}, 9: {"color": 1}}
290
+ self.nd = {3: self.kd, 0: {9: dd}, 1: {8: {}}, 2: {9: {"color": 1}}}
291
+ self.s = {3: self.nd, 0: {3: {7: dd}}, 1: {}, 2: {3: {8: {}}}}
292
+ self.p = {3: {}, 0: {3: {9: dd}}, 1: {}, 2: {1: {8: {}}}}
293
+ self.adjview = nx.classes.coreviews.UnionMultiAdjacency(self.s, self.p)
294
+
295
+ def test_getitem(self):
296
+ assert self.adjview[1] is not self.s[1]
297
+ assert self.adjview[3][0][9] is self.adjview[0][3][9]
298
+ assert self.adjview[3][2][9]["color"] == 1
299
+ pytest.raises(KeyError, self.adjview.__getitem__, 4)
300
+
301
+ def test_copy(self):
302
+ avcopy = self.adjview.copy()
303
+ assert avcopy[0] == self.adjview[0]
304
+ assert avcopy[0] is not self.adjview[0]
305
+
306
+ avcopy[2][3][8]["ht"] = 4
307
+ assert avcopy[2] != self.adjview[2]
308
+ self.adjview[2][3][8]["ht"] = 4
309
+ assert avcopy[2] == self.adjview[2]
310
+ del self.adjview[2][3][8]["ht"]
311
+
312
+ assert not hasattr(self.adjview, "__setitem__")
313
+ assert hasattr(avcopy, "__setitem__")
314
+
315
+
316
+ class TestFilteredGraphs:
317
+ def setup_method(self):
318
+ self.Graphs = [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
319
+
320
+ def test_hide_show_nodes(self):
321
+ SubGraph = nx.subgraph_view
322
+ for Graph in self.Graphs:
323
+ G = nx.path_graph(4, Graph)
324
+ SG = G.subgraph([2, 3])
325
+ RG = SubGraph(G, filter_node=nx.filters.hide_nodes([0, 1]))
326
+ assert SG.nodes == RG.nodes
327
+ assert SG.edges == RG.edges
328
+ SGC = SG.copy()
329
+ RGC = RG.copy()
330
+ assert SGC.nodes == RGC.nodes
331
+ assert SGC.edges == RGC.edges
332
+
333
+ def test_str_repr(self):
334
+ SubGraph = nx.subgraph_view
335
+ for Graph in self.Graphs:
336
+ G = nx.path_graph(4, Graph)
337
+ SG = G.subgraph([2, 3])
338
+ RG = SubGraph(G, filter_node=nx.filters.hide_nodes([0, 1]))
339
+ str(SG.adj)
340
+ str(RG.adj)
341
+ repr(SG.adj)
342
+ repr(RG.adj)
343
+ str(SG.adj[2])
344
+ str(RG.adj[2])
345
+ repr(SG.adj[2])
346
+ repr(RG.adj[2])
347
+
348
+ def test_copy(self):
349
+ SubGraph = nx.subgraph_view
350
+ for Graph in self.Graphs:
351
+ G = nx.path_graph(4, Graph)
352
+ SG = G.subgraph([2, 3])
353
+ RG = SubGraph(G, filter_node=nx.filters.hide_nodes([0, 1]))
354
+ RsG = SubGraph(G, filter_node=nx.filters.show_nodes([2, 3]))
355
+ assert G.adj.copy() == G.adj
356
+ assert G.adj[2].copy() == G.adj[2]
357
+ assert SG.adj.copy() == SG.adj
358
+ assert SG.adj[2].copy() == SG.adj[2]
359
+ assert RG.adj.copy() == RG.adj
360
+ assert RG.adj[2].copy() == RG.adj[2]
361
+ assert RsG.adj.copy() == RsG.adj
362
+ assert RsG.adj[2].copy() == RsG.adj[2]
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_digraph.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import networkx as nx
4
+ from networkx.utils import nodes_equal
5
+
6
+ from .test_graph import BaseAttrGraphTester, BaseGraphTester
7
+ from .test_graph import TestEdgeSubgraph as _TestGraphEdgeSubgraph
8
+ from .test_graph import TestGraph as _TestGraph
9
+
10
+
11
+ class BaseDiGraphTester(BaseGraphTester):
12
+ def test_has_successor(self):
13
+ G = self.K3
14
+ assert G.has_successor(0, 1)
15
+ assert not G.has_successor(0, -1)
16
+
17
+ def test_successors(self):
18
+ G = self.K3
19
+ assert sorted(G.successors(0)) == [1, 2]
20
+ with pytest.raises(nx.NetworkXError):
21
+ G.successors(-1)
22
+
23
+ def test_has_predecessor(self):
24
+ G = self.K3
25
+ assert G.has_predecessor(0, 1)
26
+ assert not G.has_predecessor(0, -1)
27
+
28
+ def test_predecessors(self):
29
+ G = self.K3
30
+ assert sorted(G.predecessors(0)) == [1, 2]
31
+ with pytest.raises(nx.NetworkXError):
32
+ G.predecessors(-1)
33
+
34
+ def test_edges(self):
35
+ G = self.K3
36
+ assert sorted(G.edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
37
+ assert sorted(G.edges(0)) == [(0, 1), (0, 2)]
38
+ assert sorted(G.edges([0, 1])) == [(0, 1), (0, 2), (1, 0), (1, 2)]
39
+ with pytest.raises(nx.NetworkXError):
40
+ G.edges(-1)
41
+
42
+ def test_out_edges(self):
43
+ G = self.K3
44
+ assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
45
+ assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)]
46
+ with pytest.raises(nx.NetworkXError):
47
+ G.out_edges(-1)
48
+
49
+ def test_out_edges_dir(self):
50
+ G = self.P3
51
+ assert sorted(G.out_edges()) == [(0, 1), (1, 2)]
52
+ assert sorted(G.out_edges(0)) == [(0, 1)]
53
+ assert sorted(G.out_edges(2)) == []
54
+
55
+ def test_out_edges_data(self):
56
+ G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
57
+ assert sorted(G.out_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
58
+ assert sorted(G.out_edges(0, data=True)) == [(0, 1, {"data": 0})]
59
+ assert sorted(G.out_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
60
+ assert sorted(G.out_edges(0, data="data")) == [(0, 1, 0)]
61
+
62
+ def test_in_edges_dir(self):
63
+ G = self.P3
64
+ assert sorted(G.in_edges()) == [(0, 1), (1, 2)]
65
+ assert sorted(G.in_edges(0)) == []
66
+ assert sorted(G.in_edges(2)) == [(1, 2)]
67
+
68
+ def test_in_edges_data(self):
69
+ G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
70
+ assert sorted(G.in_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
71
+ assert sorted(G.in_edges(1, data=True)) == [(0, 1, {"data": 0})]
72
+ assert sorted(G.in_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
73
+ assert sorted(G.in_edges(1, data="data")) == [(0, 1, 0)]
74
+
75
+ def test_degree(self):
76
+ G = self.K3
77
+ assert sorted(G.degree()) == [(0, 4), (1, 4), (2, 4)]
78
+ assert dict(G.degree()) == {0: 4, 1: 4, 2: 4}
79
+ assert G.degree(0) == 4
80
+ assert list(G.degree(iter([0]))) == [(0, 4)] # run through iterator
81
+
82
+ def test_in_degree(self):
83
+ G = self.K3
84
+ assert sorted(G.in_degree()) == [(0, 2), (1, 2), (2, 2)]
85
+ assert dict(G.in_degree()) == {0: 2, 1: 2, 2: 2}
86
+ assert G.in_degree(0) == 2
87
+ assert list(G.in_degree(iter([0]))) == [(0, 2)] # run through iterator
88
+
89
+ def test_out_degree(self):
90
+ G = self.K3
91
+ assert sorted(G.out_degree()) == [(0, 2), (1, 2), (2, 2)]
92
+ assert dict(G.out_degree()) == {0: 2, 1: 2, 2: 2}
93
+ assert G.out_degree(0) == 2
94
+ assert list(G.out_degree(iter([0]))) == [(0, 2)]
95
+
96
+ def test_size(self):
97
+ G = self.K3
98
+ assert G.size() == 6
99
+ assert G.number_of_edges() == 6
100
+
101
+ def test_to_undirected_reciprocal(self):
102
+ G = self.Graph()
103
+ G.add_edge(1, 2)
104
+ assert G.to_undirected().has_edge(1, 2)
105
+ assert not G.to_undirected(reciprocal=True).has_edge(1, 2)
106
+ G.add_edge(2, 1)
107
+ assert G.to_undirected(reciprocal=True).has_edge(1, 2)
108
+
109
+ def test_reverse_copy(self):
110
+ G = nx.DiGraph([(0, 1), (1, 2)])
111
+ R = G.reverse()
112
+ assert sorted(R.edges()) == [(1, 0), (2, 1)]
113
+ R.remove_edge(1, 0)
114
+ assert sorted(R.edges()) == [(2, 1)]
115
+ assert sorted(G.edges()) == [(0, 1), (1, 2)]
116
+
117
+ def test_reverse_nocopy(self):
118
+ G = nx.DiGraph([(0, 1), (1, 2)])
119
+ R = G.reverse(copy=False)
120
+ assert sorted(R.edges()) == [(1, 0), (2, 1)]
121
+ with pytest.raises(nx.NetworkXError):
122
+ R.remove_edge(1, 0)
123
+
124
+ def test_reverse_hashable(self):
125
+ class Foo:
126
+ pass
127
+
128
+ x = Foo()
129
+ y = Foo()
130
+ G = nx.DiGraph()
131
+ G.add_edge(x, y)
132
+ assert nodes_equal(G.nodes(), G.reverse().nodes())
133
+ assert [(y, x)] == list(G.reverse().edges())
134
+
135
+ def test_di_cache_reset(self):
136
+ G = self.K3.copy()
137
+ old_succ = G.succ
138
+ assert id(G.succ) == id(old_succ)
139
+ old_adj = G.adj
140
+ assert id(G.adj) == id(old_adj)
141
+
142
+ G._succ = {}
143
+ assert id(G.succ) != id(old_succ)
144
+ assert id(G.adj) != id(old_adj)
145
+
146
+ old_pred = G.pred
147
+ assert id(G.pred) == id(old_pred)
148
+ G._pred = {}
149
+ assert id(G.pred) != id(old_pred)
150
+
151
+ def test_di_attributes_cached(self):
152
+ G = self.K3.copy()
153
+ assert id(G.in_edges) == id(G.in_edges)
154
+ assert id(G.out_edges) == id(G.out_edges)
155
+ assert id(G.in_degree) == id(G.in_degree)
156
+ assert id(G.out_degree) == id(G.out_degree)
157
+ assert id(G.succ) == id(G.succ)
158
+ assert id(G.pred) == id(G.pred)
159
+
160
+
161
+ class BaseAttrDiGraphTester(BaseDiGraphTester, BaseAttrGraphTester):
162
+ def test_edges_data(self):
163
+ G = self.K3
164
+ all_edges = [
165
+ (0, 1, {}),
166
+ (0, 2, {}),
167
+ (1, 0, {}),
168
+ (1, 2, {}),
169
+ (2, 0, {}),
170
+ (2, 1, {}),
171
+ ]
172
+ assert sorted(G.edges(data=True)) == all_edges
173
+ assert sorted(G.edges(0, data=True)) == all_edges[:2]
174
+ assert sorted(G.edges([0, 1], data=True)) == all_edges[:4]
175
+ with pytest.raises(nx.NetworkXError):
176
+ G.edges(-1, True)
177
+
178
+ def test_in_degree_weighted(self):
179
+ G = self.K3.copy()
180
+ G.add_edge(0, 1, weight=0.3, other=1.2)
181
+ assert sorted(G.in_degree(weight="weight")) == [(0, 2), (1, 1.3), (2, 2)]
182
+ assert dict(G.in_degree(weight="weight")) == {0: 2, 1: 1.3, 2: 2}
183
+ assert G.in_degree(1, weight="weight") == 1.3
184
+ assert sorted(G.in_degree(weight="other")) == [(0, 2), (1, 2.2), (2, 2)]
185
+ assert dict(G.in_degree(weight="other")) == {0: 2, 1: 2.2, 2: 2}
186
+ assert G.in_degree(1, weight="other") == 2.2
187
+ assert list(G.in_degree(iter([1]), weight="other")) == [(1, 2.2)]
188
+
189
+ def test_out_degree_weighted(self):
190
+ G = self.K3.copy()
191
+ G.add_edge(0, 1, weight=0.3, other=1.2)
192
+ assert sorted(G.out_degree(weight="weight")) == [(0, 1.3), (1, 2), (2, 2)]
193
+ assert dict(G.out_degree(weight="weight")) == {0: 1.3, 1: 2, 2: 2}
194
+ assert G.out_degree(0, weight="weight") == 1.3
195
+ assert sorted(G.out_degree(weight="other")) == [(0, 2.2), (1, 2), (2, 2)]
196
+ assert dict(G.out_degree(weight="other")) == {0: 2.2, 1: 2, 2: 2}
197
+ assert G.out_degree(0, weight="other") == 2.2
198
+ assert list(G.out_degree(iter([0]), weight="other")) == [(0, 2.2)]
199
+
200
+
201
+ class TestDiGraph(BaseAttrDiGraphTester, _TestGraph):
202
+ """Tests specific to dict-of-dict-of-dict digraph data structure"""
203
+
204
+ def setup_method(self):
205
+ self.Graph = nx.DiGraph
206
+ # build dict-of-dict-of-dict K3
207
+ ed1, ed2, ed3, ed4, ed5, ed6 = ({}, {}, {}, {}, {}, {})
208
+ self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed3, 2: ed4}, 2: {0: ed5, 1: ed6}}
209
+ self.k3edges = [(0, 1), (0, 2), (1, 2)]
210
+ self.k3nodes = [0, 1, 2]
211
+ self.K3 = self.Graph()
212
+ self.K3._succ = self.k3adj # K3._adj is synced with K3._succ
213
+ self.K3._pred = {0: {1: ed3, 2: ed5}, 1: {0: ed1, 2: ed6}, 2: {0: ed2, 1: ed4}}
214
+ self.K3._node = {}
215
+ self.K3._node[0] = {}
216
+ self.K3._node[1] = {}
217
+ self.K3._node[2] = {}
218
+
219
+ ed1, ed2 = ({}, {})
220
+ self.P3 = self.Graph()
221
+ self.P3._succ = {0: {1: ed1}, 1: {2: ed2}, 2: {}}
222
+ self.P3._pred = {0: {}, 1: {0: ed1}, 2: {1: ed2}}
223
+ # P3._adj is synced with P3._succ
224
+ self.P3._node = {}
225
+ self.P3._node[0] = {}
226
+ self.P3._node[1] = {}
227
+ self.P3._node[2] = {}
228
+
229
+ def test_data_input(self):
230
+ G = self.Graph({1: [2], 2: [1]}, name="test")
231
+ assert G.name == "test"
232
+ assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
233
+ assert sorted(G.succ.items()) == [(1, {2: {}}), (2, {1: {}})]
234
+ assert sorted(G.pred.items()) == [(1, {2: {}}), (2, {1: {}})]
235
+
236
+ def test_add_edge(self):
237
+ G = self.Graph()
238
+ G.add_edge(0, 1)
239
+ assert G.adj == {0: {1: {}}, 1: {}}
240
+ assert G.succ == {0: {1: {}}, 1: {}}
241
+ assert G.pred == {0: {}, 1: {0: {}}}
242
+ G = self.Graph()
243
+ G.add_edge(*(0, 1))
244
+ assert G.adj == {0: {1: {}}, 1: {}}
245
+ assert G.succ == {0: {1: {}}, 1: {}}
246
+ assert G.pred == {0: {}, 1: {0: {}}}
247
+ with pytest.raises(ValueError, match="None cannot be a node"):
248
+ G.add_edge(None, 3)
249
+
250
+ def test_add_edges_from(self):
251
+ G = self.Graph()
252
+ G.add_edges_from([(0, 1), (0, 2, {"data": 3})], data=2)
253
+ assert G.adj == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
254
+ assert G.succ == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
255
+ assert G.pred == {0: {}, 1: {0: {"data": 2}}, 2: {0: {"data": 3}}}
256
+
257
+ with pytest.raises(nx.NetworkXError):
258
+ G.add_edges_from([(0,)]) # too few in tuple
259
+ with pytest.raises(nx.NetworkXError):
260
+ G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
261
+ with pytest.raises(TypeError):
262
+ G.add_edges_from([0]) # not a tuple
263
+ with pytest.raises(ValueError, match="None cannot be a node"):
264
+ G.add_edges_from([(None, 3), (3, 2)])
265
+
266
+ def test_remove_edge(self):
267
+ G = self.K3.copy()
268
+ G.remove_edge(0, 1)
269
+ assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
270
+ assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
271
+ with pytest.raises(nx.NetworkXError):
272
+ G.remove_edge(-1, 0)
273
+
274
+ def test_remove_edges_from(self):
275
+ G = self.K3.copy()
276
+ G.remove_edges_from([(0, 1)])
277
+ assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
278
+ assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
279
+ G.remove_edges_from([(0, 0)]) # silent fail
280
+
281
+ def test_clear(self):
282
+ G = self.K3
283
+ G.graph["name"] = "K3"
284
+ G.clear()
285
+ assert list(G.nodes) == []
286
+ assert G.succ == {}
287
+ assert G.pred == {}
288
+ assert G.graph == {}
289
+
290
+ def test_clear_edges(self):
291
+ G = self.K3
292
+ G.graph["name"] = "K3"
293
+ nodes = list(G.nodes)
294
+ G.clear_edges()
295
+ assert list(G.nodes) == nodes
296
+ expected = {0: {}, 1: {}, 2: {}}
297
+ assert G.succ == expected
298
+ assert G.pred == expected
299
+ assert list(G.edges) == []
300
+ assert G.graph["name"] == "K3"
301
+
302
+
303
+ class TestEdgeSubgraph(_TestGraphEdgeSubgraph):
304
+ """Unit tests for the :meth:`DiGraph.edge_subgraph` method."""
305
+
306
+ def setup_method(self):
307
+ # Create a doubly-linked path graph on five nodes.
308
+ G = nx.DiGraph(nx.path_graph(5))
309
+ # Add some node, edge, and graph attributes.
310
+ for i in range(5):
311
+ G.nodes[i]["name"] = f"node{i}"
312
+ G.edges[0, 1]["name"] = "edge01"
313
+ G.edges[3, 4]["name"] = "edge34"
314
+ G.graph["name"] = "graph"
315
+ # Get the subgraph induced by the first and last edges.
316
+ self.G = G
317
+ self.H = G.edge_subgraph([(0, 1), (3, 4)])
318
+
319
+ def test_pred_succ(self):
320
+ """Test that nodes are added to predecessors and successors.
321
+
322
+ For more information, see GitHub issue #2370.
323
+
324
+ """
325
+ G = nx.DiGraph()
326
+ G.add_edge(0, 1)
327
+ H = G.edge_subgraph([(0, 1)])
328
+ assert list(H.predecessors(0)) == []
329
+ assert list(H.successors(0)) == [1]
330
+ assert list(H.predecessors(1)) == [0]
331
+ assert list(H.successors(1)) == []
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_function.py ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+ import pytest
4
+
5
+ import networkx as nx
6
+ from networkx.utils import edges_equal, nodes_equal
7
+
8
+
9
+ class TestFunction:
10
+ def setup_method(self):
11
+ self.G = nx.Graph({0: [1, 2, 3], 1: [1, 2, 0], 4: []}, name="Test")
12
+ self.Gdegree = {0: 3, 1: 2, 2: 2, 3: 1, 4: 0}
13
+ self.Gnodes = list(range(5))
14
+ self.Gedges = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2)]
15
+ self.DG = nx.DiGraph({0: [1, 2, 3], 1: [1, 2, 0], 4: []})
16
+ self.DGin_degree = {0: 1, 1: 2, 2: 2, 3: 1, 4: 0}
17
+ self.DGout_degree = {0: 3, 1: 3, 2: 0, 3: 0, 4: 0}
18
+ self.DGnodes = list(range(5))
19
+ self.DGedges = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2)]
20
+
21
+ def test_nodes(self):
22
+ assert nodes_equal(self.G.nodes(), list(nx.nodes(self.G)))
23
+ assert nodes_equal(self.DG.nodes(), list(nx.nodes(self.DG)))
24
+
25
+ def test_edges(self):
26
+ assert edges_equal(self.G.edges(), list(nx.edges(self.G)))
27
+ assert sorted(self.DG.edges()) == sorted(nx.edges(self.DG))
28
+ assert edges_equal(
29
+ self.G.edges(nbunch=[0, 1, 3]), list(nx.edges(self.G, nbunch=[0, 1, 3]))
30
+ )
31
+ assert sorted(self.DG.edges(nbunch=[0, 1, 3])) == sorted(
32
+ nx.edges(self.DG, nbunch=[0, 1, 3])
33
+ )
34
+
35
+ def test_degree(self):
36
+ assert edges_equal(self.G.degree(), list(nx.degree(self.G)))
37
+ assert sorted(self.DG.degree()) == sorted(nx.degree(self.DG))
38
+ assert edges_equal(
39
+ self.G.degree(nbunch=[0, 1]), list(nx.degree(self.G, nbunch=[0, 1]))
40
+ )
41
+ assert sorted(self.DG.degree(nbunch=[0, 1])) == sorted(
42
+ nx.degree(self.DG, nbunch=[0, 1])
43
+ )
44
+ assert edges_equal(
45
+ self.G.degree(weight="weight"), list(nx.degree(self.G, weight="weight"))
46
+ )
47
+ assert sorted(self.DG.degree(weight="weight")) == sorted(
48
+ nx.degree(self.DG, weight="weight")
49
+ )
50
+
51
+ def test_neighbors(self):
52
+ assert list(self.G.neighbors(1)) == list(nx.neighbors(self.G, 1))
53
+ assert list(self.DG.neighbors(1)) == list(nx.neighbors(self.DG, 1))
54
+
55
+ def test_number_of_nodes(self):
56
+ assert self.G.number_of_nodes() == nx.number_of_nodes(self.G)
57
+ assert self.DG.number_of_nodes() == nx.number_of_nodes(self.DG)
58
+
59
+ def test_number_of_edges(self):
60
+ assert self.G.number_of_edges() == nx.number_of_edges(self.G)
61
+ assert self.DG.number_of_edges() == nx.number_of_edges(self.DG)
62
+
63
+ def test_is_directed(self):
64
+ assert self.G.is_directed() == nx.is_directed(self.G)
65
+ assert self.DG.is_directed() == nx.is_directed(self.DG)
66
+
67
+ def test_add_star(self):
68
+ G = self.G.copy()
69
+ nlist = [12, 13, 14, 15]
70
+ nx.add_star(G, nlist)
71
+ assert edges_equal(G.edges(nlist), [(12, 13), (12, 14), (12, 15)])
72
+
73
+ G = self.G.copy()
74
+ nx.add_star(G, nlist, weight=2.0)
75
+ assert edges_equal(
76
+ G.edges(nlist, data=True),
77
+ [
78
+ (12, 13, {"weight": 2.0}),
79
+ (12, 14, {"weight": 2.0}),
80
+ (12, 15, {"weight": 2.0}),
81
+ ],
82
+ )
83
+
84
+ G = self.G.copy()
85
+ nlist = [12]
86
+ nx.add_star(G, nlist)
87
+ assert nodes_equal(G, list(self.G) + nlist)
88
+
89
+ G = self.G.copy()
90
+ nlist = []
91
+ nx.add_star(G, nlist)
92
+ assert nodes_equal(G.nodes, self.Gnodes)
93
+ assert edges_equal(G.edges, self.G.edges)
94
+
95
+ def test_add_path(self):
96
+ G = self.G.copy()
97
+ nlist = [12, 13, 14, 15]
98
+ nx.add_path(G, nlist)
99
+ assert edges_equal(G.edges(nlist), [(12, 13), (13, 14), (14, 15)])
100
+ G = self.G.copy()
101
+ nx.add_path(G, nlist, weight=2.0)
102
+ assert edges_equal(
103
+ G.edges(nlist, data=True),
104
+ [
105
+ (12, 13, {"weight": 2.0}),
106
+ (13, 14, {"weight": 2.0}),
107
+ (14, 15, {"weight": 2.0}),
108
+ ],
109
+ )
110
+
111
+ G = self.G.copy()
112
+ nlist = ["node"]
113
+ nx.add_path(G, nlist)
114
+ assert edges_equal(G.edges(nlist), [])
115
+ assert nodes_equal(G, list(self.G) + ["node"])
116
+
117
+ G = self.G.copy()
118
+ nlist = iter(["node"])
119
+ nx.add_path(G, nlist)
120
+ assert edges_equal(G.edges(["node"]), [])
121
+ assert nodes_equal(G, list(self.G) + ["node"])
122
+
123
+ G = self.G.copy()
124
+ nlist = [12]
125
+ nx.add_path(G, nlist)
126
+ assert edges_equal(G.edges(nlist), [])
127
+ assert nodes_equal(G, list(self.G) + [12])
128
+
129
+ G = self.G.copy()
130
+ nlist = iter([12])
131
+ nx.add_path(G, nlist)
132
+ assert edges_equal(G.edges([12]), [])
133
+ assert nodes_equal(G, list(self.G) + [12])
134
+
135
+ G = self.G.copy()
136
+ nlist = []
137
+ nx.add_path(G, nlist)
138
+ assert edges_equal(G.edges, self.G.edges)
139
+ assert nodes_equal(G, list(self.G))
140
+
141
+ G = self.G.copy()
142
+ nlist = iter([])
143
+ nx.add_path(G, nlist)
144
+ assert edges_equal(G.edges, self.G.edges)
145
+ assert nodes_equal(G, list(self.G))
146
+
147
+ def test_add_cycle(self):
148
+ G = self.G.copy()
149
+ nlist = [12, 13, 14, 15]
150
+ oklists = [
151
+ [(12, 13), (12, 15), (13, 14), (14, 15)],
152
+ [(12, 13), (13, 14), (14, 15), (15, 12)],
153
+ ]
154
+ nx.add_cycle(G, nlist)
155
+ assert sorted(G.edges(nlist)) in oklists
156
+ G = self.G.copy()
157
+ oklists = [
158
+ [
159
+ (12, 13, {"weight": 1.0}),
160
+ (12, 15, {"weight": 1.0}),
161
+ (13, 14, {"weight": 1.0}),
162
+ (14, 15, {"weight": 1.0}),
163
+ ],
164
+ [
165
+ (12, 13, {"weight": 1.0}),
166
+ (13, 14, {"weight": 1.0}),
167
+ (14, 15, {"weight": 1.0}),
168
+ (15, 12, {"weight": 1.0}),
169
+ ],
170
+ ]
171
+ nx.add_cycle(G, nlist, weight=1.0)
172
+ assert sorted(G.edges(nlist, data=True)) in oklists
173
+
174
+ G = self.G.copy()
175
+ nlist = [12]
176
+ nx.add_cycle(G, nlist)
177
+ assert nodes_equal(G, list(self.G) + nlist)
178
+
179
+ G = self.G.copy()
180
+ nlist = []
181
+ nx.add_cycle(G, nlist)
182
+ assert nodes_equal(G.nodes, self.Gnodes)
183
+ assert edges_equal(G.edges, self.G.edges)
184
+
185
+ def test_subgraph(self):
186
+ assert (
187
+ self.G.subgraph([0, 1, 2, 4]).adj == nx.subgraph(self.G, [0, 1, 2, 4]).adj
188
+ )
189
+ assert (
190
+ self.DG.subgraph([0, 1, 2, 4]).adj == nx.subgraph(self.DG, [0, 1, 2, 4]).adj
191
+ )
192
+ assert (
193
+ self.G.subgraph([0, 1, 2, 4]).adj
194
+ == nx.induced_subgraph(self.G, [0, 1, 2, 4]).adj
195
+ )
196
+ assert (
197
+ self.DG.subgraph([0, 1, 2, 4]).adj
198
+ == nx.induced_subgraph(self.DG, [0, 1, 2, 4]).adj
199
+ )
200
+ # subgraph-subgraph chain is allowed in function interface
201
+ H = nx.induced_subgraph(self.G.subgraph([0, 1, 2, 4]), [0, 1, 4])
202
+ assert H._graph is not self.G
203
+ assert H.adj == self.G.subgraph([0, 1, 4]).adj
204
+
205
+ def test_edge_subgraph(self):
206
+ assert (
207
+ self.G.edge_subgraph([(1, 2), (0, 3)]).adj
208
+ == nx.edge_subgraph(self.G, [(1, 2), (0, 3)]).adj
209
+ )
210
+ assert (
211
+ self.DG.edge_subgraph([(1, 2), (0, 3)]).adj
212
+ == nx.edge_subgraph(self.DG, [(1, 2), (0, 3)]).adj
213
+ )
214
+
215
+ def test_create_empty_copy(self):
216
+ G = nx.create_empty_copy(self.G, with_data=False)
217
+ assert nodes_equal(G, list(self.G))
218
+ assert G.graph == {}
219
+ assert G._node == {}.fromkeys(self.G.nodes(), {})
220
+ assert G._adj == {}.fromkeys(self.G.nodes(), {})
221
+ G = nx.create_empty_copy(self.G)
222
+ assert nodes_equal(G, list(self.G))
223
+ assert G.graph == self.G.graph
224
+ assert G._node == self.G._node
225
+ assert G._adj == {}.fromkeys(self.G.nodes(), {})
226
+
227
+ def test_degree_histogram(self):
228
+ assert nx.degree_histogram(self.G) == [1, 1, 1, 1, 1]
229
+
230
+ def test_density(self):
231
+ assert nx.density(self.G) == 0.5
232
+ assert nx.density(self.DG) == 0.3
233
+ G = nx.Graph()
234
+ G.add_node(1)
235
+ assert nx.density(G) == 0.0
236
+
237
+ def test_density_selfloop(self):
238
+ G = nx.Graph()
239
+ G.add_edge(1, 1)
240
+ assert nx.density(G) == 0.0
241
+ G.add_edge(1, 2)
242
+ assert nx.density(G) == 2.0
243
+
244
+ def test_freeze(self):
245
+ G = nx.freeze(self.G)
246
+ assert G.frozen
247
+ pytest.raises(nx.NetworkXError, G.add_node, 1)
248
+ pytest.raises(nx.NetworkXError, G.add_nodes_from, [1])
249
+ pytest.raises(nx.NetworkXError, G.remove_node, 1)
250
+ pytest.raises(nx.NetworkXError, G.remove_nodes_from, [1])
251
+ pytest.raises(nx.NetworkXError, G.add_edge, 1, 2)
252
+ pytest.raises(nx.NetworkXError, G.add_edges_from, [(1, 2)])
253
+ pytest.raises(nx.NetworkXError, G.remove_edge, 1, 2)
254
+ pytest.raises(nx.NetworkXError, G.remove_edges_from, [(1, 2)])
255
+ pytest.raises(nx.NetworkXError, G.clear_edges)
256
+ pytest.raises(nx.NetworkXError, G.clear)
257
+
258
+ def test_is_frozen(self):
259
+ assert not nx.is_frozen(self.G)
260
+ G = nx.freeze(self.G)
261
+ assert G.frozen == nx.is_frozen(self.G)
262
+ assert G.frozen
263
+
264
+ def test_node_attributes_are_still_mutable_on_frozen_graph(self):
265
+ G = nx.freeze(nx.path_graph(3))
266
+ node = G.nodes[0]
267
+ node["node_attribute"] = True
268
+ assert node["node_attribute"] == True
269
+
270
+ def test_edge_attributes_are_still_mutable_on_frozen_graph(self):
271
+ G = nx.freeze(nx.path_graph(3))
272
+ edge = G.edges[(0, 1)]
273
+ edge["edge_attribute"] = True
274
+ assert edge["edge_attribute"] == True
275
+
276
+ def test_neighbors_complete_graph(self):
277
+ graph = nx.complete_graph(100)
278
+ pop = random.sample(list(graph), 1)
279
+ nbors = list(nx.neighbors(graph, pop[0]))
280
+ # should be all the other vertices in the graph
281
+ assert len(nbors) == len(graph) - 1
282
+
283
+ graph = nx.path_graph(100)
284
+ node = random.sample(list(graph), 1)[0]
285
+ nbors = list(nx.neighbors(graph, node))
286
+ # should be all the other vertices in the graph
287
+ if node != 0 and node != 99:
288
+ assert len(nbors) == 2
289
+ else:
290
+ assert len(nbors) == 1
291
+
292
+ # create a star graph with 99 outer nodes
293
+ graph = nx.star_graph(99)
294
+ nbors = list(nx.neighbors(graph, 0))
295
+ assert len(nbors) == 99
296
+
297
+ def test_non_neighbors(self):
298
+ graph = nx.complete_graph(100)
299
+ pop = random.sample(list(graph), 1)
300
+ nbors = list(nx.non_neighbors(graph, pop[0]))
301
+ # should be all the other vertices in the graph
302
+ assert len(nbors) == 0
303
+
304
+ graph = nx.path_graph(100)
305
+ node = random.sample(list(graph), 1)[0]
306
+ nbors = list(nx.non_neighbors(graph, node))
307
+ # should be all the other vertices in the graph
308
+ if node != 0 and node != 99:
309
+ assert len(nbors) == 97
310
+ else:
311
+ assert len(nbors) == 98
312
+
313
+ # create a star graph with 99 outer nodes
314
+ graph = nx.star_graph(99)
315
+ nbors = list(nx.non_neighbors(graph, 0))
316
+ assert len(nbors) == 0
317
+
318
+ # disconnected graph
319
+ graph = nx.Graph()
320
+ graph.add_nodes_from(range(10))
321
+ nbors = list(nx.non_neighbors(graph, 0))
322
+ assert len(nbors) == 9
323
+
324
+ def test_non_edges(self):
325
+ # All possible edges exist
326
+ graph = nx.complete_graph(5)
327
+ nedges = list(nx.non_edges(graph))
328
+ assert len(nedges) == 0
329
+
330
+ graph = nx.path_graph(4)
331
+ expected = [(0, 2), (0, 3), (1, 3)]
332
+ nedges = list(nx.non_edges(graph))
333
+ for u, v in expected:
334
+ assert (u, v) in nedges or (v, u) in nedges
335
+
336
+ graph = nx.star_graph(4)
337
+ expected = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
338
+ nedges = list(nx.non_edges(graph))
339
+ for u, v in expected:
340
+ assert (u, v) in nedges or (v, u) in nedges
341
+
342
+ # Directed graphs
343
+ graph = nx.DiGraph()
344
+ graph.add_edges_from([(0, 2), (2, 0), (2, 1)])
345
+ expected = [(0, 1), (1, 0), (1, 2)]
346
+ nedges = list(nx.non_edges(graph))
347
+ for e in expected:
348
+ assert e in nedges
349
+
350
+ def test_is_weighted(self):
351
+ G = nx.Graph()
352
+ assert not nx.is_weighted(G)
353
+
354
+ G = nx.path_graph(4)
355
+ assert not nx.is_weighted(G)
356
+ assert not nx.is_weighted(G, (2, 3))
357
+
358
+ G.add_node(4)
359
+ G.add_edge(3, 4, weight=4)
360
+ assert not nx.is_weighted(G)
361
+ assert nx.is_weighted(G, (3, 4))
362
+
363
+ G = nx.DiGraph()
364
+ G.add_weighted_edges_from(
365
+ [
366
+ ("0", "3", 3),
367
+ ("0", "1", -5),
368
+ ("1", "0", -5),
369
+ ("0", "2", 2),
370
+ ("1", "2", 4),
371
+ ("2", "3", 1),
372
+ ]
373
+ )
374
+ assert nx.is_weighted(G)
375
+ assert nx.is_weighted(G, ("1", "0"))
376
+
377
+ G = G.to_undirected()
378
+ assert nx.is_weighted(G)
379
+ assert nx.is_weighted(G, ("1", "0"))
380
+
381
+ pytest.raises(nx.NetworkXError, nx.is_weighted, G, (1, 2))
382
+
383
+ def test_is_negatively_weighted(self):
384
+ G = nx.Graph()
385
+ assert not nx.is_negatively_weighted(G)
386
+
387
+ G.add_node(1)
388
+ G.add_nodes_from([2, 3, 4, 5])
389
+ assert not nx.is_negatively_weighted(G)
390
+
391
+ G.add_edge(1, 2, weight=4)
392
+ assert not nx.is_negatively_weighted(G, (1, 2))
393
+
394
+ G.add_edges_from([(1, 3), (2, 4), (2, 6)])
395
+ G[1][3]["color"] = "blue"
396
+ assert not nx.is_negatively_weighted(G)
397
+ assert not nx.is_negatively_weighted(G, (1, 3))
398
+
399
+ G[2][4]["weight"] = -2
400
+ assert nx.is_negatively_weighted(G, (2, 4))
401
+ assert nx.is_negatively_weighted(G)
402
+
403
+ G = nx.DiGraph()
404
+ G.add_weighted_edges_from(
405
+ [
406
+ ("0", "3", 3),
407
+ ("0", "1", -5),
408
+ ("1", "0", -2),
409
+ ("0", "2", 2),
410
+ ("1", "2", -3),
411
+ ("2", "3", 1),
412
+ ]
413
+ )
414
+ assert nx.is_negatively_weighted(G)
415
+ assert not nx.is_negatively_weighted(G, ("0", "3"))
416
+ assert nx.is_negatively_weighted(G, ("1", "0"))
417
+
418
+ pytest.raises(nx.NetworkXError, nx.is_negatively_weighted, G, (1, 4))
419
+
420
+
421
+ class TestCommonNeighbors:
422
+ @classmethod
423
+ def setup_class(cls):
424
+ cls.func = staticmethod(nx.common_neighbors)
425
+
426
+ def test_func(G, u, v, expected):
427
+ result = sorted(cls.func(G, u, v))
428
+ assert result == expected
429
+
430
+ cls.test = staticmethod(test_func)
431
+
432
+ def test_K5(self):
433
+ G = nx.complete_graph(5)
434
+ self.test(G, 0, 1, [2, 3, 4])
435
+
436
+ def test_P3(self):
437
+ G = nx.path_graph(3)
438
+ self.test(G, 0, 2, [1])
439
+
440
+ def test_S4(self):
441
+ G = nx.star_graph(4)
442
+ self.test(G, 1, 2, [0])
443
+
444
+ def test_digraph(self):
445
+ with pytest.raises(nx.NetworkXNotImplemented):
446
+ G = nx.DiGraph()
447
+ G.add_edges_from([(0, 1), (1, 2)])
448
+ self.func(G, 0, 2)
449
+
450
+ def test_nonexistent_nodes(self):
451
+ G = nx.complete_graph(5)
452
+ pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 5, 4)
453
+ pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 4, 5)
454
+ pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 5, 6)
455
+
456
+ def test_custom1(self):
457
+ """Case of no common neighbors."""
458
+ G = nx.Graph()
459
+ G.add_nodes_from([0, 1])
460
+ self.test(G, 0, 1, [])
461
+
462
+ def test_custom2(self):
463
+ """Case of equal nodes."""
464
+ G = nx.complete_graph(4)
465
+ self.test(G, 0, 0, [1, 2, 3])
466
+
467
+
468
+ @pytest.mark.parametrize(
469
+ "graph_type", (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
470
+ )
471
+ def test_set_node_attributes(graph_type):
472
+ # Test single value
473
+ G = nx.path_graph(3, create_using=graph_type)
474
+ vals = 100
475
+ attr = "hello"
476
+ nx.set_node_attributes(G, vals, attr)
477
+ assert G.nodes[0][attr] == vals
478
+ assert G.nodes[1][attr] == vals
479
+ assert G.nodes[2][attr] == vals
480
+
481
+ # Test dictionary
482
+ G = nx.path_graph(3, create_using=graph_type)
483
+ vals = dict(zip(sorted(G.nodes()), range(len(G))))
484
+ attr = "hi"
485
+ nx.set_node_attributes(G, vals, attr)
486
+ assert G.nodes[0][attr] == 0
487
+ assert G.nodes[1][attr] == 1
488
+ assert G.nodes[2][attr] == 2
489
+
490
+ # Test dictionary of dictionaries
491
+ G = nx.path_graph(3, create_using=graph_type)
492
+ d = {"hi": 0, "hello": 200}
493
+ vals = dict.fromkeys(G.nodes(), d)
494
+ vals.pop(0)
495
+ nx.set_node_attributes(G, vals)
496
+ assert G.nodes[0] == {}
497
+ assert G.nodes[1]["hi"] == 0
498
+ assert G.nodes[2]["hello"] == 200
499
+
500
+
501
+ @pytest.mark.parametrize(
502
+ ("values", "name"),
503
+ (
504
+ ({0: "red", 1: "blue"}, "color"), # values dictionary
505
+ ({0: {"color": "red"}, 1: {"color": "blue"}}, None), # dict-of-dict
506
+ ),
507
+ )
508
+ def test_set_node_attributes_ignores_extra_nodes(values, name):
509
+ """
510
+ When `values` is a dict or dict-of-dict keyed by nodes, ensure that keys
511
+ that correspond to nodes not in G are ignored.
512
+ """
513
+ G = nx.Graph()
514
+ G.add_node(0)
515
+ nx.set_node_attributes(G, values, name)
516
+ assert G.nodes[0]["color"] == "red"
517
+ assert 1 not in G.nodes
518
+
519
+
520
+ @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph))
521
+ def test_set_edge_attributes(graph_type):
522
+ # Test single value
523
+ G = nx.path_graph(3, create_using=graph_type)
524
+ attr = "hello"
525
+ vals = 3
526
+ nx.set_edge_attributes(G, vals, attr)
527
+ assert G[0][1][attr] == vals
528
+ assert G[1][2][attr] == vals
529
+
530
+ # Test multiple values
531
+ G = nx.path_graph(3, create_using=graph_type)
532
+ attr = "hi"
533
+ edges = [(0, 1), (1, 2)]
534
+ vals = dict(zip(edges, range(len(edges))))
535
+ nx.set_edge_attributes(G, vals, attr)
536
+ assert G[0][1][attr] == 0
537
+ assert G[1][2][attr] == 1
538
+
539
+ # Test dictionary of dictionaries
540
+ G = nx.path_graph(3, create_using=graph_type)
541
+ d = {"hi": 0, "hello": 200}
542
+ edges = [(0, 1)]
543
+ vals = dict.fromkeys(edges, d)
544
+ nx.set_edge_attributes(G, vals)
545
+ assert G[0][1]["hi"] == 0
546
+ assert G[0][1]["hello"] == 200
547
+ assert G[1][2] == {}
548
+
549
+
550
+ @pytest.mark.parametrize(
551
+ ("values", "name"),
552
+ (
553
+ ({(0, 1): 1.0, (0, 2): 2.0}, "weight"), # values dict
554
+ ({(0, 1): {"weight": 1.0}, (0, 2): {"weight": 2.0}}, None), # values dod
555
+ ),
556
+ )
557
+ def test_set_edge_attributes_ignores_extra_edges(values, name):
558
+ """If `values` is a dict or dict-of-dicts containing edges that are not in
559
+ G, data associate with these edges should be ignored.
560
+ """
561
+ G = nx.Graph([(0, 1)])
562
+ nx.set_edge_attributes(G, values, name)
563
+ assert G[0][1]["weight"] == 1.0
564
+ assert (0, 2) not in G.edges
565
+
566
+
567
+ @pytest.mark.parametrize("graph_type", (nx.MultiGraph, nx.MultiDiGraph))
568
+ def test_set_edge_attributes_multi(graph_type):
569
+ # Test single value
570
+ G = nx.path_graph(3, create_using=graph_type)
571
+ attr = "hello"
572
+ vals = 3
573
+ nx.set_edge_attributes(G, vals, attr)
574
+ assert G[0][1][0][attr] == vals
575
+ assert G[1][2][0][attr] == vals
576
+
577
+ # Test multiple values
578
+ G = nx.path_graph(3, create_using=graph_type)
579
+ attr = "hi"
580
+ edges = [(0, 1, 0), (1, 2, 0)]
581
+ vals = dict(zip(edges, range(len(edges))))
582
+ nx.set_edge_attributes(G, vals, attr)
583
+ assert G[0][1][0][attr] == 0
584
+ assert G[1][2][0][attr] == 1
585
+
586
+ # Test dictionary of dictionaries
587
+ G = nx.path_graph(3, create_using=graph_type)
588
+ d = {"hi": 0, "hello": 200}
589
+ edges = [(0, 1, 0)]
590
+ vals = dict.fromkeys(edges, d)
591
+ nx.set_edge_attributes(G, vals)
592
+ assert G[0][1][0]["hi"] == 0
593
+ assert G[0][1][0]["hello"] == 200
594
+ assert G[1][2][0] == {}
595
+
596
+
597
+ @pytest.mark.parametrize(
598
+ ("values", "name"),
599
+ (
600
+ ({(0, 1, 0): 1.0, (0, 2, 0): 2.0}, "weight"), # values dict
601
+ ({(0, 1, 0): {"weight": 1.0}, (0, 2, 0): {"weight": 2.0}}, None), # values dod
602
+ ),
603
+ )
604
+ def test_set_edge_attributes_multi_ignores_extra_edges(values, name):
605
+ """If `values` is a dict or dict-of-dicts containing edges that are not in
606
+ G, data associate with these edges should be ignored.
607
+ """
608
+ G = nx.MultiGraph([(0, 1, 0), (0, 1, 1)])
609
+ nx.set_edge_attributes(G, values, name)
610
+ assert G[0][1][0]["weight"] == 1.0
611
+ assert G[0][1][1] == {}
612
+ assert (0, 2) not in G.edges()
613
+
614
+
615
+ def test_get_node_attributes():
616
+ graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
617
+ for G in graphs:
618
+ G = nx.path_graph(3, create_using=G)
619
+ attr = "hello"
620
+ vals = 100
621
+ nx.set_node_attributes(G, vals, attr)
622
+ attrs = nx.get_node_attributes(G, attr)
623
+ assert attrs[0] == vals
624
+ assert attrs[1] == vals
625
+ assert attrs[2] == vals
626
+ default_val = 1
627
+ G.add_node(4)
628
+ attrs = nx.get_node_attributes(G, attr, default=default_val)
629
+ assert attrs[4] == default_val
630
+
631
+
632
+ def test_get_edge_attributes():
633
+ graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
634
+ for G in graphs:
635
+ G = nx.path_graph(3, create_using=G)
636
+ attr = "hello"
637
+ vals = 100
638
+ nx.set_edge_attributes(G, vals, attr)
639
+ attrs = nx.get_edge_attributes(G, attr)
640
+ assert len(attrs) == 2
641
+
642
+ for edge in G.edges:
643
+ assert attrs[edge] == vals
644
+
645
+ default_val = vals
646
+ G.add_edge(4, 5)
647
+ deafult_attrs = nx.get_edge_attributes(G, attr, default=default_val)
648
+ assert len(deafult_attrs) == 3
649
+
650
+ for edge in G.edges:
651
+ assert deafult_attrs[edge] == vals
652
+
653
+
654
+ def test_is_empty():
655
+ graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
656
+ for G in graphs:
657
+ assert nx.is_empty(G)
658
+ G.add_nodes_from(range(5))
659
+ assert nx.is_empty(G)
660
+ G.add_edges_from([(1, 2), (3, 4)])
661
+ assert not nx.is_empty(G)
662
+
663
+
664
+ @pytest.mark.parametrize(
665
+ "graph_type", [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
666
+ )
667
+ def test_selfloops(graph_type):
668
+ G = nx.complete_graph(3, create_using=graph_type)
669
+ G.add_edge(0, 0)
670
+ assert nodes_equal(nx.nodes_with_selfloops(G), [0])
671
+ assert edges_equal(nx.selfloop_edges(G), [(0, 0)])
672
+ assert edges_equal(nx.selfloop_edges(G, data=True), [(0, 0, {})])
673
+ assert nx.number_of_selfloops(G) == 1
674
+
675
+
676
+ @pytest.mark.parametrize(
677
+ "graph_type", [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
678
+ )
679
+ def test_selfloop_edges_attr(graph_type):
680
+ G = nx.complete_graph(3, create_using=graph_type)
681
+ G.add_edge(0, 0)
682
+ G.add_edge(1, 1, weight=2)
683
+ assert edges_equal(
684
+ nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
685
+ )
686
+ assert edges_equal(nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)])
687
+
688
+
689
+ def test_selfloop_edges_multi_with_data_and_keys():
690
+ G = nx.complete_graph(3, create_using=nx.MultiGraph)
691
+ G.add_edge(0, 0, weight=10)
692
+ G.add_edge(0, 0, weight=100)
693
+ assert edges_equal(
694
+ nx.selfloop_edges(G, data="weight", keys=True), [(0, 0, 0, 10), (0, 0, 1, 100)]
695
+ )
696
+
697
+
698
+ @pytest.mark.parametrize("graph_type", [nx.Graph, nx.DiGraph])
699
+ def test_selfloops_removal(graph_type):
700
+ G = nx.complete_graph(3, create_using=graph_type)
701
+ G.add_edge(0, 0)
702
+ G.remove_edges_from(nx.selfloop_edges(G, keys=True))
703
+ G.add_edge(0, 0)
704
+ G.remove_edges_from(nx.selfloop_edges(G, data=True))
705
+ G.add_edge(0, 0)
706
+ G.remove_edges_from(nx.selfloop_edges(G, keys=True, data=True))
707
+
708
+
709
+ @pytest.mark.parametrize("graph_type", [nx.MultiGraph, nx.MultiDiGraph])
710
+ def test_selfloops_removal_multi(graph_type):
711
+ """test removing selfloops behavior vis-a-vis altering a dict while iterating.
712
+ cf. gh-4068"""
713
+ G = nx.complete_graph(3, create_using=graph_type)
714
+ # Defaults - see gh-4080
715
+ G.add_edge(0, 0)
716
+ G.add_edge(0, 0)
717
+ G.remove_edges_from(nx.selfloop_edges(G))
718
+ assert (0, 0) not in G.edges()
719
+ # With keys
720
+ G.add_edge(0, 0)
721
+ G.add_edge(0, 0)
722
+ with pytest.raises(RuntimeError):
723
+ G.remove_edges_from(nx.selfloop_edges(G, keys=True))
724
+ # With data
725
+ G.add_edge(0, 0)
726
+ G.add_edge(0, 0)
727
+ with pytest.raises(TypeError):
728
+ G.remove_edges_from(nx.selfloop_edges(G, data=True))
729
+ # With keys and data
730
+ G.add_edge(0, 0)
731
+ G.add_edge(0, 0)
732
+ with pytest.raises(RuntimeError):
733
+ G.remove_edges_from(nx.selfloop_edges(G, data=True, keys=True))
734
+
735
+
736
+ def test_pathweight():
737
+ valid_path = [1, 2, 3]
738
+ invalid_path = [1, 3, 2]
739
+ graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
740
+ edges = [
741
+ (1, 2, {"cost": 5, "dist": 6}),
742
+ (2, 3, {"cost": 3, "dist": 4}),
743
+ (1, 2, {"cost": 1, "dist": 2}),
744
+ ]
745
+ for graph in graphs:
746
+ graph.add_edges_from(edges)
747
+ assert nx.path_weight(graph, valid_path, "cost") == 4
748
+ assert nx.path_weight(graph, valid_path, "dist") == 6
749
+ pytest.raises(nx.NetworkXNoPath, nx.path_weight, graph, invalid_path, "cost")
750
+
751
+
752
+ @pytest.mark.parametrize(
753
+ "G", (nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph())
754
+ )
755
+ def test_ispath(G):
756
+ G.add_edges_from([(1, 2), (2, 3), (1, 2), (3, 4)])
757
+ valid_path = [1, 2, 3, 4]
758
+ invalid_path = [1, 2, 4, 3] # wrong node order
759
+ another_invalid_path = [1, 2, 3, 4, 5] # contains node not in G
760
+ assert nx.is_path(G, valid_path)
761
+ assert not nx.is_path(G, invalid_path)
762
+ assert not nx.is_path(G, another_invalid_path)
763
+
764
+
765
+ @pytest.mark.parametrize("G", (nx.Graph(), nx.DiGraph()))
766
+ def test_restricted_view(G):
767
+ G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2)])
768
+ G.add_node(4)
769
+ H = nx.restricted_view(G, [0, 2, 5], [(1, 2), (3, 4)])
770
+ assert set(H.nodes()) == {1, 3, 4}
771
+ assert set(H.edges()) == {(1, 1)}
772
+
773
+
774
+ @pytest.mark.parametrize("G", (nx.MultiGraph(), nx.MultiDiGraph()))
775
+ def test_restricted_view_multi(G):
776
+ G.add_edges_from(
777
+ [(0, 1, 0), (0, 2, 0), (0, 3, 0), (0, 1, 1), (1, 0, 0), (1, 1, 0), (1, 2, 0)]
778
+ )
779
+ G.add_node(4)
780
+ H = nx.restricted_view(G, [0, 2, 5], [(1, 2, 0), (3, 4, 0)])
781
+ assert set(H.nodes()) == {1, 3, 4}
782
+ assert set(H.edges()) == {(1, 1)}
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/classes/tests/test_graph.py ADDED
@@ -0,0 +1,920 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gc
2
+ import pickle
3
+ import platform
4
+ import weakref
5
+
6
+ import pytest
7
+
8
+ import networkx as nx
9
+ from networkx.utils import edges_equal, graphs_equal, nodes_equal
10
+
11
+
12
+ class BaseGraphTester:
13
+ """Tests for data-structure independent graph class features."""
14
+
15
+ def test_contains(self):
16
+ G = self.K3
17
+ assert 1 in G
18
+ assert 4 not in G
19
+ assert "b" not in G
20
+ assert [] not in G # no exception for nonhashable
21
+ assert {1: 1} not in G # no exception for nonhashable
22
+
23
+ def test_order(self):
24
+ G = self.K3
25
+ assert len(G) == 3
26
+ assert G.order() == 3
27
+ assert G.number_of_nodes() == 3
28
+
29
+ def test_nodes(self):
30
+ G = self.K3
31
+ assert isinstance(G._node, G.node_dict_factory)
32
+ assert isinstance(G._adj, G.adjlist_outer_dict_factory)
33
+ assert all(
34
+ isinstance(adj, G.adjlist_inner_dict_factory) for adj in G._adj.values()
35
+ )
36
+ assert sorted(G.nodes()) == self.k3nodes
37
+ assert sorted(G.nodes(data=True)) == [(0, {}), (1, {}), (2, {})]
38
+
39
+ def test_none_node(self):
40
+ G = self.Graph()
41
+ with pytest.raises(ValueError):
42
+ G.add_node(None)
43
+ with pytest.raises(ValueError):
44
+ G.add_nodes_from([None])
45
+ with pytest.raises(ValueError):
46
+ G.add_edge(0, None)
47
+ with pytest.raises(ValueError):
48
+ G.add_edges_from([(0, None)])
49
+
50
+ def test_has_node(self):
51
+ G = self.K3
52
+ assert G.has_node(1)
53
+ assert not G.has_node(4)
54
+ assert not G.has_node([]) # no exception for nonhashable
55
+ assert not G.has_node({1: 1}) # no exception for nonhashable
56
+
57
+ def test_has_edge(self):
58
+ G = self.K3
59
+ assert G.has_edge(0, 1)
60
+ assert not G.has_edge(0, -1)
61
+
62
+ def test_neighbors(self):
63
+ G = self.K3
64
+ assert sorted(G.neighbors(0)) == [1, 2]
65
+ with pytest.raises(nx.NetworkXError):
66
+ G.neighbors(-1)
67
+
68
+ @pytest.mark.skipif(
69
+ platform.python_implementation() == "PyPy", reason="PyPy gc is different"
70
+ )
71
+ def test_memory_leak(self):
72
+ G = self.Graph()
73
+
74
+ def count_objects_of_type(_type):
75
+ # Iterating over all objects tracked by gc can include weak references
76
+ # whose weakly-referenced objects may no longer exist. Calling `isinstance`
77
+ # on such a weak reference will raise ReferenceError. There are at least
78
+ # three workarounds for this: one is to compare type names instead of using
79
+ # `isinstance` such as `type(obj).__name__ == typename`, another is to use
80
+ # `type(obj) == _type`, and the last is to ignore ProxyTypes as we do below.
81
+ # NOTE: even if this safeguard is deemed unnecessary to pass NetworkX tests,
82
+ # we should still keep it for maximum safety for other NetworkX backends.
83
+ return sum(
84
+ 1
85
+ for obj in gc.get_objects()
86
+ if not isinstance(obj, weakref.ProxyTypes) and isinstance(obj, _type)
87
+ )
88
+
89
+ gc.collect()
90
+ before = count_objects_of_type(self.Graph)
91
+ G.copy()
92
+ gc.collect()
93
+ after = count_objects_of_type(self.Graph)
94
+ assert before == after
95
+
96
+ # test a subgraph of the base class
97
+ class MyGraph(self.Graph):
98
+ pass
99
+
100
+ gc.collect()
101
+ G = MyGraph()
102
+ before = count_objects_of_type(MyGraph)
103
+ G.copy()
104
+ gc.collect()
105
+ after = count_objects_of_type(MyGraph)
106
+ assert before == after
107
+
108
+ def test_edges(self):
109
+ G = self.K3
110
+ assert isinstance(G._adj, G.adjlist_outer_dict_factory)
111
+ assert edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
112
+ assert edges_equal(G.edges(0), [(0, 1), (0, 2)])
113
+ assert edges_equal(G.edges([0, 1]), [(0, 1), (0, 2), (1, 2)])
114
+ with pytest.raises(nx.NetworkXError):
115
+ G.edges(-1)
116
+
117
+ def test_degree(self):
118
+ G = self.K3
119
+ assert sorted(G.degree()) == [(0, 2), (1, 2), (2, 2)]
120
+ assert dict(G.degree()) == {0: 2, 1: 2, 2: 2}
121
+ assert G.degree(0) == 2
122
+ with pytest.raises(nx.NetworkXError):
123
+ G.degree(-1) # node not in graph
124
+
125
+ def test_size(self):
126
+ G = self.K3
127
+ assert G.size() == 3
128
+ assert G.number_of_edges() == 3
129
+
130
+ def test_nbunch_iter(self):
131
+ G = self.K3
132
+ assert nodes_equal(G.nbunch_iter(), self.k3nodes) # all nodes
133
+ assert nodes_equal(G.nbunch_iter(0), [0]) # single node
134
+ assert nodes_equal(G.nbunch_iter([0, 1]), [0, 1]) # sequence
135
+ # sequence with none in graph
136
+ assert nodes_equal(G.nbunch_iter([-1]), [])
137
+ # string sequence with none in graph
138
+ assert nodes_equal(G.nbunch_iter("foo"), [])
139
+ # node not in graph doesn't get caught upon creation of iterator
140
+ bunch = G.nbunch_iter(-1)
141
+ # but gets caught when iterator used
142
+ with pytest.raises(nx.NetworkXError, match="is not a node or a sequence"):
143
+ list(bunch)
144
+ # unhashable doesn't get caught upon creation of iterator
145
+ bunch = G.nbunch_iter([0, 1, 2, {}])
146
+ # but gets caught when iterator hits the unhashable
147
+ with pytest.raises(
148
+ nx.NetworkXError, match="in sequence nbunch is not a valid node"
149
+ ):
150
+ list(bunch)
151
+
152
+ def test_nbunch_iter_node_format_raise(self):
153
+ # Tests that a node that would have failed string formatting
154
+ # doesn't cause an error when attempting to raise a
155
+ # :exc:`nx.NetworkXError`.
156
+
157
+ # For more information, see pull request #1813.
158
+ G = self.Graph()
159
+ nbunch = [("x", set())]
160
+ with pytest.raises(nx.NetworkXError):
161
+ list(G.nbunch_iter(nbunch))
162
+
163
+ def test_selfloop_degree(self):
164
+ G = self.Graph()
165
+ G.add_edge(1, 1)
166
+ assert sorted(G.degree()) == [(1, 2)]
167
+ assert dict(G.degree()) == {1: 2}
168
+ assert G.degree(1) == 2
169
+ assert sorted(G.degree([1])) == [(1, 2)]
170
+ assert G.degree(1, weight="weight") == 2
171
+
172
+ def test_selfloops(self):
173
+ G = self.K3.copy()
174
+ G.add_edge(0, 0)
175
+ assert nodes_equal(nx.nodes_with_selfloops(G), [0])
176
+ assert edges_equal(nx.selfloop_edges(G), [(0, 0)])
177
+ assert nx.number_of_selfloops(G) == 1
178
+ G.remove_edge(0, 0)
179
+ G.add_edge(0, 0)
180
+ G.remove_edges_from([(0, 0)])
181
+ G.add_edge(1, 1)
182
+ G.remove_node(1)
183
+ G.add_edge(0, 0)
184
+ G.add_edge(1, 1)
185
+ G.remove_nodes_from([0, 1])
186
+
187
+ def test_cache_reset(self):
188
+ G = self.K3.copy()
189
+ old_adj = G.adj
190
+ assert id(G.adj) == id(old_adj)
191
+ G._adj = {}
192
+ assert id(G.adj) != id(old_adj)
193
+
194
+ old_nodes = G.nodes
195
+ assert id(G.nodes) == id(old_nodes)
196
+ G._node = {}
197
+ assert id(G.nodes) != id(old_nodes)
198
+
199
+ def test_attributes_cached(self):
200
+ G = self.K3.copy()
201
+ assert id(G.nodes) == id(G.nodes)
202
+ assert id(G.edges) == id(G.edges)
203
+ assert id(G.degree) == id(G.degree)
204
+ assert id(G.adj) == id(G.adj)
205
+
206
+
207
+ class BaseAttrGraphTester(BaseGraphTester):
208
+ """Tests of graph class attribute features."""
209
+
210
+ def test_weighted_degree(self):
211
+ G = self.Graph()
212
+ G.add_edge(1, 2, weight=2, other=3)
213
+ G.add_edge(2, 3, weight=3, other=4)
214
+ assert sorted(d for n, d in G.degree(weight="weight")) == [2, 3, 5]
215
+ assert dict(G.degree(weight="weight")) == {1: 2, 2: 5, 3: 3}
216
+ assert G.degree(1, weight="weight") == 2
217
+ assert nodes_equal((G.degree([1], weight="weight")), [(1, 2)])
218
+
219
+ assert nodes_equal((d for n, d in G.degree(weight="other")), [3, 7, 4])
220
+ assert dict(G.degree(weight="other")) == {1: 3, 2: 7, 3: 4}
221
+ assert G.degree(1, weight="other") == 3
222
+ assert edges_equal((G.degree([1], weight="other")), [(1, 3)])
223
+
224
+ def add_attributes(self, G):
225
+ G.graph["foo"] = []
226
+ G.nodes[0]["foo"] = []
227
+ G.remove_edge(1, 2)
228
+ ll = []
229
+ G.add_edge(1, 2, foo=ll)
230
+ G.add_edge(2, 1, foo=ll)
231
+
232
+ def test_name(self):
233
+ G = self.Graph(name="")
234
+ assert G.name == ""
235
+ G = self.Graph(name="test")
236
+ assert G.name == "test"
237
+
238
+ def test_str_unnamed(self):
239
+ G = self.Graph()
240
+ G.add_edges_from([(1, 2), (2, 3)])
241
+ assert str(G) == f"{type(G).__name__} with 3 nodes and 2 edges"
242
+
243
+ def test_str_named(self):
244
+ G = self.Graph(name="foo")
245
+ G.add_edges_from([(1, 2), (2, 3)])
246
+ assert str(G) == f"{type(G).__name__} named 'foo' with 3 nodes and 2 edges"
247
+
248
+ def test_graph_chain(self):
249
+ G = self.Graph([(0, 1), (1, 2)])
250
+ DG = G.to_directed(as_view=True)
251
+ SDG = DG.subgraph([0, 1])
252
+ RSDG = SDG.reverse(copy=False)
253
+ assert G is DG._graph
254
+ assert DG is SDG._graph
255
+ assert SDG is RSDG._graph
256
+
257
+ def test_copy(self):
258
+ G = self.Graph()
259
+ G.add_node(0)
260
+ G.add_edge(1, 2)
261
+ self.add_attributes(G)
262
+ # copy edge datadict but any container attr are same
263
+ H = G.copy()
264
+ self.graphs_equal(H, G)
265
+ self.different_attrdict(H, G)
266
+ self.shallow_copy_attrdict(H, G)
267
+
268
+ def test_class_copy(self):
269
+ G = self.Graph()
270
+ G.add_node(0)
271
+ G.add_edge(1, 2)
272
+ self.add_attributes(G)
273
+ # copy edge datadict but any container attr are same
274
+ H = G.__class__(G)
275
+ self.graphs_equal(H, G)
276
+ self.different_attrdict(H, G)
277
+ self.shallow_copy_attrdict(H, G)
278
+
279
+ def test_fresh_copy(self):
280
+ G = self.Graph()
281
+ G.add_node(0)
282
+ G.add_edge(1, 2)
283
+ self.add_attributes(G)
284
+ # copy graph structure but use fresh datadict
285
+ H = G.__class__()
286
+ H.add_nodes_from(G)
287
+ H.add_edges_from(G.edges())
288
+ assert len(G.nodes[0]) == 1
289
+ ddict = G.adj[1][2][0] if G.is_multigraph() else G.adj[1][2]
290
+ assert len(ddict) == 1
291
+ assert len(H.nodes[0]) == 0
292
+ ddict = H.adj[1][2][0] if H.is_multigraph() else H.adj[1][2]
293
+ assert len(ddict) == 0
294
+
295
+ def is_deepcopy(self, H, G):
296
+ self.graphs_equal(H, G)
297
+ self.different_attrdict(H, G)
298
+ self.deep_copy_attrdict(H, G)
299
+
300
+ def deep_copy_attrdict(self, H, G):
301
+ self.deepcopy_graph_attr(H, G)
302
+ self.deepcopy_node_attr(H, G)
303
+ self.deepcopy_edge_attr(H, G)
304
+
305
+ def deepcopy_graph_attr(self, H, G):
306
+ assert G.graph["foo"] == H.graph["foo"]
307
+ G.graph["foo"].append(1)
308
+ assert G.graph["foo"] != H.graph["foo"]
309
+
310
+ def deepcopy_node_attr(self, H, G):
311
+ assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
312
+ G.nodes[0]["foo"].append(1)
313
+ assert G.nodes[0]["foo"] != H.nodes[0]["foo"]
314
+
315
+ def deepcopy_edge_attr(self, H, G):
316
+ assert G[1][2]["foo"] == H[1][2]["foo"]
317
+ G[1][2]["foo"].append(1)
318
+ assert G[1][2]["foo"] != H[1][2]["foo"]
319
+
320
+ def is_shallow_copy(self, H, G):
321
+ self.graphs_equal(H, G)
322
+ self.shallow_copy_attrdict(H, G)
323
+
324
+ def shallow_copy_attrdict(self, H, G):
325
+ self.shallow_copy_graph_attr(H, G)
326
+ self.shallow_copy_node_attr(H, G)
327
+ self.shallow_copy_edge_attr(H, G)
328
+
329
+ def shallow_copy_graph_attr(self, H, G):
330
+ assert G.graph["foo"] == H.graph["foo"]
331
+ G.graph["foo"].append(1)
332
+ assert G.graph["foo"] == H.graph["foo"]
333
+
334
+ def shallow_copy_node_attr(self, H, G):
335
+ assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
336
+ G.nodes[0]["foo"].append(1)
337
+ assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
338
+
339
+ def shallow_copy_edge_attr(self, H, G):
340
+ assert G[1][2]["foo"] == H[1][2]["foo"]
341
+ G[1][2]["foo"].append(1)
342
+ assert G[1][2]["foo"] == H[1][2]["foo"]
343
+
344
+ def same_attrdict(self, H, G):
345
+ old_foo = H[1][2]["foo"]
346
+ H.adj[1][2]["foo"] = "baz"
347
+ assert G.edges == H.edges
348
+ H.adj[1][2]["foo"] = old_foo
349
+ assert G.edges == H.edges
350
+
351
+ old_foo = H.nodes[0]["foo"]
352
+ H.nodes[0]["foo"] = "baz"
353
+ assert G.nodes == H.nodes
354
+ H.nodes[0]["foo"] = old_foo
355
+ assert G.nodes == H.nodes
356
+
357
+ def different_attrdict(self, H, G):
358
+ old_foo = H[1][2]["foo"]
359
+ H.adj[1][2]["foo"] = "baz"
360
+ assert G._adj != H._adj
361
+ H.adj[1][2]["foo"] = old_foo
362
+ assert G._adj == H._adj
363
+
364
+ old_foo = H.nodes[0]["foo"]
365
+ H.nodes[0]["foo"] = "baz"
366
+ assert G._node != H._node
367
+ H.nodes[0]["foo"] = old_foo
368
+ assert G._node == H._node
369
+
370
+ def graphs_equal(self, H, G):
371
+ assert G._adj == H._adj
372
+ assert G._node == H._node
373
+ assert G.graph == H.graph
374
+ assert G.name == H.name
375
+ if not G.is_directed() and not H.is_directed():
376
+ assert H._adj[1][2] is H._adj[2][1]
377
+ assert G._adj[1][2] is G._adj[2][1]
378
+ else: # at least one is directed
379
+ if not G.is_directed():
380
+ G._pred = G._adj
381
+ G._succ = G._adj
382
+ if not H.is_directed():
383
+ H._pred = H._adj
384
+ H._succ = H._adj
385
+ assert G._pred == H._pred
386
+ assert G._succ == H._succ
387
+ assert H._succ[1][2] is H._pred[2][1]
388
+ assert G._succ[1][2] is G._pred[2][1]
389
+
390
+ def test_graph_attr(self):
391
+ G = self.K3.copy()
392
+ G.graph["foo"] = "bar"
393
+ assert isinstance(G.graph, G.graph_attr_dict_factory)
394
+ assert G.graph["foo"] == "bar"
395
+ del G.graph["foo"]
396
+ assert G.graph == {}
397
+ H = self.Graph(foo="bar")
398
+ assert H.graph["foo"] == "bar"
399
+
400
+ def test_node_attr(self):
401
+ G = self.K3.copy()
402
+ G.add_node(1, foo="bar")
403
+ assert all(
404
+ isinstance(d, G.node_attr_dict_factory) for u, d in G.nodes(data=True)
405
+ )
406
+ assert nodes_equal(G.nodes(), [0, 1, 2])
407
+ assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "bar"}), (2, {})])
408
+ G.nodes[1]["foo"] = "baz"
409
+ assert nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "baz"}), (2, {})])
410
+ assert nodes_equal(G.nodes(data="foo"), [(0, None), (1, "baz"), (2, None)])
411
+ assert nodes_equal(
412
+ G.nodes(data="foo", default="bar"), [(0, "bar"), (1, "baz"), (2, "bar")]
413
+ )
414
+
415
+ def test_node_attr2(self):
416
+ G = self.K3.copy()
417
+ a = {"foo": "bar"}
418
+ G.add_node(3, **a)
419
+ assert nodes_equal(G.nodes(), [0, 1, 2, 3])
420
+ assert nodes_equal(
421
+ G.nodes(data=True), [(0, {}), (1, {}), (2, {}), (3, {"foo": "bar"})]
422
+ )
423
+
424
+ def test_edge_lookup(self):
425
+ G = self.Graph()
426
+ G.add_edge(1, 2, foo="bar")
427
+ assert edges_equal(G.edges[1, 2], {"foo": "bar"})
428
+
429
+ def test_edge_attr(self):
430
+ G = self.Graph()
431
+ G.add_edge(1, 2, foo="bar")
432
+ assert all(
433
+ isinstance(d, G.edge_attr_dict_factory) for u, v, d in G.edges(data=True)
434
+ )
435
+ assert edges_equal(G.edges(data=True), [(1, 2, {"foo": "bar"})])
436
+ assert edges_equal(G.edges(data="foo"), [(1, 2, "bar")])
437
+
438
+ def test_edge_attr2(self):
439
+ G = self.Graph()
440
+ G.add_edges_from([(1, 2), (3, 4)], foo="foo")
441
+ assert edges_equal(
442
+ G.edges(data=True), [(1, 2, {"foo": "foo"}), (3, 4, {"foo": "foo"})]
443
+ )
444
+ assert edges_equal(G.edges(data="foo"), [(1, 2, "foo"), (3, 4, "foo")])
445
+
446
+ def test_edge_attr3(self):
447
+ G = self.Graph()
448
+ G.add_edges_from([(1, 2, {"weight": 32}), (3, 4, {"weight": 64})], foo="foo")
449
+ assert edges_equal(
450
+ G.edges(data=True),
451
+ [
452
+ (1, 2, {"foo": "foo", "weight": 32}),
453
+ (3, 4, {"foo": "foo", "weight": 64}),
454
+ ],
455
+ )
456
+
457
+ G.remove_edges_from([(1, 2), (3, 4)])
458
+ G.add_edge(1, 2, data=7, spam="bar", bar="foo")
459
+ assert edges_equal(
460
+ G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
461
+ )
462
+
463
+ def test_edge_attr4(self):
464
+ G = self.Graph()
465
+ G.add_edge(1, 2, data=7, spam="bar", bar="foo")
466
+ assert edges_equal(
467
+ G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
468
+ )
469
+ G[1][2]["data"] = 10 # OK to set data like this
470
+ assert edges_equal(
471
+ G.edges(data=True), [(1, 2, {"data": 10, "spam": "bar", "bar": "foo"})]
472
+ )
473
+
474
+ G.adj[1][2]["data"] = 20
475
+ assert edges_equal(
476
+ G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})]
477
+ )
478
+ G.edges[1, 2]["data"] = 21 # another spelling, "edge"
479
+ assert edges_equal(
480
+ G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})]
481
+ )
482
+ G.adj[1][2]["listdata"] = [20, 200]
483
+ G.adj[1][2]["weight"] = 20
484
+ dd = {
485
+ "data": 21,
486
+ "spam": "bar",
487
+ "bar": "foo",
488
+ "listdata": [20, 200],
489
+ "weight": 20,
490
+ }
491
+ assert edges_equal(G.edges(data=True), [(1, 2, dd)])
492
+
493
+ def test_to_undirected(self):
494
+ G = self.K3
495
+ self.add_attributes(G)
496
+ H = nx.Graph(G)
497
+ self.is_shallow_copy(H, G)
498
+ self.different_attrdict(H, G)
499
+ H = G.to_undirected()
500
+ self.is_deepcopy(H, G)
501
+
502
+ def test_to_directed_as_view(self):
503
+ H = nx.path_graph(2, create_using=self.Graph)
504
+ H2 = H.to_directed(as_view=True)
505
+ assert H is H2._graph
506
+ assert H2.has_edge(0, 1)
507
+ assert H2.has_edge(1, 0) or H.is_directed()
508
+ pytest.raises(nx.NetworkXError, H2.add_node, -1)
509
+ pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
510
+ H.add_edge(1, 2)
511
+ assert H2.has_edge(1, 2)
512
+ assert H2.has_edge(2, 1) or H.is_directed()
513
+
514
+ def test_to_undirected_as_view(self):
515
+ H = nx.path_graph(2, create_using=self.Graph)
516
+ H2 = H.to_undirected(as_view=True)
517
+ assert H is H2._graph
518
+ assert H2.has_edge(0, 1)
519
+ assert H2.has_edge(1, 0)
520
+ pytest.raises(nx.NetworkXError, H2.add_node, -1)
521
+ pytest.raises(nx.NetworkXError, H2.add_edge, 1, 2)
522
+ H.add_edge(1, 2)
523
+ assert H2.has_edge(1, 2)
524
+ assert H2.has_edge(2, 1)
525
+
526
+ def test_directed_class(self):
527
+ G = self.Graph()
528
+
529
+ class newGraph(G.to_undirected_class()):
530
+ def to_directed_class(self):
531
+ return newDiGraph
532
+
533
+ def to_undirected_class(self):
534
+ return newGraph
535
+
536
+ class newDiGraph(G.to_directed_class()):
537
+ def to_directed_class(self):
538
+ return newDiGraph
539
+
540
+ def to_undirected_class(self):
541
+ return newGraph
542
+
543
+ G = newDiGraph() if G.is_directed() else newGraph()
544
+ H = G.to_directed()
545
+ assert isinstance(H, newDiGraph)
546
+ H = G.to_undirected()
547
+ assert isinstance(H, newGraph)
548
+
549
+ def test_to_directed(self):
550
+ G = self.K3
551
+ self.add_attributes(G)
552
+ H = nx.DiGraph(G)
553
+ self.is_shallow_copy(H, G)
554
+ self.different_attrdict(H, G)
555
+ H = G.to_directed()
556
+ self.is_deepcopy(H, G)
557
+
558
+ def test_subgraph(self):
559
+ G = self.K3
560
+ self.add_attributes(G)
561
+ H = G.subgraph([0, 1, 2, 5])
562
+ self.graphs_equal(H, G)
563
+ self.same_attrdict(H, G)
564
+ self.shallow_copy_attrdict(H, G)
565
+
566
+ H = G.subgraph(0)
567
+ assert H.adj == {0: {}}
568
+ H = G.subgraph([])
569
+ assert H.adj == {}
570
+ assert G.adj != {}
571
+
572
+ def test_selfloops_attr(self):
573
+ G = self.K3.copy()
574
+ G.add_edge(0, 0)
575
+ G.add_edge(1, 1, weight=2)
576
+ assert edges_equal(
577
+ nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
578
+ )
579
+ assert edges_equal(
580
+ nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)]
581
+ )
582
+
583
+
584
+ class TestGraph(BaseAttrGraphTester):
585
+ """Tests specific to dict-of-dict-of-dict graph data structure"""
586
+
587
+ def setup_method(self):
588
+ self.Graph = nx.Graph
589
+ # build dict-of-dict-of-dict K3
590
+ ed1, ed2, ed3 = ({}, {}, {})
591
+ self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
592
+ self.k3edges = [(0, 1), (0, 2), (1, 2)]
593
+ self.k3nodes = [0, 1, 2]
594
+ self.K3 = self.Graph()
595
+ self.K3._adj = self.k3adj
596
+ self.K3._node = {}
597
+ self.K3._node[0] = {}
598
+ self.K3._node[1] = {}
599
+ self.K3._node[2] = {}
600
+
601
+ def test_pickle(self):
602
+ G = self.K3
603
+ pg = pickle.loads(pickle.dumps(G, -1))
604
+ self.graphs_equal(pg, G)
605
+ pg = pickle.loads(pickle.dumps(G))
606
+ self.graphs_equal(pg, G)
607
+
608
+ def test_data_input(self):
609
+ G = self.Graph({1: [2], 2: [1]}, name="test")
610
+ assert G.name == "test"
611
+ assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
612
+
613
+ def test_adjacency(self):
614
+ G = self.K3
615
+ assert dict(G.adjacency()) == {
616
+ 0: {1: {}, 2: {}},
617
+ 1: {0: {}, 2: {}},
618
+ 2: {0: {}, 1: {}},
619
+ }
620
+
621
+ def test_getitem(self):
622
+ G = self.K3
623
+ assert G.adj[0] == {1: {}, 2: {}}
624
+ assert G[0] == {1: {}, 2: {}}
625
+ with pytest.raises(KeyError):
626
+ G.__getitem__("j")
627
+ with pytest.raises(TypeError):
628
+ G.__getitem__(["A"])
629
+
630
+ def test_add_node(self):
631
+ G = self.Graph()
632
+ G.add_node(0)
633
+ assert G.adj == {0: {}}
634
+ # test add attributes
635
+ G.add_node(1, c="red")
636
+ G.add_node(2, c="blue")
637
+ G.add_node(3, c="red")
638
+ assert G.nodes[1]["c"] == "red"
639
+ assert G.nodes[2]["c"] == "blue"
640
+ assert G.nodes[3]["c"] == "red"
641
+ # test updating attributes
642
+ G.add_node(1, c="blue")
643
+ G.add_node(2, c="red")
644
+ G.add_node(3, c="blue")
645
+ assert G.nodes[1]["c"] == "blue"
646
+ assert G.nodes[2]["c"] == "red"
647
+ assert G.nodes[3]["c"] == "blue"
648
+
649
+ def test_add_nodes_from(self):
650
+ G = self.Graph()
651
+ G.add_nodes_from([0, 1, 2])
652
+ assert G.adj == {0: {}, 1: {}, 2: {}}
653
+ # test add attributes
654
+ G.add_nodes_from([0, 1, 2], c="red")
655
+ assert G.nodes[0]["c"] == "red"
656
+ assert G.nodes[2]["c"] == "red"
657
+ # test that attribute dicts are not the same
658
+ assert G.nodes[0] is not G.nodes[1]
659
+ # test updating attributes
660
+ G.add_nodes_from([0, 1, 2], c="blue")
661
+ assert G.nodes[0]["c"] == "blue"
662
+ assert G.nodes[2]["c"] == "blue"
663
+ assert G.nodes[0] is not G.nodes[1]
664
+ # test tuple input
665
+ H = self.Graph()
666
+ H.add_nodes_from(G.nodes(data=True))
667
+ assert H.nodes[0]["c"] == "blue"
668
+ assert H.nodes[2]["c"] == "blue"
669
+ assert H.nodes[0] is not H.nodes[1]
670
+ # specific overrides general
671
+ H.add_nodes_from([0, (1, {"c": "green"}), (3, {"c": "cyan"})], c="red")
672
+ assert H.nodes[0]["c"] == "red"
673
+ assert H.nodes[1]["c"] == "green"
674
+ assert H.nodes[2]["c"] == "blue"
675
+ assert H.nodes[3]["c"] == "cyan"
676
+
677
+ def test_remove_node(self):
678
+ G = self.K3.copy()
679
+ G.remove_node(0)
680
+ assert G.adj == {1: {2: {}}, 2: {1: {}}}
681
+ with pytest.raises(nx.NetworkXError):
682
+ G.remove_node(-1)
683
+
684
+ # generator here to implement list,set,string...
685
+
686
+ def test_remove_nodes_from(self):
687
+ G = self.K3.copy()
688
+ G.remove_nodes_from([0, 1])
689
+ assert G.adj == {2: {}}
690
+ G.remove_nodes_from([-1]) # silent fail
691
+
692
+ def test_add_edge(self):
693
+ G = self.Graph()
694
+ G.add_edge(0, 1)
695
+ assert G.adj == {0: {1: {}}, 1: {0: {}}}
696
+ G = self.Graph()
697
+ G.add_edge(*(0, 1))
698
+ assert G.adj == {0: {1: {}}, 1: {0: {}}}
699
+ G = self.Graph()
700
+ with pytest.raises(ValueError):
701
+ G.add_edge(None, "anything")
702
+
703
+ def test_add_edges_from(self):
704
+ G = self.Graph()
705
+ G.add_edges_from([(0, 1), (0, 2, {"weight": 3})])
706
+ assert G.adj == {
707
+ 0: {1: {}, 2: {"weight": 3}},
708
+ 1: {0: {}},
709
+ 2: {0: {"weight": 3}},
710
+ }
711
+ G = self.Graph()
712
+ G.add_edges_from([(0, 1), (0, 2, {"weight": 3}), (1, 2, {"data": 4})], data=2)
713
+ assert G.adj == {
714
+ 0: {1: {"data": 2}, 2: {"weight": 3, "data": 2}},
715
+ 1: {0: {"data": 2}, 2: {"data": 4}},
716
+ 2: {0: {"weight": 3, "data": 2}, 1: {"data": 4}},
717
+ }
718
+
719
+ with pytest.raises(nx.NetworkXError):
720
+ G.add_edges_from([(0,)]) # too few in tuple
721
+ with pytest.raises(nx.NetworkXError):
722
+ G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
723
+ with pytest.raises(TypeError):
724
+ G.add_edges_from([0]) # not a tuple
725
+ with pytest.raises(ValueError):
726
+ G.add_edges_from([(None, 3), (3, 2)]) # None cannot be a node
727
+
728
+ def test_remove_edge(self):
729
+ G = self.K3.copy()
730
+ G.remove_edge(0, 1)
731
+ assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
732
+ with pytest.raises(nx.NetworkXError):
733
+ G.remove_edge(-1, 0)
734
+
735
+ def test_remove_edges_from(self):
736
+ G = self.K3.copy()
737
+ G.remove_edges_from([(0, 1)])
738
+ assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
739
+ G.remove_edges_from([(0, 0)]) # silent fail
740
+
741
+ def test_clear(self):
742
+ G = self.K3.copy()
743
+ G.graph["name"] = "K3"
744
+ G.clear()
745
+ assert list(G.nodes) == []
746
+ assert G.adj == {}
747
+ assert G.graph == {}
748
+
749
+ def test_clear_edges(self):
750
+ G = self.K3.copy()
751
+ G.graph["name"] = "K3"
752
+ nodes = list(G.nodes)
753
+ G.clear_edges()
754
+ assert list(G.nodes) == nodes
755
+ assert G.adj == {0: {}, 1: {}, 2: {}}
756
+ assert list(G.edges) == []
757
+ assert G.graph["name"] == "K3"
758
+
759
+ def test_edges_data(self):
760
+ G = self.K3
761
+ all_edges = [(0, 1, {}), (0, 2, {}), (1, 2, {})]
762
+ assert edges_equal(G.edges(data=True), all_edges)
763
+ assert edges_equal(G.edges(0, data=True), [(0, 1, {}), (0, 2, {})])
764
+ assert edges_equal(G.edges([0, 1], data=True), all_edges)
765
+ with pytest.raises(nx.NetworkXError):
766
+ G.edges(-1, True)
767
+
768
+ def test_get_edge_data(self):
769
+ G = self.K3.copy()
770
+ assert G.get_edge_data(0, 1) == {}
771
+ assert G[0][1] == {}
772
+ assert G.get_edge_data(10, 20) is None
773
+ assert G.get_edge_data(-1, 0) is None
774
+ assert G.get_edge_data(-1, 0, default=1) == 1
775
+
776
+ def test_update(self):
777
+ # specify both edges and nodes
778
+ G = self.K3.copy()
779
+ G.update(nodes=[3, (4, {"size": 2})], edges=[(4, 5), (6, 7, {"weight": 2})])
780
+ nlist = [
781
+ (0, {}),
782
+ (1, {}),
783
+ (2, {}),
784
+ (3, {}),
785
+ (4, {"size": 2}),
786
+ (5, {}),
787
+ (6, {}),
788
+ (7, {}),
789
+ ]
790
+ assert sorted(G.nodes.data()) == nlist
791
+ if G.is_directed():
792
+ elist = [
793
+ (0, 1, {}),
794
+ (0, 2, {}),
795
+ (1, 0, {}),
796
+ (1, 2, {}),
797
+ (2, 0, {}),
798
+ (2, 1, {}),
799
+ (4, 5, {}),
800
+ (6, 7, {"weight": 2}),
801
+ ]
802
+ else:
803
+ elist = [
804
+ (0, 1, {}),
805
+ (0, 2, {}),
806
+ (1, 2, {}),
807
+ (4, 5, {}),
808
+ (6, 7, {"weight": 2}),
809
+ ]
810
+ assert sorted(G.edges.data()) == elist
811
+ assert G.graph == {}
812
+
813
+ # no keywords -- order is edges, nodes
814
+ G = self.K3.copy()
815
+ G.update([(4, 5), (6, 7, {"weight": 2})], [3, (4, {"size": 2})])
816
+ assert sorted(G.nodes.data()) == nlist
817
+ assert sorted(G.edges.data()) == elist
818
+ assert G.graph == {}
819
+
820
+ # update using only a graph
821
+ G = self.Graph()
822
+ G.graph["foo"] = "bar"
823
+ G.add_node(2, data=4)
824
+ G.add_edge(0, 1, weight=0.5)
825
+ GG = G.copy()
826
+ H = self.Graph()
827
+ GG.update(H)
828
+ assert graphs_equal(G, GG)
829
+ H.update(G)
830
+ assert graphs_equal(H, G)
831
+
832
+ # update nodes only
833
+ H = self.Graph()
834
+ H.update(nodes=[3, 4])
835
+ assert H.nodes ^ {3, 4} == set()
836
+ assert H.size() == 0
837
+
838
+ # update edges only
839
+ H = self.Graph()
840
+ H.update(edges=[(3, 4)])
841
+ assert sorted(H.edges.data()) == [(3, 4, {})]
842
+ assert H.size() == 1
843
+
844
+ # No inputs -> exception
845
+ with pytest.raises(nx.NetworkXError):
846
+ nx.Graph().update()
847
+
848
+
849
+ class TestEdgeSubgraph:
850
+ """Unit tests for the :meth:`Graph.edge_subgraph` method."""
851
+
852
+ def setup_method(self):
853
+ # Create a path graph on five nodes.
854
+ G = nx.path_graph(5)
855
+ # Add some node, edge, and graph attributes.
856
+ for i in range(5):
857
+ G.nodes[i]["name"] = f"node{i}"
858
+ G.edges[0, 1]["name"] = "edge01"
859
+ G.edges[3, 4]["name"] = "edge34"
860
+ G.graph["name"] = "graph"
861
+ # Get the subgraph induced by the first and last edges.
862
+ self.G = G
863
+ self.H = G.edge_subgraph([(0, 1), (3, 4)])
864
+
865
+ def test_correct_nodes(self):
866
+ """Tests that the subgraph has the correct nodes."""
867
+ assert [0, 1, 3, 4] == sorted(self.H.nodes())
868
+
869
+ def test_correct_edges(self):
870
+ """Tests that the subgraph has the correct edges."""
871
+ assert [(0, 1, "edge01"), (3, 4, "edge34")] == sorted(self.H.edges(data="name"))
872
+
873
+ def test_add_node(self):
874
+ """Tests that adding a node to the original graph does not
875
+ affect the nodes of the subgraph.
876
+
877
+ """
878
+ self.G.add_node(5)
879
+ assert [0, 1, 3, 4] == sorted(self.H.nodes())
880
+
881
+ def test_remove_node(self):
882
+ """Tests that removing a node in the original graph does
883
+ affect the nodes of the subgraph.
884
+
885
+ """
886
+ self.G.remove_node(0)
887
+ assert [1, 3, 4] == sorted(self.H.nodes())
888
+
889
+ def test_node_attr_dict(self):
890
+ """Tests that the node attribute dictionary of the two graphs is
891
+ the same object.
892
+
893
+ """
894
+ for v in self.H:
895
+ assert self.G.nodes[v] == self.H.nodes[v]
896
+ # Making a change to G should make a change in H and vice versa.
897
+ self.G.nodes[0]["name"] = "foo"
898
+ assert self.G.nodes[0] == self.H.nodes[0]
899
+ self.H.nodes[1]["name"] = "bar"
900
+ assert self.G.nodes[1] == self.H.nodes[1]
901
+
902
+ def test_edge_attr_dict(self):
903
+ """Tests that the edge attribute dictionary of the two graphs is
904
+ the same object.
905
+
906
+ """
907
+ for u, v in self.H.edges():
908
+ assert self.G.edges[u, v] == self.H.edges[u, v]
909
+ # Making a change to G should make a change in H and vice versa.
910
+ self.G.edges[0, 1]["name"] = "foo"
911
+ assert self.G.edges[0, 1]["name"] == self.H.edges[0, 1]["name"]
912
+ self.H.edges[3, 4]["name"] = "bar"
913
+ assert self.G.edges[3, 4]["name"] == self.H.edges[3, 4]["name"]
914
+
915
+ def test_graph_attr_dict(self):
916
+ """Tests that the graph attribute dictionary of the two graphs
917
+ is the same object.
918
+
919
+ """
920
+ assert self.G.graph is self.H.graph