Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/Jinja2-3.1.3.dist-info/top_level.txt +1 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/__init__.py +49 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__init__.py +5 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/__init__.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/correlation.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/neighbor_degree.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/connectivity.py +122 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/correlation.py +302 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/mixing.py +250 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/pairs.py +118 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/__init__.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/base_test.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_connectivity.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_mixing.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_neighbor_degree.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_pairs.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/test_connectivity.py +143 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/test_pairs.py +87 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/__init__.py +4 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/__pycache__/equitable_coloring.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/greedy_coloring.py +572 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/__pycache__/__init__.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/__pycache__/kcutsets.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__init__.py +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/__init__.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_connectivity.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_cuts.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_kcutsets.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_stoer_wagner.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_connectivity.py +421 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_cuts.py +309 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +502 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +488 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_kcomponents.py +296 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_kcutsets.py +266 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__init__.py +5 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/__init__.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/astar.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/dense.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/generic.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/weighted.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/astar.py +214 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/dense.py +256 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_dense.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_generic.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_unweighted.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_weighted.cpython-311.pyc +0 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py +210 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +89 -0
- tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py +149 -0
tuning-competition-baseline/.venv/lib/python3.11/site-packages/Jinja2-3.1.3.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
jinja2
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/__init__.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NetworkX
|
| 3 |
+
========
|
| 4 |
+
|
| 5 |
+
NetworkX is a Python package for the creation, manipulation, and study of the
|
| 6 |
+
structure, dynamics, and functions of complex networks.
|
| 7 |
+
|
| 8 |
+
See https://networkx.org for complete documentation.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
__version__ = "3.2.1"
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
# These are imported in order as listed
|
| 15 |
+
from networkx.lazy_imports import _lazy_import
|
| 16 |
+
|
| 17 |
+
from networkx.exception import *
|
| 18 |
+
|
| 19 |
+
from networkx import utils
|
| 20 |
+
from networkx.utils.backends import _dispatch
|
| 21 |
+
|
| 22 |
+
from networkx import classes
|
| 23 |
+
from networkx.classes import filters
|
| 24 |
+
from networkx.classes import *
|
| 25 |
+
|
| 26 |
+
from networkx import convert
|
| 27 |
+
from networkx.convert import *
|
| 28 |
+
|
| 29 |
+
from networkx import convert_matrix
|
| 30 |
+
from networkx.convert_matrix import *
|
| 31 |
+
|
| 32 |
+
from networkx import relabel
|
| 33 |
+
from networkx.relabel import *
|
| 34 |
+
|
| 35 |
+
from networkx import generators
|
| 36 |
+
from networkx.generators import *
|
| 37 |
+
|
| 38 |
+
from networkx import readwrite
|
| 39 |
+
from networkx.readwrite import *
|
| 40 |
+
|
| 41 |
+
# Need to test with SciPy, when available
|
| 42 |
+
from networkx import algorithms
|
| 43 |
+
from networkx.algorithms import *
|
| 44 |
+
|
| 45 |
+
from networkx import linalg
|
| 46 |
+
from networkx.linalg import *
|
| 47 |
+
|
| 48 |
+
from networkx import drawing
|
| 49 |
+
from networkx.drawing import *
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from networkx.algorithms.assortativity.connectivity import *
|
| 2 |
+
from networkx.algorithms.assortativity.correlation import *
|
| 3 |
+
from networkx.algorithms.assortativity.mixing import *
|
| 4 |
+
from networkx.algorithms.assortativity.neighbor_degree import *
|
| 5 |
+
from networkx.algorithms.assortativity.pairs import *
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (566 Bytes). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/correlation.cpython-311.pyc
ADDED
|
Binary file (12.3 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/__pycache__/neighbor_degree.cpython-311.pyc
ADDED
|
Binary file (6.62 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/connectivity.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import defaultdict
|
| 2 |
+
|
| 3 |
+
import networkx as nx
|
| 4 |
+
|
| 5 |
+
__all__ = ["average_degree_connectivity"]
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@nx._dispatch(edge_attrs="weight")
|
| 9 |
+
def average_degree_connectivity(
|
| 10 |
+
G, source="in+out", target="in+out", nodes=None, weight=None
|
| 11 |
+
):
|
| 12 |
+
r"""Compute the average degree connectivity of graph.
|
| 13 |
+
|
| 14 |
+
The average degree connectivity is the average nearest neighbor degree of
|
| 15 |
+
nodes with degree k. For weighted graphs, an analogous measure can
|
| 16 |
+
be computed using the weighted average neighbors degree defined in
|
| 17 |
+
[1]_, for a node `i`, as
|
| 18 |
+
|
| 19 |
+
.. math::
|
| 20 |
+
|
| 21 |
+
k_{nn,i}^{w} = \frac{1}{s_i} \sum_{j \in N(i)} w_{ij} k_j
|
| 22 |
+
|
| 23 |
+
where `s_i` is the weighted degree of node `i`,
|
| 24 |
+
`w_{ij}` is the weight of the edge that links `i` and `j`,
|
| 25 |
+
and `N(i)` are the neighbors of node `i`.
|
| 26 |
+
|
| 27 |
+
Parameters
|
| 28 |
+
----------
|
| 29 |
+
G : NetworkX graph
|
| 30 |
+
|
| 31 |
+
source : "in"|"out"|"in+out" (default:"in+out")
|
| 32 |
+
Directed graphs only. Use "in"- or "out"-degree for source node.
|
| 33 |
+
|
| 34 |
+
target : "in"|"out"|"in+out" (default:"in+out"
|
| 35 |
+
Directed graphs only. Use "in"- or "out"-degree for target node.
|
| 36 |
+
|
| 37 |
+
nodes : list or iterable (optional)
|
| 38 |
+
Compute neighbor connectivity for these nodes. The default is all
|
| 39 |
+
nodes.
|
| 40 |
+
|
| 41 |
+
weight : string or None, optional (default=None)
|
| 42 |
+
The edge attribute that holds the numerical value used as a weight.
|
| 43 |
+
If None, then each edge has weight 1.
|
| 44 |
+
|
| 45 |
+
Returns
|
| 46 |
+
-------
|
| 47 |
+
d : dict
|
| 48 |
+
A dictionary keyed by degree k with the value of average connectivity.
|
| 49 |
+
|
| 50 |
+
Raises
|
| 51 |
+
------
|
| 52 |
+
NetworkXError
|
| 53 |
+
If either `source` or `target` are not one of 'in',
|
| 54 |
+
'out', or 'in+out'.
|
| 55 |
+
If either `source` or `target` is passed for an undirected graph.
|
| 56 |
+
|
| 57 |
+
Examples
|
| 58 |
+
--------
|
| 59 |
+
>>> G = nx.path_graph(4)
|
| 60 |
+
>>> G.edges[1, 2]["weight"] = 3
|
| 61 |
+
>>> nx.average_degree_connectivity(G)
|
| 62 |
+
{1: 2.0, 2: 1.5}
|
| 63 |
+
>>> nx.average_degree_connectivity(G, weight="weight")
|
| 64 |
+
{1: 2.0, 2: 1.75}
|
| 65 |
+
|
| 66 |
+
See Also
|
| 67 |
+
--------
|
| 68 |
+
average_neighbor_degree
|
| 69 |
+
|
| 70 |
+
References
|
| 71 |
+
----------
|
| 72 |
+
.. [1] A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
|
| 73 |
+
"The architecture of complex weighted networks".
|
| 74 |
+
PNAS 101 (11): 3747–3752 (2004).
|
| 75 |
+
"""
|
| 76 |
+
# First, determine the type of neighbors and the type of degree to use.
|
| 77 |
+
if G.is_directed():
|
| 78 |
+
if source not in ("in", "out", "in+out"):
|
| 79 |
+
raise nx.NetworkXError('source must be one of "in", "out", or "in+out"')
|
| 80 |
+
if target not in ("in", "out", "in+out"):
|
| 81 |
+
raise nx.NetworkXError('target must be one of "in", "out", or "in+out"')
|
| 82 |
+
direction = {"out": G.out_degree, "in": G.in_degree, "in+out": G.degree}
|
| 83 |
+
neighbor_funcs = {
|
| 84 |
+
"out": G.successors,
|
| 85 |
+
"in": G.predecessors,
|
| 86 |
+
"in+out": G.neighbors,
|
| 87 |
+
}
|
| 88 |
+
source_degree = direction[source]
|
| 89 |
+
target_degree = direction[target]
|
| 90 |
+
neighbors = neighbor_funcs[source]
|
| 91 |
+
# `reverse` indicates whether to look at the in-edge when
|
| 92 |
+
# computing the weight of an edge.
|
| 93 |
+
reverse = source == "in"
|
| 94 |
+
else:
|
| 95 |
+
if source != "in+out" or target != "in+out":
|
| 96 |
+
raise nx.NetworkXError(
|
| 97 |
+
f"source and target arguments are only supported for directed graphs"
|
| 98 |
+
)
|
| 99 |
+
source_degree = G.degree
|
| 100 |
+
target_degree = G.degree
|
| 101 |
+
neighbors = G.neighbors
|
| 102 |
+
reverse = False
|
| 103 |
+
dsum = defaultdict(int)
|
| 104 |
+
dnorm = defaultdict(int)
|
| 105 |
+
# Check if `source_nodes` is actually a single node in the graph.
|
| 106 |
+
source_nodes = source_degree(nodes)
|
| 107 |
+
if nodes in G:
|
| 108 |
+
source_nodes = [(nodes, source_degree(nodes))]
|
| 109 |
+
for n, k in source_nodes:
|
| 110 |
+
nbrdeg = target_degree(neighbors(n))
|
| 111 |
+
if weight is None:
|
| 112 |
+
s = sum(d for n, d in nbrdeg)
|
| 113 |
+
else: # weight nbr degree by weight of (n,nbr) edge
|
| 114 |
+
if reverse:
|
| 115 |
+
s = sum(G[nbr][n].get(weight, 1) * d for nbr, d in nbrdeg)
|
| 116 |
+
else:
|
| 117 |
+
s = sum(G[n][nbr].get(weight, 1) * d for nbr, d in nbrdeg)
|
| 118 |
+
dnorm[k] += source_degree(n, weight=weight)
|
| 119 |
+
dsum[k] += s
|
| 120 |
+
|
| 121 |
+
# normalize
|
| 122 |
+
return {k: avg if dnorm[k] == 0 else avg / dnorm[k] for k, avg in dsum.items()}
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/correlation.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Node assortativity coefficients and correlation measures.
|
| 2 |
+
"""
|
| 3 |
+
import networkx as nx
|
| 4 |
+
from networkx.algorithms.assortativity.mixing import (
|
| 5 |
+
attribute_mixing_matrix,
|
| 6 |
+
degree_mixing_matrix,
|
| 7 |
+
)
|
| 8 |
+
from networkx.algorithms.assortativity.pairs import node_degree_xy
|
| 9 |
+
|
| 10 |
+
__all__ = [
|
| 11 |
+
"degree_pearson_correlation_coefficient",
|
| 12 |
+
"degree_assortativity_coefficient",
|
| 13 |
+
"attribute_assortativity_coefficient",
|
| 14 |
+
"numeric_assortativity_coefficient",
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@nx._dispatch(edge_attrs="weight")
|
| 19 |
+
def degree_assortativity_coefficient(G, x="out", y="in", weight=None, nodes=None):
|
| 20 |
+
"""Compute degree assortativity of graph.
|
| 21 |
+
|
| 22 |
+
Assortativity measures the similarity of connections
|
| 23 |
+
in the graph with respect to the node degree.
|
| 24 |
+
|
| 25 |
+
Parameters
|
| 26 |
+
----------
|
| 27 |
+
G : NetworkX graph
|
| 28 |
+
|
| 29 |
+
x: string ('in','out')
|
| 30 |
+
The degree type for source node (directed graphs only).
|
| 31 |
+
|
| 32 |
+
y: string ('in','out')
|
| 33 |
+
The degree type for target node (directed graphs only).
|
| 34 |
+
|
| 35 |
+
weight: string or None, optional (default=None)
|
| 36 |
+
The edge attribute that holds the numerical value used
|
| 37 |
+
as a weight. If None, then each edge has weight 1.
|
| 38 |
+
The degree is the sum of the edge weights adjacent to the node.
|
| 39 |
+
|
| 40 |
+
nodes: list or iterable (optional)
|
| 41 |
+
Compute degree assortativity only for nodes in container.
|
| 42 |
+
The default is all nodes.
|
| 43 |
+
|
| 44 |
+
Returns
|
| 45 |
+
-------
|
| 46 |
+
r : float
|
| 47 |
+
Assortativity of graph by degree.
|
| 48 |
+
|
| 49 |
+
Examples
|
| 50 |
+
--------
|
| 51 |
+
>>> G = nx.path_graph(4)
|
| 52 |
+
>>> r = nx.degree_assortativity_coefficient(G)
|
| 53 |
+
>>> print(f"{r:3.1f}")
|
| 54 |
+
-0.5
|
| 55 |
+
|
| 56 |
+
See Also
|
| 57 |
+
--------
|
| 58 |
+
attribute_assortativity_coefficient
|
| 59 |
+
numeric_assortativity_coefficient
|
| 60 |
+
degree_mixing_dict
|
| 61 |
+
degree_mixing_matrix
|
| 62 |
+
|
| 63 |
+
Notes
|
| 64 |
+
-----
|
| 65 |
+
This computes Eq. (21) in Ref. [1]_ , where e is the joint
|
| 66 |
+
probability distribution (mixing matrix) of the degrees. If G is
|
| 67 |
+
directed than the matrix e is the joint probability of the
|
| 68 |
+
user-specified degree type for the source and target.
|
| 69 |
+
|
| 70 |
+
References
|
| 71 |
+
----------
|
| 72 |
+
.. [1] M. E. J. Newman, Mixing patterns in networks,
|
| 73 |
+
Physical Review E, 67 026126, 2003
|
| 74 |
+
.. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
|
| 75 |
+
Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
|
| 76 |
+
"""
|
| 77 |
+
if nodes is None:
|
| 78 |
+
nodes = G.nodes
|
| 79 |
+
|
| 80 |
+
degrees = None
|
| 81 |
+
|
| 82 |
+
if G.is_directed():
|
| 83 |
+
indeg = (
|
| 84 |
+
{d for _, d in G.in_degree(nodes, weight=weight)}
|
| 85 |
+
if "in" in (x, y)
|
| 86 |
+
else set()
|
| 87 |
+
)
|
| 88 |
+
outdeg = (
|
| 89 |
+
{d for _, d in G.out_degree(nodes, weight=weight)}
|
| 90 |
+
if "out" in (x, y)
|
| 91 |
+
else set()
|
| 92 |
+
)
|
| 93 |
+
degrees = set.union(indeg, outdeg)
|
| 94 |
+
else:
|
| 95 |
+
degrees = {d for _, d in G.degree(nodes, weight=weight)}
|
| 96 |
+
|
| 97 |
+
mapping = {d: i for i, d, in enumerate(degrees)}
|
| 98 |
+
M = degree_mixing_matrix(G, x=x, y=y, nodes=nodes, weight=weight, mapping=mapping)
|
| 99 |
+
|
| 100 |
+
return _numeric_ac(M, mapping=mapping)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
@nx._dispatch(edge_attrs="weight")
|
| 104 |
+
def degree_pearson_correlation_coefficient(G, x="out", y="in", weight=None, nodes=None):
|
| 105 |
+
"""Compute degree assortativity of graph.
|
| 106 |
+
|
| 107 |
+
Assortativity measures the similarity of connections
|
| 108 |
+
in the graph with respect to the node degree.
|
| 109 |
+
|
| 110 |
+
This is the same as degree_assortativity_coefficient but uses the
|
| 111 |
+
potentially faster scipy.stats.pearsonr function.
|
| 112 |
+
|
| 113 |
+
Parameters
|
| 114 |
+
----------
|
| 115 |
+
G : NetworkX graph
|
| 116 |
+
|
| 117 |
+
x: string ('in','out')
|
| 118 |
+
The degree type for source node (directed graphs only).
|
| 119 |
+
|
| 120 |
+
y: string ('in','out')
|
| 121 |
+
The degree type for target node (directed graphs only).
|
| 122 |
+
|
| 123 |
+
weight: string or None, optional (default=None)
|
| 124 |
+
The edge attribute that holds the numerical value used
|
| 125 |
+
as a weight. If None, then each edge has weight 1.
|
| 126 |
+
The degree is the sum of the edge weights adjacent to the node.
|
| 127 |
+
|
| 128 |
+
nodes: list or iterable (optional)
|
| 129 |
+
Compute pearson correlation of degrees only for specified nodes.
|
| 130 |
+
The default is all nodes.
|
| 131 |
+
|
| 132 |
+
Returns
|
| 133 |
+
-------
|
| 134 |
+
r : float
|
| 135 |
+
Assortativity of graph by degree.
|
| 136 |
+
|
| 137 |
+
Examples
|
| 138 |
+
--------
|
| 139 |
+
>>> G = nx.path_graph(4)
|
| 140 |
+
>>> r = nx.degree_pearson_correlation_coefficient(G)
|
| 141 |
+
>>> print(f"{r:3.1f}")
|
| 142 |
+
-0.5
|
| 143 |
+
|
| 144 |
+
Notes
|
| 145 |
+
-----
|
| 146 |
+
This calls scipy.stats.pearsonr.
|
| 147 |
+
|
| 148 |
+
References
|
| 149 |
+
----------
|
| 150 |
+
.. [1] M. E. J. Newman, Mixing patterns in networks
|
| 151 |
+
Physical Review E, 67 026126, 2003
|
| 152 |
+
.. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
|
| 153 |
+
Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
|
| 154 |
+
"""
|
| 155 |
+
import scipy as sp
|
| 156 |
+
|
| 157 |
+
xy = node_degree_xy(G, x=x, y=y, nodes=nodes, weight=weight)
|
| 158 |
+
x, y = zip(*xy)
|
| 159 |
+
return sp.stats.pearsonr(x, y)[0]
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
@nx._dispatch(node_attrs="attribute")
|
| 163 |
+
def attribute_assortativity_coefficient(G, attribute, nodes=None):
|
| 164 |
+
"""Compute assortativity for node attributes.
|
| 165 |
+
|
| 166 |
+
Assortativity measures the similarity of connections
|
| 167 |
+
in the graph with respect to the given attribute.
|
| 168 |
+
|
| 169 |
+
Parameters
|
| 170 |
+
----------
|
| 171 |
+
G : NetworkX graph
|
| 172 |
+
|
| 173 |
+
attribute : string
|
| 174 |
+
Node attribute key
|
| 175 |
+
|
| 176 |
+
nodes: list or iterable (optional)
|
| 177 |
+
Compute attribute assortativity for nodes in container.
|
| 178 |
+
The default is all nodes.
|
| 179 |
+
|
| 180 |
+
Returns
|
| 181 |
+
-------
|
| 182 |
+
r: float
|
| 183 |
+
Assortativity of graph for given attribute
|
| 184 |
+
|
| 185 |
+
Examples
|
| 186 |
+
--------
|
| 187 |
+
>>> G = nx.Graph()
|
| 188 |
+
>>> G.add_nodes_from([0, 1], color="red")
|
| 189 |
+
>>> G.add_nodes_from([2, 3], color="blue")
|
| 190 |
+
>>> G.add_edges_from([(0, 1), (2, 3)])
|
| 191 |
+
>>> print(nx.attribute_assortativity_coefficient(G, "color"))
|
| 192 |
+
1.0
|
| 193 |
+
|
| 194 |
+
Notes
|
| 195 |
+
-----
|
| 196 |
+
This computes Eq. (2) in Ref. [1]_ , (trace(M)-sum(M^2))/(1-sum(M^2)),
|
| 197 |
+
where M is the joint probability distribution (mixing matrix)
|
| 198 |
+
of the specified attribute.
|
| 199 |
+
|
| 200 |
+
References
|
| 201 |
+
----------
|
| 202 |
+
.. [1] M. E. J. Newman, Mixing patterns in networks,
|
| 203 |
+
Physical Review E, 67 026126, 2003
|
| 204 |
+
"""
|
| 205 |
+
M = attribute_mixing_matrix(G, attribute, nodes)
|
| 206 |
+
return attribute_ac(M)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
@nx._dispatch(node_attrs="attribute")
|
| 210 |
+
def numeric_assortativity_coefficient(G, attribute, nodes=None):
|
| 211 |
+
"""Compute assortativity for numerical node attributes.
|
| 212 |
+
|
| 213 |
+
Assortativity measures the similarity of connections
|
| 214 |
+
in the graph with respect to the given numeric attribute.
|
| 215 |
+
|
| 216 |
+
Parameters
|
| 217 |
+
----------
|
| 218 |
+
G : NetworkX graph
|
| 219 |
+
|
| 220 |
+
attribute : string
|
| 221 |
+
Node attribute key.
|
| 222 |
+
|
| 223 |
+
nodes: list or iterable (optional)
|
| 224 |
+
Compute numeric assortativity only for attributes of nodes in
|
| 225 |
+
container. The default is all nodes.
|
| 226 |
+
|
| 227 |
+
Returns
|
| 228 |
+
-------
|
| 229 |
+
r: float
|
| 230 |
+
Assortativity of graph for given attribute
|
| 231 |
+
|
| 232 |
+
Examples
|
| 233 |
+
--------
|
| 234 |
+
>>> G = nx.Graph()
|
| 235 |
+
>>> G.add_nodes_from([0, 1], size=2)
|
| 236 |
+
>>> G.add_nodes_from([2, 3], size=3)
|
| 237 |
+
>>> G.add_edges_from([(0, 1), (2, 3)])
|
| 238 |
+
>>> print(nx.numeric_assortativity_coefficient(G, "size"))
|
| 239 |
+
1.0
|
| 240 |
+
|
| 241 |
+
Notes
|
| 242 |
+
-----
|
| 243 |
+
This computes Eq. (21) in Ref. [1]_ , which is the Pearson correlation
|
| 244 |
+
coefficient of the specified (scalar valued) attribute across edges.
|
| 245 |
+
|
| 246 |
+
References
|
| 247 |
+
----------
|
| 248 |
+
.. [1] M. E. J. Newman, Mixing patterns in networks
|
| 249 |
+
Physical Review E, 67 026126, 2003
|
| 250 |
+
"""
|
| 251 |
+
if nodes is None:
|
| 252 |
+
nodes = G.nodes
|
| 253 |
+
vals = {G.nodes[n][attribute] for n in nodes}
|
| 254 |
+
mapping = {d: i for i, d, in enumerate(vals)}
|
| 255 |
+
M = attribute_mixing_matrix(G, attribute, nodes, mapping)
|
| 256 |
+
return _numeric_ac(M, mapping)
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def attribute_ac(M):
|
| 260 |
+
"""Compute assortativity for attribute matrix M.
|
| 261 |
+
|
| 262 |
+
Parameters
|
| 263 |
+
----------
|
| 264 |
+
M : numpy.ndarray
|
| 265 |
+
2D ndarray representing the attribute mixing matrix.
|
| 266 |
+
|
| 267 |
+
Notes
|
| 268 |
+
-----
|
| 269 |
+
This computes Eq. (2) in Ref. [1]_ , (trace(e)-sum(e^2))/(1-sum(e^2)),
|
| 270 |
+
where e is the joint probability distribution (mixing matrix)
|
| 271 |
+
of the specified attribute.
|
| 272 |
+
|
| 273 |
+
References
|
| 274 |
+
----------
|
| 275 |
+
.. [1] M. E. J. Newman, Mixing patterns in networks,
|
| 276 |
+
Physical Review E, 67 026126, 2003
|
| 277 |
+
"""
|
| 278 |
+
if M.sum() != 1.0:
|
| 279 |
+
M = M / M.sum()
|
| 280 |
+
s = (M @ M).sum()
|
| 281 |
+
t = M.trace()
|
| 282 |
+
r = (t - s) / (1 - s)
|
| 283 |
+
return r
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def _numeric_ac(M, mapping):
|
| 287 |
+
# M is a 2D numpy array
|
| 288 |
+
# numeric assortativity coefficient, pearsonr
|
| 289 |
+
import numpy as np
|
| 290 |
+
|
| 291 |
+
if M.sum() != 1.0:
|
| 292 |
+
M = M / M.sum()
|
| 293 |
+
x = np.array(list(mapping.keys()))
|
| 294 |
+
y = x # x and y have the same support
|
| 295 |
+
idx = list(mapping.values())
|
| 296 |
+
a = M.sum(axis=0)
|
| 297 |
+
b = M.sum(axis=1)
|
| 298 |
+
vara = (a[idx] * x**2).sum() - ((a[idx] * x).sum()) ** 2
|
| 299 |
+
varb = (b[idx] * y**2).sum() - ((b[idx] * y).sum()) ** 2
|
| 300 |
+
xy = np.outer(x, y)
|
| 301 |
+
ab = np.outer(a[idx], b[idx])
|
| 302 |
+
return (xy * (M - ab)).sum() / np.sqrt(vara * varb)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/mixing.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Mixing matrices for node attributes and degree.
|
| 3 |
+
"""
|
| 4 |
+
import networkx as nx
|
| 5 |
+
from networkx.algorithms.assortativity.pairs import node_attribute_xy, node_degree_xy
|
| 6 |
+
from networkx.utils import dict_to_numpy_array
|
| 7 |
+
|
| 8 |
+
__all__ = [
|
| 9 |
+
"attribute_mixing_matrix",
|
| 10 |
+
"attribute_mixing_dict",
|
| 11 |
+
"degree_mixing_matrix",
|
| 12 |
+
"degree_mixing_dict",
|
| 13 |
+
"mixing_dict",
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@nx._dispatch(node_attrs="attribute")
|
| 18 |
+
def attribute_mixing_dict(G, attribute, nodes=None, normalized=False):
|
| 19 |
+
"""Returns dictionary representation of mixing matrix for attribute.
|
| 20 |
+
|
| 21 |
+
Parameters
|
| 22 |
+
----------
|
| 23 |
+
G : graph
|
| 24 |
+
NetworkX graph object.
|
| 25 |
+
|
| 26 |
+
attribute : string
|
| 27 |
+
Node attribute key.
|
| 28 |
+
|
| 29 |
+
nodes: list or iterable (optional)
|
| 30 |
+
Unse nodes in container to build the dict. The default is all nodes.
|
| 31 |
+
|
| 32 |
+
normalized : bool (default=False)
|
| 33 |
+
Return counts if False or probabilities if True.
|
| 34 |
+
|
| 35 |
+
Examples
|
| 36 |
+
--------
|
| 37 |
+
>>> G = nx.Graph()
|
| 38 |
+
>>> G.add_nodes_from([0, 1], color="red")
|
| 39 |
+
>>> G.add_nodes_from([2, 3], color="blue")
|
| 40 |
+
>>> G.add_edge(1, 3)
|
| 41 |
+
>>> d = nx.attribute_mixing_dict(G, "color")
|
| 42 |
+
>>> print(d["red"]["blue"])
|
| 43 |
+
1
|
| 44 |
+
>>> print(d["blue"]["red"]) # d symmetric for undirected graphs
|
| 45 |
+
1
|
| 46 |
+
|
| 47 |
+
Returns
|
| 48 |
+
-------
|
| 49 |
+
d : dictionary
|
| 50 |
+
Counts or joint probability of occurrence of attribute pairs.
|
| 51 |
+
"""
|
| 52 |
+
xy_iter = node_attribute_xy(G, attribute, nodes)
|
| 53 |
+
return mixing_dict(xy_iter, normalized=normalized)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
@nx._dispatch(node_attrs="attribute")
|
| 57 |
+
def attribute_mixing_matrix(G, attribute, nodes=None, mapping=None, normalized=True):
|
| 58 |
+
"""Returns mixing matrix for attribute.
|
| 59 |
+
|
| 60 |
+
Parameters
|
| 61 |
+
----------
|
| 62 |
+
G : graph
|
| 63 |
+
NetworkX graph object.
|
| 64 |
+
|
| 65 |
+
attribute : string
|
| 66 |
+
Node attribute key.
|
| 67 |
+
|
| 68 |
+
nodes: list or iterable (optional)
|
| 69 |
+
Use only nodes in container to build the matrix. The default is
|
| 70 |
+
all nodes.
|
| 71 |
+
|
| 72 |
+
mapping : dictionary, optional
|
| 73 |
+
Mapping from node attribute to integer index in matrix.
|
| 74 |
+
If not specified, an arbitrary ordering will be used.
|
| 75 |
+
|
| 76 |
+
normalized : bool (default=True)
|
| 77 |
+
Return counts if False or probabilities if True.
|
| 78 |
+
|
| 79 |
+
Returns
|
| 80 |
+
-------
|
| 81 |
+
m: numpy array
|
| 82 |
+
Counts or joint probability of occurrence of attribute pairs.
|
| 83 |
+
|
| 84 |
+
Notes
|
| 85 |
+
-----
|
| 86 |
+
If each node has a unique attribute value, the unnormalized mixing matrix
|
| 87 |
+
will be equal to the adjacency matrix. To get a denser mixing matrix,
|
| 88 |
+
the rounding can be performed to form groups of nodes with equal values.
|
| 89 |
+
For example, the exact height of persons in cm (180.79155222, 163.9080892,
|
| 90 |
+
163.30095355, 167.99016217, 168.21590163, ...) can be rounded to (180, 163,
|
| 91 |
+
163, 168, 168, ...).
|
| 92 |
+
|
| 93 |
+
Definitions of attribute mixing matrix vary on whether the matrix
|
| 94 |
+
should include rows for attribute values that don't arise. Here we
|
| 95 |
+
do not include such empty-rows. But you can force them to appear
|
| 96 |
+
by inputting a `mapping` that includes those values.
|
| 97 |
+
|
| 98 |
+
Examples
|
| 99 |
+
--------
|
| 100 |
+
>>> G = nx.path_graph(3)
|
| 101 |
+
>>> gender = {0: 'male', 1: 'female', 2: 'female'}
|
| 102 |
+
>>> nx.set_node_attributes(G, gender, 'gender')
|
| 103 |
+
>>> mapping = {'male': 0, 'female': 1}
|
| 104 |
+
>>> mix_mat = nx.attribute_mixing_matrix(G, 'gender', mapping=mapping)
|
| 105 |
+
>>> # mixing from male nodes to female nodes
|
| 106 |
+
>>> mix_mat[mapping['male'], mapping['female']]
|
| 107 |
+
0.25
|
| 108 |
+
"""
|
| 109 |
+
d = attribute_mixing_dict(G, attribute, nodes)
|
| 110 |
+
a = dict_to_numpy_array(d, mapping=mapping)
|
| 111 |
+
if normalized:
|
| 112 |
+
a = a / a.sum()
|
| 113 |
+
return a
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
@nx._dispatch(edge_attrs="weight")
|
| 117 |
+
def degree_mixing_dict(G, x="out", y="in", weight=None, nodes=None, normalized=False):
|
| 118 |
+
"""Returns dictionary representation of mixing matrix for degree.
|
| 119 |
+
|
| 120 |
+
Parameters
|
| 121 |
+
----------
|
| 122 |
+
G : graph
|
| 123 |
+
NetworkX graph object.
|
| 124 |
+
|
| 125 |
+
x: string ('in','out')
|
| 126 |
+
The degree type for source node (directed graphs only).
|
| 127 |
+
|
| 128 |
+
y: string ('in','out')
|
| 129 |
+
The degree type for target node (directed graphs only).
|
| 130 |
+
|
| 131 |
+
weight: string or None, optional (default=None)
|
| 132 |
+
The edge attribute that holds the numerical value used
|
| 133 |
+
as a weight. If None, then each edge has weight 1.
|
| 134 |
+
The degree is the sum of the edge weights adjacent to the node.
|
| 135 |
+
|
| 136 |
+
normalized : bool (default=False)
|
| 137 |
+
Return counts if False or probabilities if True.
|
| 138 |
+
|
| 139 |
+
Returns
|
| 140 |
+
-------
|
| 141 |
+
d: dictionary
|
| 142 |
+
Counts or joint probability of occurrence of degree pairs.
|
| 143 |
+
"""
|
| 144 |
+
xy_iter = node_degree_xy(G, x=x, y=y, nodes=nodes, weight=weight)
|
| 145 |
+
return mixing_dict(xy_iter, normalized=normalized)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
@nx._dispatch(edge_attrs="weight")
|
| 149 |
+
def degree_mixing_matrix(
|
| 150 |
+
G, x="out", y="in", weight=None, nodes=None, normalized=True, mapping=None
|
| 151 |
+
):
|
| 152 |
+
"""Returns mixing matrix for attribute.
|
| 153 |
+
|
| 154 |
+
Parameters
|
| 155 |
+
----------
|
| 156 |
+
G : graph
|
| 157 |
+
NetworkX graph object.
|
| 158 |
+
|
| 159 |
+
x: string ('in','out')
|
| 160 |
+
The degree type for source node (directed graphs only).
|
| 161 |
+
|
| 162 |
+
y: string ('in','out')
|
| 163 |
+
The degree type for target node (directed graphs only).
|
| 164 |
+
|
| 165 |
+
nodes: list or iterable (optional)
|
| 166 |
+
Build the matrix using only nodes in container.
|
| 167 |
+
The default is all nodes.
|
| 168 |
+
|
| 169 |
+
weight: string or None, optional (default=None)
|
| 170 |
+
The edge attribute that holds the numerical value used
|
| 171 |
+
as a weight. If None, then each edge has weight 1.
|
| 172 |
+
The degree is the sum of the edge weights adjacent to the node.
|
| 173 |
+
|
| 174 |
+
normalized : bool (default=True)
|
| 175 |
+
Return counts if False or probabilities if True.
|
| 176 |
+
|
| 177 |
+
mapping : dictionary, optional
|
| 178 |
+
Mapping from node degree to integer index in matrix.
|
| 179 |
+
If not specified, an arbitrary ordering will be used.
|
| 180 |
+
|
| 181 |
+
Returns
|
| 182 |
+
-------
|
| 183 |
+
m: numpy array
|
| 184 |
+
Counts, or joint probability, of occurrence of node degree.
|
| 185 |
+
|
| 186 |
+
Notes
|
| 187 |
+
-----
|
| 188 |
+
Definitions of degree mixing matrix vary on whether the matrix
|
| 189 |
+
should include rows for degree values that don't arise. Here we
|
| 190 |
+
do not include such empty-rows. But you can force them to appear
|
| 191 |
+
by inputting a `mapping` that includes those values. See examples.
|
| 192 |
+
|
| 193 |
+
Examples
|
| 194 |
+
--------
|
| 195 |
+
>>> G = nx.star_graph(3)
|
| 196 |
+
>>> mix_mat = nx.degree_mixing_matrix(G)
|
| 197 |
+
>>> mix_mat[0, 1] # mixing from node degree 1 to node degree 3
|
| 198 |
+
0.5
|
| 199 |
+
|
| 200 |
+
If you want every possible degree to appear as a row, even if no nodes
|
| 201 |
+
have that degree, use `mapping` as follows,
|
| 202 |
+
|
| 203 |
+
>>> max_degree = max(deg for n, deg in G.degree)
|
| 204 |
+
>>> mapping = {x: x for x in range(max_degree + 1)} # identity mapping
|
| 205 |
+
>>> mix_mat = nx.degree_mixing_matrix(G, mapping=mapping)
|
| 206 |
+
>>> mix_mat[3, 1] # mixing from node degree 3 to node degree 1
|
| 207 |
+
0.5
|
| 208 |
+
"""
|
| 209 |
+
d = degree_mixing_dict(G, x=x, y=y, nodes=nodes, weight=weight)
|
| 210 |
+
a = dict_to_numpy_array(d, mapping=mapping)
|
| 211 |
+
if normalized:
|
| 212 |
+
a = a / a.sum()
|
| 213 |
+
return a
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def mixing_dict(xy, normalized=False):
|
| 217 |
+
"""Returns a dictionary representation of mixing matrix.
|
| 218 |
+
|
| 219 |
+
Parameters
|
| 220 |
+
----------
|
| 221 |
+
xy : list or container of two-tuples
|
| 222 |
+
Pairs of (x,y) items.
|
| 223 |
+
|
| 224 |
+
attribute : string
|
| 225 |
+
Node attribute key
|
| 226 |
+
|
| 227 |
+
normalized : bool (default=False)
|
| 228 |
+
Return counts if False or probabilities if True.
|
| 229 |
+
|
| 230 |
+
Returns
|
| 231 |
+
-------
|
| 232 |
+
d: dictionary
|
| 233 |
+
Counts or Joint probability of occurrence of values in xy.
|
| 234 |
+
"""
|
| 235 |
+
d = {}
|
| 236 |
+
psum = 0.0
|
| 237 |
+
for x, y in xy:
|
| 238 |
+
if x not in d:
|
| 239 |
+
d[x] = {}
|
| 240 |
+
if y not in d:
|
| 241 |
+
d[y] = {}
|
| 242 |
+
v = d[x].get(y, 0)
|
| 243 |
+
d[x][y] = v + 1
|
| 244 |
+
psum += 1
|
| 245 |
+
|
| 246 |
+
if normalized:
|
| 247 |
+
for _, jdict in d.items():
|
| 248 |
+
for j in jdict:
|
| 249 |
+
jdict[j] /= psum
|
| 250 |
+
return d
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/pairs.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Generators of x-y pairs of node data."""
|
| 2 |
+
import networkx as nx
|
| 3 |
+
|
| 4 |
+
__all__ = ["node_attribute_xy", "node_degree_xy"]
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@nx._dispatch(node_attrs="attribute")
|
| 8 |
+
def node_attribute_xy(G, attribute, nodes=None):
|
| 9 |
+
"""Returns iterator of node-attribute pairs for all edges in G.
|
| 10 |
+
|
| 11 |
+
Parameters
|
| 12 |
+
----------
|
| 13 |
+
G: NetworkX graph
|
| 14 |
+
|
| 15 |
+
attribute: key
|
| 16 |
+
The node attribute key.
|
| 17 |
+
|
| 18 |
+
nodes: list or iterable (optional)
|
| 19 |
+
Use only edges that are incident to specified nodes.
|
| 20 |
+
The default is all nodes.
|
| 21 |
+
|
| 22 |
+
Returns
|
| 23 |
+
-------
|
| 24 |
+
(x, y): 2-tuple
|
| 25 |
+
Generates 2-tuple of (attribute, attribute) values.
|
| 26 |
+
|
| 27 |
+
Examples
|
| 28 |
+
--------
|
| 29 |
+
>>> G = nx.DiGraph()
|
| 30 |
+
>>> G.add_node(1, color="red")
|
| 31 |
+
>>> G.add_node(2, color="blue")
|
| 32 |
+
>>> G.add_edge(1, 2)
|
| 33 |
+
>>> list(nx.node_attribute_xy(G, "color"))
|
| 34 |
+
[('red', 'blue')]
|
| 35 |
+
|
| 36 |
+
Notes
|
| 37 |
+
-----
|
| 38 |
+
For undirected graphs each edge is produced twice, once for each edge
|
| 39 |
+
representation (u, v) and (v, u), with the exception of self-loop edges
|
| 40 |
+
which only appear once.
|
| 41 |
+
"""
|
| 42 |
+
if nodes is None:
|
| 43 |
+
nodes = set(G)
|
| 44 |
+
else:
|
| 45 |
+
nodes = set(nodes)
|
| 46 |
+
Gnodes = G.nodes
|
| 47 |
+
for u, nbrsdict in G.adjacency():
|
| 48 |
+
if u not in nodes:
|
| 49 |
+
continue
|
| 50 |
+
uattr = Gnodes[u].get(attribute, None)
|
| 51 |
+
if G.is_multigraph():
|
| 52 |
+
for v, keys in nbrsdict.items():
|
| 53 |
+
vattr = Gnodes[v].get(attribute, None)
|
| 54 |
+
for _ in keys:
|
| 55 |
+
yield (uattr, vattr)
|
| 56 |
+
else:
|
| 57 |
+
for v in nbrsdict:
|
| 58 |
+
vattr = Gnodes[v].get(attribute, None)
|
| 59 |
+
yield (uattr, vattr)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
@nx._dispatch(edge_attrs="weight")
|
| 63 |
+
def node_degree_xy(G, x="out", y="in", weight=None, nodes=None):
|
| 64 |
+
"""Generate node degree-degree pairs for edges in G.
|
| 65 |
+
|
| 66 |
+
Parameters
|
| 67 |
+
----------
|
| 68 |
+
G: NetworkX graph
|
| 69 |
+
|
| 70 |
+
x: string ('in','out')
|
| 71 |
+
The degree type for source node (directed graphs only).
|
| 72 |
+
|
| 73 |
+
y: string ('in','out')
|
| 74 |
+
The degree type for target node (directed graphs only).
|
| 75 |
+
|
| 76 |
+
weight: string or None, optional (default=None)
|
| 77 |
+
The edge attribute that holds the numerical value used
|
| 78 |
+
as a weight. If None, then each edge has weight 1.
|
| 79 |
+
The degree is the sum of the edge weights adjacent to the node.
|
| 80 |
+
|
| 81 |
+
nodes: list or iterable (optional)
|
| 82 |
+
Use only edges that are adjacency to specified nodes.
|
| 83 |
+
The default is all nodes.
|
| 84 |
+
|
| 85 |
+
Returns
|
| 86 |
+
-------
|
| 87 |
+
(x, y): 2-tuple
|
| 88 |
+
Generates 2-tuple of (degree, degree) values.
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
Examples
|
| 92 |
+
--------
|
| 93 |
+
>>> G = nx.DiGraph()
|
| 94 |
+
>>> G.add_edge(1, 2)
|
| 95 |
+
>>> list(nx.node_degree_xy(G, x="out", y="in"))
|
| 96 |
+
[(1, 1)]
|
| 97 |
+
>>> list(nx.node_degree_xy(G, x="in", y="out"))
|
| 98 |
+
[(0, 0)]
|
| 99 |
+
|
| 100 |
+
Notes
|
| 101 |
+
-----
|
| 102 |
+
For undirected graphs each edge is produced twice, once for each edge
|
| 103 |
+
representation (u, v) and (v, u), with the exception of self-loop edges
|
| 104 |
+
which only appear once.
|
| 105 |
+
"""
|
| 106 |
+
nodes = set(G) if nodes is None else set(nodes)
|
| 107 |
+
if G.is_directed():
|
| 108 |
+
direction = {"out": G.out_degree, "in": G.in_degree}
|
| 109 |
+
xdeg = direction[x]
|
| 110 |
+
ydeg = direction[y]
|
| 111 |
+
else:
|
| 112 |
+
xdeg = ydeg = G.degree
|
| 113 |
+
|
| 114 |
+
for u, degu in xdeg(nodes, weight=weight):
|
| 115 |
+
# use G.edges to treat multigraphs correctly
|
| 116 |
+
neighbors = (nbr for _, nbr in G.edges(u) if nbr in nodes)
|
| 117 |
+
for _, degv in ydeg(neighbors, weight=weight):
|
| 118 |
+
yield degu, degv
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (240 Bytes). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/base_test.cpython-311.pyc
ADDED
|
Binary file (6.09 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_connectivity.cpython-311.pyc
ADDED
|
Binary file (8.9 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_mixing.cpython-311.pyc
ADDED
|
Binary file (13.2 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_neighbor_degree.cpython-311.pyc
ADDED
|
Binary file (7.57 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/__pycache__/test_pairs.cpython-311.pyc
ADDED
|
Binary file (5.93 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/test_connectivity.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from itertools import permutations
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
|
| 5 |
+
import networkx as nx
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class TestNeighborConnectivity:
|
| 9 |
+
def test_degree_p4(self):
|
| 10 |
+
G = nx.path_graph(4)
|
| 11 |
+
answer = {1: 2.0, 2: 1.5}
|
| 12 |
+
nd = nx.average_degree_connectivity(G)
|
| 13 |
+
assert nd == answer
|
| 14 |
+
|
| 15 |
+
D = G.to_directed()
|
| 16 |
+
answer = {2: 2.0, 4: 1.5}
|
| 17 |
+
nd = nx.average_degree_connectivity(D)
|
| 18 |
+
assert nd == answer
|
| 19 |
+
|
| 20 |
+
answer = {1: 2.0, 2: 1.5}
|
| 21 |
+
D = G.to_directed()
|
| 22 |
+
nd = nx.average_degree_connectivity(D, source="in", target="in")
|
| 23 |
+
assert nd == answer
|
| 24 |
+
|
| 25 |
+
D = G.to_directed()
|
| 26 |
+
nd = nx.average_degree_connectivity(D, source="in", target="in")
|
| 27 |
+
assert nd == answer
|
| 28 |
+
|
| 29 |
+
def test_degree_p4_weighted(self):
|
| 30 |
+
G = nx.path_graph(4)
|
| 31 |
+
G[1][2]["weight"] = 4
|
| 32 |
+
answer = {1: 2.0, 2: 1.8}
|
| 33 |
+
nd = nx.average_degree_connectivity(G, weight="weight")
|
| 34 |
+
assert nd == answer
|
| 35 |
+
answer = {1: 2.0, 2: 1.5}
|
| 36 |
+
nd = nx.average_degree_connectivity(G)
|
| 37 |
+
assert nd == answer
|
| 38 |
+
|
| 39 |
+
D = G.to_directed()
|
| 40 |
+
answer = {2: 2.0, 4: 1.8}
|
| 41 |
+
nd = nx.average_degree_connectivity(D, weight="weight")
|
| 42 |
+
assert nd == answer
|
| 43 |
+
|
| 44 |
+
answer = {1: 2.0, 2: 1.8}
|
| 45 |
+
D = G.to_directed()
|
| 46 |
+
nd = nx.average_degree_connectivity(
|
| 47 |
+
D, weight="weight", source="in", target="in"
|
| 48 |
+
)
|
| 49 |
+
assert nd == answer
|
| 50 |
+
|
| 51 |
+
D = G.to_directed()
|
| 52 |
+
nd = nx.average_degree_connectivity(
|
| 53 |
+
D, source="in", target="out", weight="weight"
|
| 54 |
+
)
|
| 55 |
+
assert nd == answer
|
| 56 |
+
|
| 57 |
+
def test_weight_keyword(self):
|
| 58 |
+
G = nx.path_graph(4)
|
| 59 |
+
G[1][2]["other"] = 4
|
| 60 |
+
answer = {1: 2.0, 2: 1.8}
|
| 61 |
+
nd = nx.average_degree_connectivity(G, weight="other")
|
| 62 |
+
assert nd == answer
|
| 63 |
+
answer = {1: 2.0, 2: 1.5}
|
| 64 |
+
nd = nx.average_degree_connectivity(G, weight=None)
|
| 65 |
+
assert nd == answer
|
| 66 |
+
|
| 67 |
+
D = G.to_directed()
|
| 68 |
+
answer = {2: 2.0, 4: 1.8}
|
| 69 |
+
nd = nx.average_degree_connectivity(D, weight="other")
|
| 70 |
+
assert nd == answer
|
| 71 |
+
|
| 72 |
+
answer = {1: 2.0, 2: 1.8}
|
| 73 |
+
D = G.to_directed()
|
| 74 |
+
nd = nx.average_degree_connectivity(D, weight="other", source="in", target="in")
|
| 75 |
+
assert nd == answer
|
| 76 |
+
|
| 77 |
+
D = G.to_directed()
|
| 78 |
+
nd = nx.average_degree_connectivity(D, weight="other", source="in", target="in")
|
| 79 |
+
assert nd == answer
|
| 80 |
+
|
| 81 |
+
def test_degree_barrat(self):
|
| 82 |
+
G = nx.star_graph(5)
|
| 83 |
+
G.add_edges_from([(5, 6), (5, 7), (5, 8), (5, 9)])
|
| 84 |
+
G[0][5]["weight"] = 5
|
| 85 |
+
nd = nx.average_degree_connectivity(G)[5]
|
| 86 |
+
assert nd == 1.8
|
| 87 |
+
nd = nx.average_degree_connectivity(G, weight="weight")[5]
|
| 88 |
+
assert nd == pytest.approx(3.222222, abs=1e-5)
|
| 89 |
+
|
| 90 |
+
def test_zero_deg(self):
|
| 91 |
+
G = nx.DiGraph()
|
| 92 |
+
G.add_edge(1, 2)
|
| 93 |
+
G.add_edge(1, 3)
|
| 94 |
+
G.add_edge(1, 4)
|
| 95 |
+
c = nx.average_degree_connectivity(G)
|
| 96 |
+
assert c == {1: 0, 3: 1}
|
| 97 |
+
c = nx.average_degree_connectivity(G, source="in", target="in")
|
| 98 |
+
assert c == {0: 0, 1: 0}
|
| 99 |
+
c = nx.average_degree_connectivity(G, source="in", target="out")
|
| 100 |
+
assert c == {0: 0, 1: 3}
|
| 101 |
+
c = nx.average_degree_connectivity(G, source="in", target="in+out")
|
| 102 |
+
assert c == {0: 0, 1: 3}
|
| 103 |
+
c = nx.average_degree_connectivity(G, source="out", target="out")
|
| 104 |
+
assert c == {0: 0, 3: 0}
|
| 105 |
+
c = nx.average_degree_connectivity(G, source="out", target="in")
|
| 106 |
+
assert c == {0: 0, 3: 1}
|
| 107 |
+
c = nx.average_degree_connectivity(G, source="out", target="in+out")
|
| 108 |
+
assert c == {0: 0, 3: 1}
|
| 109 |
+
|
| 110 |
+
def test_in_out_weight(self):
|
| 111 |
+
G = nx.DiGraph()
|
| 112 |
+
G.add_edge(1, 2, weight=1)
|
| 113 |
+
G.add_edge(1, 3, weight=1)
|
| 114 |
+
G.add_edge(3, 1, weight=1)
|
| 115 |
+
for s, t in permutations(["in", "out", "in+out"], 2):
|
| 116 |
+
c = nx.average_degree_connectivity(G, source=s, target=t)
|
| 117 |
+
cw = nx.average_degree_connectivity(G, source=s, target=t, weight="weight")
|
| 118 |
+
assert c == cw
|
| 119 |
+
|
| 120 |
+
def test_invalid_source(self):
|
| 121 |
+
with pytest.raises(nx.NetworkXError):
|
| 122 |
+
G = nx.DiGraph()
|
| 123 |
+
nx.average_degree_connectivity(G, source="bogus")
|
| 124 |
+
|
| 125 |
+
def test_invalid_target(self):
|
| 126 |
+
with pytest.raises(nx.NetworkXError):
|
| 127 |
+
G = nx.DiGraph()
|
| 128 |
+
nx.average_degree_connectivity(G, target="bogus")
|
| 129 |
+
|
| 130 |
+
def test_invalid_undirected_graph(self):
|
| 131 |
+
G = nx.Graph()
|
| 132 |
+
with pytest.raises(nx.NetworkXError):
|
| 133 |
+
nx.average_degree_connectivity(G, target="bogus")
|
| 134 |
+
with pytest.raises(nx.NetworkXError):
|
| 135 |
+
nx.average_degree_connectivity(G, source="bogus")
|
| 136 |
+
|
| 137 |
+
def test_single_node(self):
|
| 138 |
+
# TODO Is this really the intended behavior for providing a
|
| 139 |
+
# single node as the argument `nodes`? Shouldn't the function
|
| 140 |
+
# just return the connectivity value itself?
|
| 141 |
+
G = nx.trivial_graph()
|
| 142 |
+
conn = nx.average_degree_connectivity(G, nodes=0)
|
| 143 |
+
assert conn == {0: 0}
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/assortativity/tests/test_pairs.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import networkx as nx
|
| 2 |
+
|
| 3 |
+
from .base_test import BaseTestAttributeMixing, BaseTestDegreeMixing
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TestAttributeMixingXY(BaseTestAttributeMixing):
|
| 7 |
+
def test_node_attribute_xy_undirected(self):
|
| 8 |
+
attrxy = sorted(nx.node_attribute_xy(self.G, "fish"))
|
| 9 |
+
attrxy_result = sorted(
|
| 10 |
+
[
|
| 11 |
+
("one", "one"),
|
| 12 |
+
("one", "one"),
|
| 13 |
+
("two", "two"),
|
| 14 |
+
("two", "two"),
|
| 15 |
+
("one", "red"),
|
| 16 |
+
("red", "one"),
|
| 17 |
+
("blue", "two"),
|
| 18 |
+
("two", "blue"),
|
| 19 |
+
]
|
| 20 |
+
)
|
| 21 |
+
assert attrxy == attrxy_result
|
| 22 |
+
|
| 23 |
+
def test_node_attribute_xy_undirected_nodes(self):
|
| 24 |
+
attrxy = sorted(nx.node_attribute_xy(self.G, "fish", nodes=["one", "yellow"]))
|
| 25 |
+
attrxy_result = sorted([])
|
| 26 |
+
assert attrxy == attrxy_result
|
| 27 |
+
|
| 28 |
+
def test_node_attribute_xy_directed(self):
|
| 29 |
+
attrxy = sorted(nx.node_attribute_xy(self.D, "fish"))
|
| 30 |
+
attrxy_result = sorted(
|
| 31 |
+
[("one", "one"), ("two", "two"), ("one", "red"), ("two", "blue")]
|
| 32 |
+
)
|
| 33 |
+
assert attrxy == attrxy_result
|
| 34 |
+
|
| 35 |
+
def test_node_attribute_xy_multigraph(self):
|
| 36 |
+
attrxy = sorted(nx.node_attribute_xy(self.M, "fish"))
|
| 37 |
+
attrxy_result = [
|
| 38 |
+
("one", "one"),
|
| 39 |
+
("one", "one"),
|
| 40 |
+
("one", "one"),
|
| 41 |
+
("one", "one"),
|
| 42 |
+
("two", "two"),
|
| 43 |
+
("two", "two"),
|
| 44 |
+
]
|
| 45 |
+
assert attrxy == attrxy_result
|
| 46 |
+
|
| 47 |
+
def test_node_attribute_xy_selfloop(self):
|
| 48 |
+
attrxy = sorted(nx.node_attribute_xy(self.S, "fish"))
|
| 49 |
+
attrxy_result = [("one", "one"), ("two", "two")]
|
| 50 |
+
assert attrxy == attrxy_result
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class TestDegreeMixingXY(BaseTestDegreeMixing):
|
| 54 |
+
def test_node_degree_xy_undirected(self):
|
| 55 |
+
xy = sorted(nx.node_degree_xy(self.P4))
|
| 56 |
+
xy_result = sorted([(1, 2), (2, 1), (2, 2), (2, 2), (1, 2), (2, 1)])
|
| 57 |
+
assert xy == xy_result
|
| 58 |
+
|
| 59 |
+
def test_node_degree_xy_undirected_nodes(self):
|
| 60 |
+
xy = sorted(nx.node_degree_xy(self.P4, nodes=[0, 1, -1]))
|
| 61 |
+
xy_result = sorted([(1, 2), (2, 1)])
|
| 62 |
+
assert xy == xy_result
|
| 63 |
+
|
| 64 |
+
def test_node_degree_xy_directed(self):
|
| 65 |
+
xy = sorted(nx.node_degree_xy(self.D))
|
| 66 |
+
xy_result = sorted([(2, 1), (2, 3), (1, 3), (1, 3)])
|
| 67 |
+
assert xy == xy_result
|
| 68 |
+
|
| 69 |
+
def test_node_degree_xy_multigraph(self):
|
| 70 |
+
xy = sorted(nx.node_degree_xy(self.M))
|
| 71 |
+
xy_result = sorted(
|
| 72 |
+
[(2, 3), (2, 3), (3, 2), (3, 2), (2, 3), (3, 2), (1, 2), (2, 1)]
|
| 73 |
+
)
|
| 74 |
+
assert xy == xy_result
|
| 75 |
+
|
| 76 |
+
def test_node_degree_xy_selfloop(self):
|
| 77 |
+
xy = sorted(nx.node_degree_xy(self.S))
|
| 78 |
+
xy_result = sorted([(2, 2), (2, 2)])
|
| 79 |
+
assert xy == xy_result
|
| 80 |
+
|
| 81 |
+
def test_node_degree_xy_weighted(self):
|
| 82 |
+
G = nx.Graph()
|
| 83 |
+
G.add_edge(1, 2, weight=7)
|
| 84 |
+
G.add_edge(2, 3, weight=10)
|
| 85 |
+
xy = sorted(nx.node_degree_xy(G, weight="weight"))
|
| 86 |
+
xy_result = sorted([(7, 17), (17, 10), (17, 7), (10, 17)])
|
| 87 |
+
assert xy == xy_result
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from networkx.algorithms.coloring.greedy_coloring import *
|
| 2 |
+
from networkx.algorithms.coloring.equitable_coloring import equitable_color
|
| 3 |
+
|
| 4 |
+
__all__ = ["greedy_color", "equitable_color"]
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/__pycache__/equitable_coloring.cpython-311.pyc
ADDED
|
Binary file (19 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/coloring/greedy_coloring.py
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Greedy graph coloring using various strategies.
|
| 3 |
+
"""
|
| 4 |
+
import itertools
|
| 5 |
+
from collections import defaultdict, deque
|
| 6 |
+
|
| 7 |
+
import networkx as nx
|
| 8 |
+
from networkx.utils import arbitrary_element, py_random_state
|
| 9 |
+
|
| 10 |
+
__all__ = [
|
| 11 |
+
"greedy_color",
|
| 12 |
+
"strategy_connected_sequential",
|
| 13 |
+
"strategy_connected_sequential_bfs",
|
| 14 |
+
"strategy_connected_sequential_dfs",
|
| 15 |
+
"strategy_independent_set",
|
| 16 |
+
"strategy_largest_first",
|
| 17 |
+
"strategy_random_sequential",
|
| 18 |
+
"strategy_saturation_largest_first",
|
| 19 |
+
"strategy_smallest_last",
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@nx._dispatch
|
| 24 |
+
def strategy_largest_first(G, colors):
|
| 25 |
+
"""Returns a list of the nodes of ``G`` in decreasing order by
|
| 26 |
+
degree.
|
| 27 |
+
|
| 28 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 29 |
+
|
| 30 |
+
"""
|
| 31 |
+
return sorted(G, key=G.degree, reverse=True)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@py_random_state(2)
|
| 35 |
+
@nx._dispatch
|
| 36 |
+
def strategy_random_sequential(G, colors, seed=None):
|
| 37 |
+
"""Returns a random permutation of the nodes of ``G`` as a list.
|
| 38 |
+
|
| 39 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 40 |
+
|
| 41 |
+
seed : integer, random_state, or None (default)
|
| 42 |
+
Indicator of random number generation state.
|
| 43 |
+
See :ref:`Randomness<randomness>`.
|
| 44 |
+
"""
|
| 45 |
+
nodes = list(G)
|
| 46 |
+
seed.shuffle(nodes)
|
| 47 |
+
return nodes
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
@nx._dispatch
|
| 51 |
+
def strategy_smallest_last(G, colors):
|
| 52 |
+
"""Returns a deque of the nodes of ``G``, "smallest" last.
|
| 53 |
+
|
| 54 |
+
Specifically, the degrees of each node are tracked in a bucket queue.
|
| 55 |
+
From this, the node of minimum degree is repeatedly popped from the
|
| 56 |
+
graph, updating its neighbors' degrees.
|
| 57 |
+
|
| 58 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 59 |
+
|
| 60 |
+
This implementation of the strategy runs in $O(n + m)$ time
|
| 61 |
+
(ignoring polylogarithmic factors), where $n$ is the number of nodes
|
| 62 |
+
and $m$ is the number of edges.
|
| 63 |
+
|
| 64 |
+
This strategy is related to :func:`strategy_independent_set`: if we
|
| 65 |
+
interpret each node removed as an independent set of size one, then
|
| 66 |
+
this strategy chooses an independent set of size one instead of a
|
| 67 |
+
maximal independent set.
|
| 68 |
+
|
| 69 |
+
"""
|
| 70 |
+
H = G.copy()
|
| 71 |
+
result = deque()
|
| 72 |
+
|
| 73 |
+
# Build initial degree list (i.e. the bucket queue data structure)
|
| 74 |
+
degrees = defaultdict(set) # set(), for fast random-access removals
|
| 75 |
+
lbound = float("inf")
|
| 76 |
+
for node, d in H.degree():
|
| 77 |
+
degrees[d].add(node)
|
| 78 |
+
lbound = min(lbound, d) # Lower bound on min-degree.
|
| 79 |
+
|
| 80 |
+
def find_min_degree():
|
| 81 |
+
# Save time by starting the iterator at `lbound`, not 0.
|
| 82 |
+
# The value that we find will be our new `lbound`, which we set later.
|
| 83 |
+
return next(d for d in itertools.count(lbound) if d in degrees)
|
| 84 |
+
|
| 85 |
+
for _ in G:
|
| 86 |
+
# Pop a min-degree node and add it to the list.
|
| 87 |
+
min_degree = find_min_degree()
|
| 88 |
+
u = degrees[min_degree].pop()
|
| 89 |
+
if not degrees[min_degree]: # Clean up the degree list.
|
| 90 |
+
del degrees[min_degree]
|
| 91 |
+
result.appendleft(u)
|
| 92 |
+
|
| 93 |
+
# Update degrees of removed node's neighbors.
|
| 94 |
+
for v in H[u]:
|
| 95 |
+
degree = H.degree(v)
|
| 96 |
+
degrees[degree].remove(v)
|
| 97 |
+
if not degrees[degree]: # Clean up the degree list.
|
| 98 |
+
del degrees[degree]
|
| 99 |
+
degrees[degree - 1].add(v)
|
| 100 |
+
|
| 101 |
+
# Finally, remove the node.
|
| 102 |
+
H.remove_node(u)
|
| 103 |
+
lbound = min_degree - 1 # Subtract 1 in case of tied neighbors.
|
| 104 |
+
|
| 105 |
+
return result
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def _maximal_independent_set(G):
|
| 109 |
+
"""Returns a maximal independent set of nodes in ``G`` by repeatedly
|
| 110 |
+
choosing an independent node of minimum degree (with respect to the
|
| 111 |
+
subgraph of unchosen nodes).
|
| 112 |
+
|
| 113 |
+
"""
|
| 114 |
+
result = set()
|
| 115 |
+
remaining = set(G)
|
| 116 |
+
while remaining:
|
| 117 |
+
G = G.subgraph(remaining)
|
| 118 |
+
v = min(remaining, key=G.degree)
|
| 119 |
+
result.add(v)
|
| 120 |
+
remaining -= set(G[v]) | {v}
|
| 121 |
+
return result
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@nx._dispatch
|
| 125 |
+
def strategy_independent_set(G, colors):
|
| 126 |
+
"""Uses a greedy independent set removal strategy to determine the
|
| 127 |
+
colors.
|
| 128 |
+
|
| 129 |
+
This function updates ``colors`` **in-place** and return ``None``,
|
| 130 |
+
unlike the other strategy functions in this module.
|
| 131 |
+
|
| 132 |
+
This algorithm repeatedly finds and removes a maximal independent
|
| 133 |
+
set, assigning each node in the set an unused color.
|
| 134 |
+
|
| 135 |
+
``G`` is a NetworkX graph.
|
| 136 |
+
|
| 137 |
+
This strategy is related to :func:`strategy_smallest_last`: in that
|
| 138 |
+
strategy, an independent set of size one is chosen at each step
|
| 139 |
+
instead of a maximal independent set.
|
| 140 |
+
|
| 141 |
+
"""
|
| 142 |
+
remaining_nodes = set(G)
|
| 143 |
+
while len(remaining_nodes) > 0:
|
| 144 |
+
nodes = _maximal_independent_set(G.subgraph(remaining_nodes))
|
| 145 |
+
remaining_nodes -= nodes
|
| 146 |
+
yield from nodes
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
@nx._dispatch
|
| 150 |
+
def strategy_connected_sequential_bfs(G, colors):
|
| 151 |
+
"""Returns an iterable over nodes in ``G`` in the order given by a
|
| 152 |
+
breadth-first traversal.
|
| 153 |
+
|
| 154 |
+
The generated sequence has the property that for each node except
|
| 155 |
+
the first, at least one neighbor appeared earlier in the sequence.
|
| 156 |
+
|
| 157 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 158 |
+
|
| 159 |
+
"""
|
| 160 |
+
return strategy_connected_sequential(G, colors, "bfs")
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
@nx._dispatch
|
| 164 |
+
def strategy_connected_sequential_dfs(G, colors):
|
| 165 |
+
"""Returns an iterable over nodes in ``G`` in the order given by a
|
| 166 |
+
depth-first traversal.
|
| 167 |
+
|
| 168 |
+
The generated sequence has the property that for each node except
|
| 169 |
+
the first, at least one neighbor appeared earlier in the sequence.
|
| 170 |
+
|
| 171 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 172 |
+
|
| 173 |
+
"""
|
| 174 |
+
return strategy_connected_sequential(G, colors, "dfs")
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
@nx._dispatch
|
| 178 |
+
def strategy_connected_sequential(G, colors, traversal="bfs"):
|
| 179 |
+
"""Returns an iterable over nodes in ``G`` in the order given by a
|
| 180 |
+
breadth-first or depth-first traversal.
|
| 181 |
+
|
| 182 |
+
``traversal`` must be one of the strings ``'dfs'`` or ``'bfs'``,
|
| 183 |
+
representing depth-first traversal or breadth-first traversal,
|
| 184 |
+
respectively.
|
| 185 |
+
|
| 186 |
+
The generated sequence has the property that for each node except
|
| 187 |
+
the first, at least one neighbor appeared earlier in the sequence.
|
| 188 |
+
|
| 189 |
+
``G`` is a NetworkX graph. ``colors`` is ignored.
|
| 190 |
+
|
| 191 |
+
"""
|
| 192 |
+
if traversal == "bfs":
|
| 193 |
+
traverse = nx.bfs_edges
|
| 194 |
+
elif traversal == "dfs":
|
| 195 |
+
traverse = nx.dfs_edges
|
| 196 |
+
else:
|
| 197 |
+
raise nx.NetworkXError(
|
| 198 |
+
"Please specify one of the strings 'bfs' or"
|
| 199 |
+
" 'dfs' for connected sequential ordering"
|
| 200 |
+
)
|
| 201 |
+
for component in nx.connected_components(G):
|
| 202 |
+
source = arbitrary_element(component)
|
| 203 |
+
# Yield the source node, then all the nodes in the specified
|
| 204 |
+
# traversal order.
|
| 205 |
+
yield source
|
| 206 |
+
for _, end in traverse(G.subgraph(component), source):
|
| 207 |
+
yield end
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
@nx._dispatch
|
| 211 |
+
def strategy_saturation_largest_first(G, colors):
|
| 212 |
+
"""Iterates over all the nodes of ``G`` in "saturation order" (also
|
| 213 |
+
known as "DSATUR").
|
| 214 |
+
|
| 215 |
+
``G`` is a NetworkX graph. ``colors`` is a dictionary mapping nodes of
|
| 216 |
+
``G`` to colors, for those nodes that have already been colored.
|
| 217 |
+
|
| 218 |
+
"""
|
| 219 |
+
distinct_colors = {v: set() for v in G}
|
| 220 |
+
|
| 221 |
+
# Add the node color assignments given in colors to the
|
| 222 |
+
# distinct colors set for each neighbor of that node
|
| 223 |
+
for node, color in colors.items():
|
| 224 |
+
for neighbor in G[node]:
|
| 225 |
+
distinct_colors[neighbor].add(color)
|
| 226 |
+
|
| 227 |
+
# Check that the color assignments in colors are valid
|
| 228 |
+
# i.e. no neighboring nodes have the same color
|
| 229 |
+
if len(colors) >= 2:
|
| 230 |
+
for node, color in colors.items():
|
| 231 |
+
if color in distinct_colors[node]:
|
| 232 |
+
raise nx.NetworkXError("Neighboring nodes must have different colors")
|
| 233 |
+
|
| 234 |
+
# If 0 nodes have been colored, simply choose the node of highest degree.
|
| 235 |
+
if not colors:
|
| 236 |
+
node = max(G, key=G.degree)
|
| 237 |
+
yield node
|
| 238 |
+
# Add the color 0 to the distinct colors set for each
|
| 239 |
+
# neighbor of that node.
|
| 240 |
+
for v in G[node]:
|
| 241 |
+
distinct_colors[v].add(0)
|
| 242 |
+
|
| 243 |
+
while len(G) != len(colors):
|
| 244 |
+
# Update the distinct color sets for the neighbors.
|
| 245 |
+
for node, color in colors.items():
|
| 246 |
+
for neighbor in G[node]:
|
| 247 |
+
distinct_colors[neighbor].add(color)
|
| 248 |
+
|
| 249 |
+
# Compute the maximum saturation and the set of nodes that
|
| 250 |
+
# achieve that saturation.
|
| 251 |
+
saturation = {v: len(c) for v, c in distinct_colors.items() if v not in colors}
|
| 252 |
+
# Yield the node with the highest saturation, and break ties by
|
| 253 |
+
# degree.
|
| 254 |
+
node = max(saturation, key=lambda v: (saturation[v], G.degree(v)))
|
| 255 |
+
yield node
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
#: Dictionary mapping name of a strategy as a string to the strategy function.
|
| 259 |
+
STRATEGIES = {
|
| 260 |
+
"largest_first": strategy_largest_first,
|
| 261 |
+
"random_sequential": strategy_random_sequential,
|
| 262 |
+
"smallest_last": strategy_smallest_last,
|
| 263 |
+
"independent_set": strategy_independent_set,
|
| 264 |
+
"connected_sequential_bfs": strategy_connected_sequential_bfs,
|
| 265 |
+
"connected_sequential_dfs": strategy_connected_sequential_dfs,
|
| 266 |
+
"connected_sequential": strategy_connected_sequential,
|
| 267 |
+
"saturation_largest_first": strategy_saturation_largest_first,
|
| 268 |
+
"DSATUR": strategy_saturation_largest_first,
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
@nx._dispatch
|
| 273 |
+
def greedy_color(G, strategy="largest_first", interchange=False):
|
| 274 |
+
"""Color a graph using various strategies of greedy graph coloring.
|
| 275 |
+
|
| 276 |
+
Attempts to color a graph using as few colors as possible, where no
|
| 277 |
+
neighbours of a node can have same color as the node itself. The
|
| 278 |
+
given strategy determines the order in which nodes are colored.
|
| 279 |
+
|
| 280 |
+
The strategies are described in [1]_, and smallest-last is based on
|
| 281 |
+
[2]_.
|
| 282 |
+
|
| 283 |
+
Parameters
|
| 284 |
+
----------
|
| 285 |
+
G : NetworkX graph
|
| 286 |
+
|
| 287 |
+
strategy : string or function(G, colors)
|
| 288 |
+
A function (or a string representing a function) that provides
|
| 289 |
+
the coloring strategy, by returning nodes in the ordering they
|
| 290 |
+
should be colored. ``G`` is the graph, and ``colors`` is a
|
| 291 |
+
dictionary of the currently assigned colors, keyed by nodes. The
|
| 292 |
+
function must return an iterable over all the nodes in ``G``.
|
| 293 |
+
|
| 294 |
+
If the strategy function is an iterator generator (that is, a
|
| 295 |
+
function with ``yield`` statements), keep in mind that the
|
| 296 |
+
``colors`` dictionary will be updated after each ``yield``, since
|
| 297 |
+
this function chooses colors greedily.
|
| 298 |
+
|
| 299 |
+
If ``strategy`` is a string, it must be one of the following,
|
| 300 |
+
each of which represents one of the built-in strategy functions.
|
| 301 |
+
|
| 302 |
+
* ``'largest_first'``
|
| 303 |
+
* ``'random_sequential'``
|
| 304 |
+
* ``'smallest_last'``
|
| 305 |
+
* ``'independent_set'``
|
| 306 |
+
* ``'connected_sequential_bfs'``
|
| 307 |
+
* ``'connected_sequential_dfs'``
|
| 308 |
+
* ``'connected_sequential'`` (alias for the previous strategy)
|
| 309 |
+
* ``'saturation_largest_first'``
|
| 310 |
+
* ``'DSATUR'`` (alias for the previous strategy)
|
| 311 |
+
|
| 312 |
+
interchange: bool
|
| 313 |
+
Will use the color interchange algorithm described by [3]_ if set
|
| 314 |
+
to ``True``.
|
| 315 |
+
|
| 316 |
+
Note that ``saturation_largest_first`` and ``independent_set``
|
| 317 |
+
do not work with interchange. Furthermore, if you use
|
| 318 |
+
interchange with your own strategy function, you cannot rely
|
| 319 |
+
on the values in the ``colors`` argument.
|
| 320 |
+
|
| 321 |
+
Returns
|
| 322 |
+
-------
|
| 323 |
+
A dictionary with keys representing nodes and values representing
|
| 324 |
+
corresponding coloring.
|
| 325 |
+
|
| 326 |
+
Examples
|
| 327 |
+
--------
|
| 328 |
+
>>> G = nx.cycle_graph(4)
|
| 329 |
+
>>> d = nx.coloring.greedy_color(G, strategy="largest_first")
|
| 330 |
+
>>> d in [{0: 0, 1: 1, 2: 0, 3: 1}, {0: 1, 1: 0, 2: 1, 3: 0}]
|
| 331 |
+
True
|
| 332 |
+
|
| 333 |
+
Raises
|
| 334 |
+
------
|
| 335 |
+
NetworkXPointlessConcept
|
| 336 |
+
If ``strategy`` is ``saturation_largest_first`` or
|
| 337 |
+
``independent_set`` and ``interchange`` is ``True``.
|
| 338 |
+
|
| 339 |
+
References
|
| 340 |
+
----------
|
| 341 |
+
.. [1] Adrian Kosowski, and Krzysztof Manuszewski,
|
| 342 |
+
Classical Coloring of Graphs, Graph Colorings, 2-19, 2004.
|
| 343 |
+
ISBN 0-8218-3458-4.
|
| 344 |
+
.. [2] David W. Matula, and Leland L. Beck, "Smallest-last
|
| 345 |
+
ordering and clustering and graph coloring algorithms." *J. ACM* 30,
|
| 346 |
+
3 (July 1983), 417–427. <https://doi.org/10.1145/2402.322385>
|
| 347 |
+
.. [3] Maciej M. Sysło, Narsingh Deo, Janusz S. Kowalik,
|
| 348 |
+
Discrete Optimization Algorithms with Pascal Programs, 415-424, 1983.
|
| 349 |
+
ISBN 0-486-45353-7.
|
| 350 |
+
|
| 351 |
+
"""
|
| 352 |
+
if len(G) == 0:
|
| 353 |
+
return {}
|
| 354 |
+
# Determine the strategy provided by the caller.
|
| 355 |
+
strategy = STRATEGIES.get(strategy, strategy)
|
| 356 |
+
if not callable(strategy):
|
| 357 |
+
raise nx.NetworkXError(
|
| 358 |
+
"strategy must be callable or a valid string. " f"{strategy} not valid."
|
| 359 |
+
)
|
| 360 |
+
# Perform some validation on the arguments before executing any
|
| 361 |
+
# strategy functions.
|
| 362 |
+
if interchange:
|
| 363 |
+
if strategy is strategy_independent_set:
|
| 364 |
+
msg = "interchange cannot be used with independent_set"
|
| 365 |
+
raise nx.NetworkXPointlessConcept(msg)
|
| 366 |
+
if strategy is strategy_saturation_largest_first:
|
| 367 |
+
msg = "interchange cannot be used with" " saturation_largest_first"
|
| 368 |
+
raise nx.NetworkXPointlessConcept(msg)
|
| 369 |
+
colors = {}
|
| 370 |
+
nodes = strategy(G, colors)
|
| 371 |
+
if interchange:
|
| 372 |
+
return _greedy_coloring_with_interchange(G, nodes)
|
| 373 |
+
for u in nodes:
|
| 374 |
+
# Set to keep track of colors of neighbours
|
| 375 |
+
neighbour_colors = {colors[v] for v in G[u] if v in colors}
|
| 376 |
+
# Find the first unused color.
|
| 377 |
+
for color in itertools.count():
|
| 378 |
+
if color not in neighbour_colors:
|
| 379 |
+
break
|
| 380 |
+
# Assign the new color to the current node.
|
| 381 |
+
colors[u] = color
|
| 382 |
+
return colors
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
# Tools for coloring with interchanges
|
| 386 |
+
class _Node:
|
| 387 |
+
__slots__ = ["node_id", "color", "adj_list", "adj_color"]
|
| 388 |
+
|
| 389 |
+
def __init__(self, node_id, n):
|
| 390 |
+
self.node_id = node_id
|
| 391 |
+
self.color = -1
|
| 392 |
+
self.adj_list = None
|
| 393 |
+
self.adj_color = [None for _ in range(n)]
|
| 394 |
+
|
| 395 |
+
def __repr__(self):
|
| 396 |
+
return (
|
| 397 |
+
f"Node_id: {self.node_id}, Color: {self.color}, "
|
| 398 |
+
f"Adj_list: ({self.adj_list}), adj_color: ({self.adj_color})"
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
def assign_color(self, adj_entry, color):
|
| 402 |
+
adj_entry.col_prev = None
|
| 403 |
+
adj_entry.col_next = self.adj_color[color]
|
| 404 |
+
self.adj_color[color] = adj_entry
|
| 405 |
+
if adj_entry.col_next is not None:
|
| 406 |
+
adj_entry.col_next.col_prev = adj_entry
|
| 407 |
+
|
| 408 |
+
def clear_color(self, adj_entry, color):
|
| 409 |
+
if adj_entry.col_prev is None:
|
| 410 |
+
self.adj_color[color] = adj_entry.col_next
|
| 411 |
+
else:
|
| 412 |
+
adj_entry.col_prev.col_next = adj_entry.col_next
|
| 413 |
+
if adj_entry.col_next is not None:
|
| 414 |
+
adj_entry.col_next.col_prev = adj_entry.col_prev
|
| 415 |
+
|
| 416 |
+
def iter_neighbors(self):
|
| 417 |
+
adj_node = self.adj_list
|
| 418 |
+
while adj_node is not None:
|
| 419 |
+
yield adj_node
|
| 420 |
+
adj_node = adj_node.next
|
| 421 |
+
|
| 422 |
+
def iter_neighbors_color(self, color):
|
| 423 |
+
adj_color_node = self.adj_color[color]
|
| 424 |
+
while adj_color_node is not None:
|
| 425 |
+
yield adj_color_node.node_id
|
| 426 |
+
adj_color_node = adj_color_node.col_next
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
class _AdjEntry:
|
| 430 |
+
__slots__ = ["node_id", "next", "mate", "col_next", "col_prev"]
|
| 431 |
+
|
| 432 |
+
def __init__(self, node_id):
|
| 433 |
+
self.node_id = node_id
|
| 434 |
+
self.next = None
|
| 435 |
+
self.mate = None
|
| 436 |
+
self.col_next = None
|
| 437 |
+
self.col_prev = None
|
| 438 |
+
|
| 439 |
+
def __repr__(self):
|
| 440 |
+
col_next = None if self.col_next is None else self.col_next.node_id
|
| 441 |
+
col_prev = None if self.col_prev is None else self.col_prev.node_id
|
| 442 |
+
return (
|
| 443 |
+
f"Node_id: {self.node_id}, Next: ({self.next}), "
|
| 444 |
+
f"Mate: ({self.mate.node_id}), "
|
| 445 |
+
f"col_next: ({col_next}), col_prev: ({col_prev})"
|
| 446 |
+
)
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def _greedy_coloring_with_interchange(G, nodes):
|
| 450 |
+
"""Return a coloring for `original_graph` using interchange approach
|
| 451 |
+
|
| 452 |
+
This procedure is an adaption of the algorithm described by [1]_,
|
| 453 |
+
and is an implementation of coloring with interchange. Please be
|
| 454 |
+
advised, that the datastructures used are rather complex because
|
| 455 |
+
they are optimized to minimize the time spent identifying
|
| 456 |
+
subcomponents of the graph, which are possible candidates for color
|
| 457 |
+
interchange.
|
| 458 |
+
|
| 459 |
+
Parameters
|
| 460 |
+
----------
|
| 461 |
+
G : NetworkX graph
|
| 462 |
+
The graph to be colored
|
| 463 |
+
|
| 464 |
+
nodes : list
|
| 465 |
+
nodes ordered using the strategy of choice
|
| 466 |
+
|
| 467 |
+
Returns
|
| 468 |
+
-------
|
| 469 |
+
dict :
|
| 470 |
+
A dictionary keyed by node to a color value
|
| 471 |
+
|
| 472 |
+
References
|
| 473 |
+
----------
|
| 474 |
+
.. [1] Maciej M. Syslo, Narsingh Deo, Janusz S. Kowalik,
|
| 475 |
+
Discrete Optimization Algorithms with Pascal Programs, 415-424, 1983.
|
| 476 |
+
ISBN 0-486-45353-7.
|
| 477 |
+
"""
|
| 478 |
+
n = len(G)
|
| 479 |
+
|
| 480 |
+
graph = {node: _Node(node, n) for node in G}
|
| 481 |
+
|
| 482 |
+
for node1, node2 in G.edges():
|
| 483 |
+
adj_entry1 = _AdjEntry(node2)
|
| 484 |
+
adj_entry2 = _AdjEntry(node1)
|
| 485 |
+
adj_entry1.mate = adj_entry2
|
| 486 |
+
adj_entry2.mate = adj_entry1
|
| 487 |
+
node1_head = graph[node1].adj_list
|
| 488 |
+
adj_entry1.next = node1_head
|
| 489 |
+
graph[node1].adj_list = adj_entry1
|
| 490 |
+
node2_head = graph[node2].adj_list
|
| 491 |
+
adj_entry2.next = node2_head
|
| 492 |
+
graph[node2].adj_list = adj_entry2
|
| 493 |
+
|
| 494 |
+
k = 0
|
| 495 |
+
for node in nodes:
|
| 496 |
+
# Find the smallest possible, unused color
|
| 497 |
+
neighbors = graph[node].iter_neighbors()
|
| 498 |
+
col_used = {graph[adj_node.node_id].color for adj_node in neighbors}
|
| 499 |
+
col_used.discard(-1)
|
| 500 |
+
k1 = next(itertools.dropwhile(lambda x: x in col_used, itertools.count()))
|
| 501 |
+
|
| 502 |
+
# k1 is now the lowest available color
|
| 503 |
+
if k1 > k:
|
| 504 |
+
connected = True
|
| 505 |
+
visited = set()
|
| 506 |
+
col1 = -1
|
| 507 |
+
col2 = -1
|
| 508 |
+
while connected and col1 < k:
|
| 509 |
+
col1 += 1
|
| 510 |
+
neighbor_cols = graph[node].iter_neighbors_color(col1)
|
| 511 |
+
col1_adj = list(neighbor_cols)
|
| 512 |
+
|
| 513 |
+
col2 = col1
|
| 514 |
+
while connected and col2 < k:
|
| 515 |
+
col2 += 1
|
| 516 |
+
visited = set(col1_adj)
|
| 517 |
+
frontier = list(col1_adj)
|
| 518 |
+
i = 0
|
| 519 |
+
while i < len(frontier):
|
| 520 |
+
search_node = frontier[i]
|
| 521 |
+
i += 1
|
| 522 |
+
col_opp = col2 if graph[search_node].color == col1 else col1
|
| 523 |
+
neighbor_cols = graph[search_node].iter_neighbors_color(col_opp)
|
| 524 |
+
|
| 525 |
+
for neighbor in neighbor_cols:
|
| 526 |
+
if neighbor not in visited:
|
| 527 |
+
visited.add(neighbor)
|
| 528 |
+
frontier.append(neighbor)
|
| 529 |
+
|
| 530 |
+
# Search if node is not adj to any col2 vertex
|
| 531 |
+
connected = (
|
| 532 |
+
len(
|
| 533 |
+
visited.intersection(graph[node].iter_neighbors_color(col2))
|
| 534 |
+
)
|
| 535 |
+
> 0
|
| 536 |
+
)
|
| 537 |
+
|
| 538 |
+
# If connected is false then we can swap !!!
|
| 539 |
+
if not connected:
|
| 540 |
+
# Update all the nodes in the component
|
| 541 |
+
for search_node in visited:
|
| 542 |
+
graph[search_node].color = (
|
| 543 |
+
col2 if graph[search_node].color == col1 else col1
|
| 544 |
+
)
|
| 545 |
+
col2_adj = graph[search_node].adj_color[col2]
|
| 546 |
+
graph[search_node].adj_color[col2] = graph[search_node].adj_color[
|
| 547 |
+
col1
|
| 548 |
+
]
|
| 549 |
+
graph[search_node].adj_color[col1] = col2_adj
|
| 550 |
+
|
| 551 |
+
# Update all the neighboring nodes
|
| 552 |
+
for search_node in visited:
|
| 553 |
+
col = graph[search_node].color
|
| 554 |
+
col_opp = col1 if col == col2 else col2
|
| 555 |
+
for adj_node in graph[search_node].iter_neighbors():
|
| 556 |
+
if graph[adj_node.node_id].color != col_opp:
|
| 557 |
+
# Direct reference to entry
|
| 558 |
+
adj_mate = adj_node.mate
|
| 559 |
+
graph[adj_node.node_id].clear_color(adj_mate, col_opp)
|
| 560 |
+
graph[adj_node.node_id].assign_color(adj_mate, col)
|
| 561 |
+
k1 = col1
|
| 562 |
+
|
| 563 |
+
# We can color this node color k1
|
| 564 |
+
graph[node].color = k1
|
| 565 |
+
k = max(k1, k)
|
| 566 |
+
|
| 567 |
+
# Update the neighbors of this node
|
| 568 |
+
for adj_node in graph[node].iter_neighbors():
|
| 569 |
+
adj_mate = adj_node.mate
|
| 570 |
+
graph[adj_node.node_id].assign_color(adj_mate, k1)
|
| 571 |
+
|
| 572 |
+
return {node.node_id: node.color for node in graph.values()}
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (589 Bytes). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/__pycache__/kcutsets.cpython-311.pyc
ADDED
|
Binary file (10.6 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__init__.py
ADDED
|
File without changes
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (239 Bytes). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_connectivity.cpython-311.pyc
ADDED
|
Binary file (29.7 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_cuts.cpython-311.pyc
ADDED
|
Binary file (18.2 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_kcutsets.cpython-311.pyc
ADDED
|
Binary file (15.3 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/__pycache__/test_stoer_wagner.cpython-311.pyc
ADDED
|
Binary file (7.11 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_connectivity.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
|
| 5 |
+
import networkx as nx
|
| 6 |
+
from networkx.algorithms import flow
|
| 7 |
+
from networkx.algorithms.connectivity import (
|
| 8 |
+
local_edge_connectivity,
|
| 9 |
+
local_node_connectivity,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
flow_funcs = [
|
| 13 |
+
flow.boykov_kolmogorov,
|
| 14 |
+
flow.dinitz,
|
| 15 |
+
flow.edmonds_karp,
|
| 16 |
+
flow.preflow_push,
|
| 17 |
+
flow.shortest_augmenting_path,
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# helper functions for tests
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _generate_no_biconnected(max_attempts=50):
|
| 25 |
+
attempts = 0
|
| 26 |
+
while True:
|
| 27 |
+
G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
|
| 28 |
+
if nx.is_connected(G) and not nx.is_biconnected(G):
|
| 29 |
+
attempts = 0
|
| 30 |
+
yield G
|
| 31 |
+
else:
|
| 32 |
+
if attempts >= max_attempts:
|
| 33 |
+
msg = f"Tried {max_attempts} times: no suitable Graph."
|
| 34 |
+
raise Exception(msg)
|
| 35 |
+
else:
|
| 36 |
+
attempts += 1
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_average_connectivity():
|
| 40 |
+
# figure 1 from:
|
| 41 |
+
# Beineke, L., O. Oellermann, and R. Pippert (2002). The average
|
| 42 |
+
# connectivity of a graph. Discrete mathematics 252(1-3), 31-45
|
| 43 |
+
# http://www.sciencedirect.com/science/article/pii/S0012365X01001807
|
| 44 |
+
G1 = nx.path_graph(3)
|
| 45 |
+
G1.add_edges_from([(1, 3), (1, 4)])
|
| 46 |
+
G2 = nx.path_graph(3)
|
| 47 |
+
G2.add_edges_from([(1, 3), (1, 4), (0, 3), (0, 4), (3, 4)])
|
| 48 |
+
G3 = nx.Graph()
|
| 49 |
+
for flow_func in flow_funcs:
|
| 50 |
+
kwargs = {"flow_func": flow_func}
|
| 51 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 52 |
+
assert nx.average_node_connectivity(G1, **kwargs) == 1, errmsg
|
| 53 |
+
assert nx.average_node_connectivity(G2, **kwargs) == 2.2, errmsg
|
| 54 |
+
assert nx.average_node_connectivity(G3, **kwargs) == 0, errmsg
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_average_connectivity_directed():
|
| 58 |
+
G = nx.DiGraph([(1, 3), (1, 4), (1, 5)])
|
| 59 |
+
for flow_func in flow_funcs:
|
| 60 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 61 |
+
assert nx.average_node_connectivity(G) == 0.25, errmsg
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def test_articulation_points():
|
| 65 |
+
Ggen = _generate_no_biconnected()
|
| 66 |
+
for flow_func in flow_funcs:
|
| 67 |
+
for i in range(3):
|
| 68 |
+
G = next(Ggen)
|
| 69 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 70 |
+
assert nx.node_connectivity(G, flow_func=flow_func) == 1, errmsg
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def test_brandes_erlebach():
|
| 74 |
+
# Figure 1 chapter 7: Connectivity
|
| 75 |
+
# http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
|
| 76 |
+
G = nx.Graph()
|
| 77 |
+
G.add_edges_from(
|
| 78 |
+
[
|
| 79 |
+
(1, 2),
|
| 80 |
+
(1, 3),
|
| 81 |
+
(1, 4),
|
| 82 |
+
(1, 5),
|
| 83 |
+
(2, 3),
|
| 84 |
+
(2, 6),
|
| 85 |
+
(3, 4),
|
| 86 |
+
(3, 6),
|
| 87 |
+
(4, 6),
|
| 88 |
+
(4, 7),
|
| 89 |
+
(5, 7),
|
| 90 |
+
(6, 8),
|
| 91 |
+
(6, 9),
|
| 92 |
+
(7, 8),
|
| 93 |
+
(7, 10),
|
| 94 |
+
(8, 11),
|
| 95 |
+
(9, 10),
|
| 96 |
+
(9, 11),
|
| 97 |
+
(10, 11),
|
| 98 |
+
]
|
| 99 |
+
)
|
| 100 |
+
for flow_func in flow_funcs:
|
| 101 |
+
kwargs = {"flow_func": flow_func}
|
| 102 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 103 |
+
assert 3 == local_edge_connectivity(G, 1, 11, **kwargs), errmsg
|
| 104 |
+
assert 3 == nx.edge_connectivity(G, 1, 11, **kwargs), errmsg
|
| 105 |
+
assert 2 == local_node_connectivity(G, 1, 11, **kwargs), errmsg
|
| 106 |
+
assert 2 == nx.node_connectivity(G, 1, 11, **kwargs), errmsg
|
| 107 |
+
assert 2 == nx.edge_connectivity(G, **kwargs), errmsg
|
| 108 |
+
assert 2 == nx.node_connectivity(G, **kwargs), errmsg
|
| 109 |
+
if flow_func is flow.preflow_push:
|
| 110 |
+
assert 3 == nx.edge_connectivity(G, 1, 11, cutoff=2, **kwargs), errmsg
|
| 111 |
+
else:
|
| 112 |
+
assert 2 == nx.edge_connectivity(G, 1, 11, cutoff=2, **kwargs), errmsg
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def test_white_harary_1():
|
| 116 |
+
# Figure 1b white and harary (2001)
|
| 117 |
+
# https://doi.org/10.1111/0081-1750.00098
|
| 118 |
+
# A graph with high adhesion (edge connectivity) and low cohesion
|
| 119 |
+
# (vertex connectivity)
|
| 120 |
+
G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
|
| 121 |
+
G.remove_node(7)
|
| 122 |
+
for i in range(4, 7):
|
| 123 |
+
G.add_edge(0, i)
|
| 124 |
+
G = nx.disjoint_union(G, nx.complete_graph(4))
|
| 125 |
+
G.remove_node(G.order() - 1)
|
| 126 |
+
for i in range(7, 10):
|
| 127 |
+
G.add_edge(0, i)
|
| 128 |
+
for flow_func in flow_funcs:
|
| 129 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 130 |
+
assert 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 131 |
+
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def test_white_harary_2():
|
| 135 |
+
# Figure 8 white and harary (2001)
|
| 136 |
+
# https://doi.org/10.1111/0081-1750.00098
|
| 137 |
+
G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
|
| 138 |
+
G.add_edge(0, 4)
|
| 139 |
+
# kappa <= lambda <= delta
|
| 140 |
+
assert 3 == min(nx.core_number(G).values())
|
| 141 |
+
for flow_func in flow_funcs:
|
| 142 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 143 |
+
assert 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 144 |
+
assert 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def test_complete_graphs():
|
| 148 |
+
for n in range(5, 20, 5):
|
| 149 |
+
for flow_func in flow_funcs:
|
| 150 |
+
G = nx.complete_graph(n)
|
| 151 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 152 |
+
assert n - 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 153 |
+
assert n - 1 == nx.node_connectivity(
|
| 154 |
+
G.to_directed(), flow_func=flow_func
|
| 155 |
+
), errmsg
|
| 156 |
+
assert n - 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 157 |
+
assert n - 1 == nx.edge_connectivity(
|
| 158 |
+
G.to_directed(), flow_func=flow_func
|
| 159 |
+
), errmsg
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def test_empty_graphs():
|
| 163 |
+
for k in range(5, 25, 5):
|
| 164 |
+
G = nx.empty_graph(k)
|
| 165 |
+
for flow_func in flow_funcs:
|
| 166 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 167 |
+
assert 0 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 168 |
+
assert 0 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def test_petersen():
|
| 172 |
+
G = nx.petersen_graph()
|
| 173 |
+
for flow_func in flow_funcs:
|
| 174 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 175 |
+
assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 176 |
+
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_tutte():
|
| 180 |
+
G = nx.tutte_graph()
|
| 181 |
+
for flow_func in flow_funcs:
|
| 182 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 183 |
+
assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 184 |
+
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def test_dodecahedral():
|
| 188 |
+
G = nx.dodecahedral_graph()
|
| 189 |
+
for flow_func in flow_funcs:
|
| 190 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 191 |
+
assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 192 |
+
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def test_octahedral():
|
| 196 |
+
G = nx.octahedral_graph()
|
| 197 |
+
for flow_func in flow_funcs:
|
| 198 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 199 |
+
assert 4 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 200 |
+
assert 4 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def test_icosahedral():
|
| 204 |
+
G = nx.icosahedral_graph()
|
| 205 |
+
for flow_func in flow_funcs:
|
| 206 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 207 |
+
assert 5 == nx.node_connectivity(G, flow_func=flow_func), errmsg
|
| 208 |
+
assert 5 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def test_missing_source():
|
| 212 |
+
G = nx.path_graph(4)
|
| 213 |
+
for flow_func in flow_funcs:
|
| 214 |
+
pytest.raises(
|
| 215 |
+
nx.NetworkXError, nx.node_connectivity, G, 10, 1, flow_func=flow_func
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def test_missing_target():
|
| 220 |
+
G = nx.path_graph(4)
|
| 221 |
+
for flow_func in flow_funcs:
|
| 222 |
+
pytest.raises(
|
| 223 |
+
nx.NetworkXError, nx.node_connectivity, G, 1, 10, flow_func=flow_func
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def test_edge_missing_source():
|
| 228 |
+
G = nx.path_graph(4)
|
| 229 |
+
for flow_func in flow_funcs:
|
| 230 |
+
pytest.raises(
|
| 231 |
+
nx.NetworkXError, nx.edge_connectivity, G, 10, 1, flow_func=flow_func
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def test_edge_missing_target():
|
| 236 |
+
G = nx.path_graph(4)
|
| 237 |
+
for flow_func in flow_funcs:
|
| 238 |
+
pytest.raises(
|
| 239 |
+
nx.NetworkXError, nx.edge_connectivity, G, 1, 10, flow_func=flow_func
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def test_not_weakly_connected():
|
| 244 |
+
G = nx.DiGraph()
|
| 245 |
+
nx.add_path(G, [1, 2, 3])
|
| 246 |
+
nx.add_path(G, [4, 5])
|
| 247 |
+
for flow_func in flow_funcs:
|
| 248 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 249 |
+
assert nx.node_connectivity(G) == 0, errmsg
|
| 250 |
+
assert nx.edge_connectivity(G) == 0, errmsg
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def test_not_connected():
|
| 254 |
+
G = nx.Graph()
|
| 255 |
+
nx.add_path(G, [1, 2, 3])
|
| 256 |
+
nx.add_path(G, [4, 5])
|
| 257 |
+
for flow_func in flow_funcs:
|
| 258 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 259 |
+
assert nx.node_connectivity(G) == 0, errmsg
|
| 260 |
+
assert nx.edge_connectivity(G) == 0, errmsg
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_directed_edge_connectivity():
|
| 264 |
+
G = nx.cycle_graph(10, create_using=nx.DiGraph()) # only one direction
|
| 265 |
+
D = nx.cycle_graph(10).to_directed() # 2 reciprocal edges
|
| 266 |
+
for flow_func in flow_funcs:
|
| 267 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 268 |
+
assert 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
|
| 269 |
+
assert 1 == local_edge_connectivity(G, 1, 4, flow_func=flow_func), errmsg
|
| 270 |
+
assert 1 == nx.edge_connectivity(G, 1, 4, flow_func=flow_func), errmsg
|
| 271 |
+
assert 2 == nx.edge_connectivity(D, flow_func=flow_func), errmsg
|
| 272 |
+
assert 2 == local_edge_connectivity(D, 1, 4, flow_func=flow_func), errmsg
|
| 273 |
+
assert 2 == nx.edge_connectivity(D, 1, 4, flow_func=flow_func), errmsg
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def test_cutoff():
|
| 277 |
+
G = nx.complete_graph(5)
|
| 278 |
+
for local_func in [local_edge_connectivity, local_node_connectivity]:
|
| 279 |
+
for flow_func in flow_funcs:
|
| 280 |
+
if flow_func is flow.preflow_push:
|
| 281 |
+
# cutoff is not supported by preflow_push
|
| 282 |
+
continue
|
| 283 |
+
for cutoff in [3, 2, 1]:
|
| 284 |
+
result = local_func(G, 0, 4, flow_func=flow_func, cutoff=cutoff)
|
| 285 |
+
assert cutoff == result, f"cutoff error in {flow_func.__name__}"
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def test_invalid_auxiliary():
|
| 289 |
+
G = nx.complete_graph(5)
|
| 290 |
+
pytest.raises(nx.NetworkXError, local_node_connectivity, G, 0, 3, auxiliary=G)
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def test_interface_only_source():
|
| 294 |
+
G = nx.complete_graph(5)
|
| 295 |
+
for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
|
| 296 |
+
pytest.raises(nx.NetworkXError, interface_func, G, s=0)
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def test_interface_only_target():
|
| 300 |
+
G = nx.complete_graph(5)
|
| 301 |
+
for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
|
| 302 |
+
pytest.raises(nx.NetworkXError, interface_func, G, t=3)
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def test_edge_connectivity_flow_vs_stoer_wagner():
|
| 306 |
+
graph_funcs = [nx.icosahedral_graph, nx.octahedral_graph, nx.dodecahedral_graph]
|
| 307 |
+
for graph_func in graph_funcs:
|
| 308 |
+
G = graph_func()
|
| 309 |
+
assert nx.stoer_wagner(G)[0] == nx.edge_connectivity(G)
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
class TestAllPairsNodeConnectivity:
|
| 313 |
+
@classmethod
|
| 314 |
+
def setup_class(cls):
|
| 315 |
+
cls.path = nx.path_graph(7)
|
| 316 |
+
cls.directed_path = nx.path_graph(7, create_using=nx.DiGraph())
|
| 317 |
+
cls.cycle = nx.cycle_graph(7)
|
| 318 |
+
cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
|
| 319 |
+
cls.gnp = nx.gnp_random_graph(30, 0.1, seed=42)
|
| 320 |
+
cls.directed_gnp = nx.gnp_random_graph(30, 0.1, directed=True, seed=42)
|
| 321 |
+
cls.K20 = nx.complete_graph(20)
|
| 322 |
+
cls.K10 = nx.complete_graph(10)
|
| 323 |
+
cls.K5 = nx.complete_graph(5)
|
| 324 |
+
cls.G_list = [
|
| 325 |
+
cls.path,
|
| 326 |
+
cls.directed_path,
|
| 327 |
+
cls.cycle,
|
| 328 |
+
cls.directed_cycle,
|
| 329 |
+
cls.gnp,
|
| 330 |
+
cls.directed_gnp,
|
| 331 |
+
cls.K10,
|
| 332 |
+
cls.K5,
|
| 333 |
+
cls.K20,
|
| 334 |
+
]
|
| 335 |
+
|
| 336 |
+
def test_cycles(self):
|
| 337 |
+
K_undir = nx.all_pairs_node_connectivity(self.cycle)
|
| 338 |
+
for source in K_undir:
|
| 339 |
+
for target, k in K_undir[source].items():
|
| 340 |
+
assert k == 2
|
| 341 |
+
K_dir = nx.all_pairs_node_connectivity(self.directed_cycle)
|
| 342 |
+
for source in K_dir:
|
| 343 |
+
for target, k in K_dir[source].items():
|
| 344 |
+
assert k == 1
|
| 345 |
+
|
| 346 |
+
def test_complete(self):
|
| 347 |
+
for G in [self.K10, self.K5, self.K20]:
|
| 348 |
+
K = nx.all_pairs_node_connectivity(G)
|
| 349 |
+
for source in K:
|
| 350 |
+
for target, k in K[source].items():
|
| 351 |
+
assert k == len(G) - 1
|
| 352 |
+
|
| 353 |
+
def test_paths(self):
|
| 354 |
+
K_undir = nx.all_pairs_node_connectivity(self.path)
|
| 355 |
+
for source in K_undir:
|
| 356 |
+
for target, k in K_undir[source].items():
|
| 357 |
+
assert k == 1
|
| 358 |
+
K_dir = nx.all_pairs_node_connectivity(self.directed_path)
|
| 359 |
+
for source in K_dir:
|
| 360 |
+
for target, k in K_dir[source].items():
|
| 361 |
+
if source < target:
|
| 362 |
+
assert k == 1
|
| 363 |
+
else:
|
| 364 |
+
assert k == 0
|
| 365 |
+
|
| 366 |
+
def test_all_pairs_connectivity_nbunch(self):
|
| 367 |
+
G = nx.complete_graph(5)
|
| 368 |
+
nbunch = [0, 2, 3]
|
| 369 |
+
C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
|
| 370 |
+
assert len(C) == len(nbunch)
|
| 371 |
+
|
| 372 |
+
def test_all_pairs_connectivity_icosahedral(self):
|
| 373 |
+
G = nx.icosahedral_graph()
|
| 374 |
+
C = nx.all_pairs_node_connectivity(G)
|
| 375 |
+
assert all(5 == C[u][v] for u, v in itertools.combinations(G, 2))
|
| 376 |
+
|
| 377 |
+
def test_all_pairs_connectivity(self):
|
| 378 |
+
G = nx.Graph()
|
| 379 |
+
nodes = [0, 1, 2, 3]
|
| 380 |
+
nx.add_path(G, nodes)
|
| 381 |
+
A = {n: {} for n in G}
|
| 382 |
+
for u, v in itertools.combinations(nodes, 2):
|
| 383 |
+
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
| 384 |
+
C = nx.all_pairs_node_connectivity(G)
|
| 385 |
+
assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
|
| 386 |
+
(k, sorted(v)) for k, v in C.items()
|
| 387 |
+
)
|
| 388 |
+
|
| 389 |
+
def test_all_pairs_connectivity_directed(self):
|
| 390 |
+
G = nx.DiGraph()
|
| 391 |
+
nodes = [0, 1, 2, 3]
|
| 392 |
+
nx.add_path(G, nodes)
|
| 393 |
+
A = {n: {} for n in G}
|
| 394 |
+
for u, v in itertools.permutations(nodes, 2):
|
| 395 |
+
A[u][v] = nx.node_connectivity(G, u, v)
|
| 396 |
+
C = nx.all_pairs_node_connectivity(G)
|
| 397 |
+
assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
|
| 398 |
+
(k, sorted(v)) for k, v in C.items()
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
def test_all_pairs_connectivity_nbunch_combinations(self):
|
| 402 |
+
G = nx.complete_graph(5)
|
| 403 |
+
nbunch = [0, 2, 3]
|
| 404 |
+
A = {n: {} for n in nbunch}
|
| 405 |
+
for u, v in itertools.combinations(nbunch, 2):
|
| 406 |
+
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
| 407 |
+
C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
|
| 408 |
+
assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
|
| 409 |
+
(k, sorted(v)) for k, v in C.items()
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
def test_all_pairs_connectivity_nbunch_iter(self):
|
| 413 |
+
G = nx.complete_graph(5)
|
| 414 |
+
nbunch = [0, 2, 3]
|
| 415 |
+
A = {n: {} for n in nbunch}
|
| 416 |
+
for u, v in itertools.combinations(nbunch, 2):
|
| 417 |
+
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
| 418 |
+
C = nx.all_pairs_node_connectivity(G, nbunch=iter(nbunch))
|
| 419 |
+
assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
|
| 420 |
+
(k, sorted(v)) for k, v in C.items()
|
| 421 |
+
)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_cuts.py
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
import networkx as nx
|
| 4 |
+
from networkx.algorithms import flow
|
| 5 |
+
from networkx.algorithms.connectivity import minimum_st_edge_cut, minimum_st_node_cut
|
| 6 |
+
from networkx.utils import arbitrary_element
|
| 7 |
+
|
| 8 |
+
flow_funcs = [
|
| 9 |
+
flow.boykov_kolmogorov,
|
| 10 |
+
flow.dinitz,
|
| 11 |
+
flow.edmonds_karp,
|
| 12 |
+
flow.preflow_push,
|
| 13 |
+
flow.shortest_augmenting_path,
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
# Tests for node and edge cutsets
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _generate_no_biconnected(max_attempts=50):
|
| 20 |
+
attempts = 0
|
| 21 |
+
while True:
|
| 22 |
+
G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
|
| 23 |
+
if nx.is_connected(G) and not nx.is_biconnected(G):
|
| 24 |
+
attempts = 0
|
| 25 |
+
yield G
|
| 26 |
+
else:
|
| 27 |
+
if attempts >= max_attempts:
|
| 28 |
+
msg = f"Tried {attempts} times: no suitable Graph."
|
| 29 |
+
raise Exception(msg)
|
| 30 |
+
else:
|
| 31 |
+
attempts += 1
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_articulation_points():
|
| 35 |
+
Ggen = _generate_no_biconnected()
|
| 36 |
+
for flow_func in flow_funcs:
|
| 37 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 38 |
+
for i in range(1): # change 1 to 3 or more for more realizations.
|
| 39 |
+
G = next(Ggen)
|
| 40 |
+
cut = nx.minimum_node_cut(G, flow_func=flow_func)
|
| 41 |
+
assert len(cut) == 1, errmsg
|
| 42 |
+
assert cut.pop() in set(nx.articulation_points(G)), errmsg
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_brandes_erlebach_book():
|
| 46 |
+
# Figure 1 chapter 7: Connectivity
|
| 47 |
+
# http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
|
| 48 |
+
G = nx.Graph()
|
| 49 |
+
G.add_edges_from(
|
| 50 |
+
[
|
| 51 |
+
(1, 2),
|
| 52 |
+
(1, 3),
|
| 53 |
+
(1, 4),
|
| 54 |
+
(1, 5),
|
| 55 |
+
(2, 3),
|
| 56 |
+
(2, 6),
|
| 57 |
+
(3, 4),
|
| 58 |
+
(3, 6),
|
| 59 |
+
(4, 6),
|
| 60 |
+
(4, 7),
|
| 61 |
+
(5, 7),
|
| 62 |
+
(6, 8),
|
| 63 |
+
(6, 9),
|
| 64 |
+
(7, 8),
|
| 65 |
+
(7, 10),
|
| 66 |
+
(8, 11),
|
| 67 |
+
(9, 10),
|
| 68 |
+
(9, 11),
|
| 69 |
+
(10, 11),
|
| 70 |
+
]
|
| 71 |
+
)
|
| 72 |
+
for flow_func in flow_funcs:
|
| 73 |
+
kwargs = {"flow_func": flow_func}
|
| 74 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 75 |
+
# edge cutsets
|
| 76 |
+
assert 3 == len(nx.minimum_edge_cut(G, 1, 11, **kwargs)), errmsg
|
| 77 |
+
edge_cut = nx.minimum_edge_cut(G, **kwargs)
|
| 78 |
+
# Node 5 has only two edges
|
| 79 |
+
assert 2 == len(edge_cut), errmsg
|
| 80 |
+
H = G.copy()
|
| 81 |
+
H.remove_edges_from(edge_cut)
|
| 82 |
+
assert not nx.is_connected(H), errmsg
|
| 83 |
+
# node cuts
|
| 84 |
+
assert {6, 7} == minimum_st_node_cut(G, 1, 11, **kwargs), errmsg
|
| 85 |
+
assert {6, 7} == nx.minimum_node_cut(G, 1, 11, **kwargs), errmsg
|
| 86 |
+
node_cut = nx.minimum_node_cut(G, **kwargs)
|
| 87 |
+
assert 2 == len(node_cut), errmsg
|
| 88 |
+
H = G.copy()
|
| 89 |
+
H.remove_nodes_from(node_cut)
|
| 90 |
+
assert not nx.is_connected(H), errmsg
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def test_white_harary_paper():
|
| 94 |
+
# Figure 1b white and harary (2001)
|
| 95 |
+
# https://doi.org/10.1111/0081-1750.00098
|
| 96 |
+
# A graph with high adhesion (edge connectivity) and low cohesion
|
| 97 |
+
# (node connectivity)
|
| 98 |
+
G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
|
| 99 |
+
G.remove_node(7)
|
| 100 |
+
for i in range(4, 7):
|
| 101 |
+
G.add_edge(0, i)
|
| 102 |
+
G = nx.disjoint_union(G, nx.complete_graph(4))
|
| 103 |
+
G.remove_node(G.order() - 1)
|
| 104 |
+
for i in range(7, 10):
|
| 105 |
+
G.add_edge(0, i)
|
| 106 |
+
for flow_func in flow_funcs:
|
| 107 |
+
kwargs = {"flow_func": flow_func}
|
| 108 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 109 |
+
# edge cuts
|
| 110 |
+
edge_cut = nx.minimum_edge_cut(G, **kwargs)
|
| 111 |
+
assert 3 == len(edge_cut), errmsg
|
| 112 |
+
H = G.copy()
|
| 113 |
+
H.remove_edges_from(edge_cut)
|
| 114 |
+
assert not nx.is_connected(H), errmsg
|
| 115 |
+
# node cuts
|
| 116 |
+
node_cut = nx.minimum_node_cut(G, **kwargs)
|
| 117 |
+
assert {0} == node_cut, errmsg
|
| 118 |
+
H = G.copy()
|
| 119 |
+
H.remove_nodes_from(node_cut)
|
| 120 |
+
assert not nx.is_connected(H), errmsg
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def test_petersen_cutset():
|
| 124 |
+
G = nx.petersen_graph()
|
| 125 |
+
for flow_func in flow_funcs:
|
| 126 |
+
kwargs = {"flow_func": flow_func}
|
| 127 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 128 |
+
# edge cuts
|
| 129 |
+
edge_cut = nx.minimum_edge_cut(G, **kwargs)
|
| 130 |
+
assert 3 == len(edge_cut), errmsg
|
| 131 |
+
H = G.copy()
|
| 132 |
+
H.remove_edges_from(edge_cut)
|
| 133 |
+
assert not nx.is_connected(H), errmsg
|
| 134 |
+
# node cuts
|
| 135 |
+
node_cut = nx.minimum_node_cut(G, **kwargs)
|
| 136 |
+
assert 3 == len(node_cut), errmsg
|
| 137 |
+
H = G.copy()
|
| 138 |
+
H.remove_nodes_from(node_cut)
|
| 139 |
+
assert not nx.is_connected(H), errmsg
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_octahedral_cutset():
|
| 143 |
+
G = nx.octahedral_graph()
|
| 144 |
+
for flow_func in flow_funcs:
|
| 145 |
+
kwargs = {"flow_func": flow_func}
|
| 146 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 147 |
+
# edge cuts
|
| 148 |
+
edge_cut = nx.minimum_edge_cut(G, **kwargs)
|
| 149 |
+
assert 4 == len(edge_cut), errmsg
|
| 150 |
+
H = G.copy()
|
| 151 |
+
H.remove_edges_from(edge_cut)
|
| 152 |
+
assert not nx.is_connected(H), errmsg
|
| 153 |
+
# node cuts
|
| 154 |
+
node_cut = nx.minimum_node_cut(G, **kwargs)
|
| 155 |
+
assert 4 == len(node_cut), errmsg
|
| 156 |
+
H = G.copy()
|
| 157 |
+
H.remove_nodes_from(node_cut)
|
| 158 |
+
assert not nx.is_connected(H), errmsg
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def test_icosahedral_cutset():
|
| 162 |
+
G = nx.icosahedral_graph()
|
| 163 |
+
for flow_func in flow_funcs:
|
| 164 |
+
kwargs = {"flow_func": flow_func}
|
| 165 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 166 |
+
# edge cuts
|
| 167 |
+
edge_cut = nx.minimum_edge_cut(G, **kwargs)
|
| 168 |
+
assert 5 == len(edge_cut), errmsg
|
| 169 |
+
H = G.copy()
|
| 170 |
+
H.remove_edges_from(edge_cut)
|
| 171 |
+
assert not nx.is_connected(H), errmsg
|
| 172 |
+
# node cuts
|
| 173 |
+
node_cut = nx.minimum_node_cut(G, **kwargs)
|
| 174 |
+
assert 5 == len(node_cut), errmsg
|
| 175 |
+
H = G.copy()
|
| 176 |
+
H.remove_nodes_from(node_cut)
|
| 177 |
+
assert not nx.is_connected(H), errmsg
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def test_node_cutset_exception():
|
| 181 |
+
G = nx.Graph()
|
| 182 |
+
G.add_edges_from([(1, 2), (3, 4)])
|
| 183 |
+
for flow_func in flow_funcs:
|
| 184 |
+
pytest.raises(nx.NetworkXError, nx.minimum_node_cut, G, flow_func=flow_func)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def test_node_cutset_random_graphs():
|
| 188 |
+
for flow_func in flow_funcs:
|
| 189 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 190 |
+
for i in range(3):
|
| 191 |
+
G = nx.fast_gnp_random_graph(50, 0.25, seed=42)
|
| 192 |
+
if not nx.is_connected(G):
|
| 193 |
+
ccs = iter(nx.connected_components(G))
|
| 194 |
+
start = arbitrary_element(next(ccs))
|
| 195 |
+
G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
|
| 196 |
+
cutset = nx.minimum_node_cut(G, flow_func=flow_func)
|
| 197 |
+
assert nx.node_connectivity(G) == len(cutset), errmsg
|
| 198 |
+
G.remove_nodes_from(cutset)
|
| 199 |
+
assert not nx.is_connected(G), errmsg
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def test_edge_cutset_random_graphs():
|
| 203 |
+
for flow_func in flow_funcs:
|
| 204 |
+
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
| 205 |
+
for i in range(3):
|
| 206 |
+
G = nx.fast_gnp_random_graph(50, 0.25, seed=42)
|
| 207 |
+
if not nx.is_connected(G):
|
| 208 |
+
ccs = iter(nx.connected_components(G))
|
| 209 |
+
start = arbitrary_element(next(ccs))
|
| 210 |
+
G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
|
| 211 |
+
cutset = nx.minimum_edge_cut(G, flow_func=flow_func)
|
| 212 |
+
assert nx.edge_connectivity(G) == len(cutset), errmsg
|
| 213 |
+
G.remove_edges_from(cutset)
|
| 214 |
+
assert not nx.is_connected(G), errmsg
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def test_empty_graphs():
|
| 218 |
+
G = nx.Graph()
|
| 219 |
+
D = nx.DiGraph()
|
| 220 |
+
for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
|
| 221 |
+
for flow_func in flow_funcs:
|
| 222 |
+
pytest.raises(
|
| 223 |
+
nx.NetworkXPointlessConcept, interface_func, G, flow_func=flow_func
|
| 224 |
+
)
|
| 225 |
+
pytest.raises(
|
| 226 |
+
nx.NetworkXPointlessConcept, interface_func, D, flow_func=flow_func
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def test_unbounded():
|
| 231 |
+
G = nx.complete_graph(5)
|
| 232 |
+
for flow_func in flow_funcs:
|
| 233 |
+
assert 4 == len(minimum_st_edge_cut(G, 1, 4, flow_func=flow_func))
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def test_missing_source():
|
| 237 |
+
G = nx.path_graph(4)
|
| 238 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 239 |
+
for flow_func in flow_funcs:
|
| 240 |
+
pytest.raises(
|
| 241 |
+
nx.NetworkXError, interface_func, G, 10, 1, flow_func=flow_func
|
| 242 |
+
)
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def test_missing_target():
|
| 246 |
+
G = nx.path_graph(4)
|
| 247 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 248 |
+
for flow_func in flow_funcs:
|
| 249 |
+
pytest.raises(
|
| 250 |
+
nx.NetworkXError, interface_func, G, 1, 10, flow_func=flow_func
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def test_not_weakly_connected():
|
| 255 |
+
G = nx.DiGraph()
|
| 256 |
+
nx.add_path(G, [1, 2, 3])
|
| 257 |
+
nx.add_path(G, [4, 5])
|
| 258 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 259 |
+
for flow_func in flow_funcs:
|
| 260 |
+
pytest.raises(nx.NetworkXError, interface_func, G, flow_func=flow_func)
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_not_connected():
|
| 264 |
+
G = nx.Graph()
|
| 265 |
+
nx.add_path(G, [1, 2, 3])
|
| 266 |
+
nx.add_path(G, [4, 5])
|
| 267 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 268 |
+
for flow_func in flow_funcs:
|
| 269 |
+
pytest.raises(nx.NetworkXError, interface_func, G, flow_func=flow_func)
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def tests_min_cut_complete():
|
| 273 |
+
G = nx.complete_graph(5)
|
| 274 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 275 |
+
for flow_func in flow_funcs:
|
| 276 |
+
assert 4 == len(interface_func(G, flow_func=flow_func))
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def tests_min_cut_complete_directed():
|
| 280 |
+
G = nx.complete_graph(5)
|
| 281 |
+
G = G.to_directed()
|
| 282 |
+
for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
|
| 283 |
+
for flow_func in flow_funcs:
|
| 284 |
+
assert 4 == len(interface_func(G, flow_func=flow_func))
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def tests_minimum_st_node_cut():
|
| 288 |
+
G = nx.Graph()
|
| 289 |
+
G.add_nodes_from([0, 1, 2, 3, 7, 8, 11, 12])
|
| 290 |
+
G.add_edges_from([(7, 11), (1, 11), (1, 12), (12, 8), (0, 1)])
|
| 291 |
+
nodelist = minimum_st_node_cut(G, 7, 11)
|
| 292 |
+
assert nodelist == {}
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def test_invalid_auxiliary():
|
| 296 |
+
G = nx.complete_graph(5)
|
| 297 |
+
pytest.raises(nx.NetworkXError, minimum_st_node_cut, G, 0, 3, auxiliary=G)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def test_interface_only_source():
|
| 301 |
+
G = nx.complete_graph(5)
|
| 302 |
+
for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
|
| 303 |
+
pytest.raises(nx.NetworkXError, interface_func, G, s=0)
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def test_interface_only_target():
|
| 307 |
+
G = nx.complete_graph(5)
|
| 308 |
+
for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
|
| 309 |
+
pytest.raises(nx.NetworkXError, interface_func, G, t=3)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_edge_augmentation.py
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools as it
|
| 2 |
+
import random
|
| 3 |
+
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
import networkx as nx
|
| 7 |
+
from networkx.algorithms.connectivity import k_edge_augmentation
|
| 8 |
+
from networkx.algorithms.connectivity.edge_augmentation import (
|
| 9 |
+
_unpack_available_edges,
|
| 10 |
+
collapse,
|
| 11 |
+
complement_edges,
|
| 12 |
+
is_k_edge_connected,
|
| 13 |
+
is_locally_k_edge_connected,
|
| 14 |
+
)
|
| 15 |
+
from networkx.utils import pairwise
|
| 16 |
+
|
| 17 |
+
# This should be set to the largest k for which an efficient algorithm is
|
| 18 |
+
# explicitly defined.
|
| 19 |
+
MAX_EFFICIENT_K = 2
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def tarjan_bridge_graph():
|
| 23 |
+
# graph from tarjan paper
|
| 24 |
+
# RE Tarjan - "A note on finding the bridges of a graph"
|
| 25 |
+
# Information Processing Letters, 1974 - Elsevier
|
| 26 |
+
# doi:10.1016/0020-0190(74)90003-9.
|
| 27 |
+
# define 2-connected components and bridges
|
| 28 |
+
ccs = [
|
| 29 |
+
(1, 2, 4, 3, 1, 4),
|
| 30 |
+
(5, 6, 7, 5),
|
| 31 |
+
(8, 9, 10, 8),
|
| 32 |
+
(17, 18, 16, 15, 17),
|
| 33 |
+
(11, 12, 14, 13, 11, 14),
|
| 34 |
+
]
|
| 35 |
+
bridges = [(4, 8), (3, 5), (3, 17)]
|
| 36 |
+
G = nx.Graph(it.chain(*(pairwise(path) for path in ccs + bridges)))
|
| 37 |
+
return G
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def test_weight_key():
|
| 41 |
+
G = nx.Graph()
|
| 42 |
+
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
| 43 |
+
G.add_edges_from([(3, 8), (1, 2), (2, 3)])
|
| 44 |
+
impossible = {(3, 6), (3, 9)}
|
| 45 |
+
rng = random.Random(0)
|
| 46 |
+
avail_uv = list(set(complement_edges(G)) - impossible)
|
| 47 |
+
avail = [(u, v, {"cost": rng.random()}) for u, v in avail_uv]
|
| 48 |
+
|
| 49 |
+
_augment_and_check(G, k=1)
|
| 50 |
+
_augment_and_check(G, k=1, avail=avail_uv)
|
| 51 |
+
_augment_and_check(G, k=1, avail=avail, weight="cost")
|
| 52 |
+
|
| 53 |
+
_check_augmentations(G, avail, weight="cost")
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def test_is_locally_k_edge_connected_exceptions():
|
| 57 |
+
pytest.raises(nx.NetworkXNotImplemented, is_k_edge_connected, nx.DiGraph(), k=0)
|
| 58 |
+
pytest.raises(nx.NetworkXNotImplemented, is_k_edge_connected, nx.MultiGraph(), k=0)
|
| 59 |
+
pytest.raises(ValueError, is_k_edge_connected, nx.Graph(), k=0)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_is_k_edge_connected():
|
| 63 |
+
G = nx.barbell_graph(10, 0)
|
| 64 |
+
assert is_k_edge_connected(G, k=1)
|
| 65 |
+
assert not is_k_edge_connected(G, k=2)
|
| 66 |
+
|
| 67 |
+
G = nx.Graph()
|
| 68 |
+
G.add_nodes_from([5, 15])
|
| 69 |
+
assert not is_k_edge_connected(G, k=1)
|
| 70 |
+
assert not is_k_edge_connected(G, k=2)
|
| 71 |
+
|
| 72 |
+
G = nx.complete_graph(5)
|
| 73 |
+
assert is_k_edge_connected(G, k=1)
|
| 74 |
+
assert is_k_edge_connected(G, k=2)
|
| 75 |
+
assert is_k_edge_connected(G, k=3)
|
| 76 |
+
assert is_k_edge_connected(G, k=4)
|
| 77 |
+
|
| 78 |
+
G = nx.compose(nx.complete_graph([0, 1, 2]), nx.complete_graph([3, 4, 5]))
|
| 79 |
+
assert not is_k_edge_connected(G, k=1)
|
| 80 |
+
assert not is_k_edge_connected(G, k=2)
|
| 81 |
+
assert not is_k_edge_connected(G, k=3)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def test_is_k_edge_connected_exceptions():
|
| 85 |
+
pytest.raises(
|
| 86 |
+
nx.NetworkXNotImplemented, is_locally_k_edge_connected, nx.DiGraph(), 1, 2, k=0
|
| 87 |
+
)
|
| 88 |
+
pytest.raises(
|
| 89 |
+
nx.NetworkXNotImplemented,
|
| 90 |
+
is_locally_k_edge_connected,
|
| 91 |
+
nx.MultiGraph(),
|
| 92 |
+
1,
|
| 93 |
+
2,
|
| 94 |
+
k=0,
|
| 95 |
+
)
|
| 96 |
+
pytest.raises(ValueError, is_locally_k_edge_connected, nx.Graph(), 1, 2, k=0)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def test_is_locally_k_edge_connected():
|
| 100 |
+
G = nx.barbell_graph(10, 0)
|
| 101 |
+
assert is_locally_k_edge_connected(G, 5, 15, k=1)
|
| 102 |
+
assert not is_locally_k_edge_connected(G, 5, 15, k=2)
|
| 103 |
+
|
| 104 |
+
G = nx.Graph()
|
| 105 |
+
G.add_nodes_from([5, 15])
|
| 106 |
+
assert not is_locally_k_edge_connected(G, 5, 15, k=2)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def test_null_graph():
|
| 110 |
+
G = nx.Graph()
|
| 111 |
+
_check_augmentations(G, max_k=MAX_EFFICIENT_K + 2)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def test_cliques():
|
| 115 |
+
for n in range(1, 10):
|
| 116 |
+
G = nx.complete_graph(n)
|
| 117 |
+
_check_augmentations(G, max_k=MAX_EFFICIENT_K + 2)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def test_clique_and_node():
|
| 121 |
+
for n in range(1, 10):
|
| 122 |
+
G = nx.complete_graph(n)
|
| 123 |
+
G.add_node(n + 1)
|
| 124 |
+
_check_augmentations(G, max_k=MAX_EFFICIENT_K + 2)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def test_point_graph():
|
| 128 |
+
G = nx.Graph()
|
| 129 |
+
G.add_node(1)
|
| 130 |
+
_check_augmentations(G, max_k=MAX_EFFICIENT_K + 2)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def test_edgeless_graph():
|
| 134 |
+
G = nx.Graph()
|
| 135 |
+
G.add_nodes_from([1, 2, 3, 4])
|
| 136 |
+
_check_augmentations(G)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_invalid_k():
|
| 140 |
+
G = nx.Graph()
|
| 141 |
+
pytest.raises(ValueError, list, k_edge_augmentation(G, k=-1))
|
| 142 |
+
pytest.raises(ValueError, list, k_edge_augmentation(G, k=0))
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_unfeasible():
|
| 146 |
+
G = tarjan_bridge_graph()
|
| 147 |
+
pytest.raises(nx.NetworkXUnfeasible, list, k_edge_augmentation(G, k=1, avail=[]))
|
| 148 |
+
|
| 149 |
+
pytest.raises(nx.NetworkXUnfeasible, list, k_edge_augmentation(G, k=2, avail=[]))
|
| 150 |
+
|
| 151 |
+
pytest.raises(
|
| 152 |
+
nx.NetworkXUnfeasible, list, k_edge_augmentation(G, k=2, avail=[(7, 9)])
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# partial solutions should not error if real solutions are infeasible
|
| 156 |
+
aug_edges = list(k_edge_augmentation(G, k=2, avail=[(7, 9)], partial=True))
|
| 157 |
+
assert aug_edges == [(7, 9)]
|
| 158 |
+
|
| 159 |
+
_check_augmentations(G, avail=[], max_k=MAX_EFFICIENT_K + 2)
|
| 160 |
+
|
| 161 |
+
_check_augmentations(G, avail=[(7, 9)], max_k=MAX_EFFICIENT_K + 2)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def test_tarjan():
|
| 165 |
+
G = tarjan_bridge_graph()
|
| 166 |
+
|
| 167 |
+
aug_edges = set(_augment_and_check(G, k=2)[0])
|
| 168 |
+
print(f"aug_edges = {aug_edges!r}")
|
| 169 |
+
# can't assert edge exactly equality due to non-determinant edge order
|
| 170 |
+
# but we do know the size of the solution must be 3
|
| 171 |
+
assert len(aug_edges) == 3
|
| 172 |
+
|
| 173 |
+
avail = [
|
| 174 |
+
(9, 7),
|
| 175 |
+
(8, 5),
|
| 176 |
+
(2, 10),
|
| 177 |
+
(6, 13),
|
| 178 |
+
(11, 18),
|
| 179 |
+
(1, 17),
|
| 180 |
+
(2, 3),
|
| 181 |
+
(16, 17),
|
| 182 |
+
(18, 14),
|
| 183 |
+
(15, 14),
|
| 184 |
+
]
|
| 185 |
+
aug_edges = set(_augment_and_check(G, avail=avail, k=2)[0])
|
| 186 |
+
|
| 187 |
+
# Can't assert exact length since approximation depends on the order of a
|
| 188 |
+
# dict traversal.
|
| 189 |
+
assert len(aug_edges) <= 3 * 2
|
| 190 |
+
|
| 191 |
+
_check_augmentations(G, avail)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def test_configuration():
|
| 195 |
+
# seeds = [2718183590, 2470619828, 1694705158, 3001036531, 2401251497]
|
| 196 |
+
seeds = [1001, 1002, 1003, 1004]
|
| 197 |
+
for seed in seeds:
|
| 198 |
+
deg_seq = nx.random_powerlaw_tree_sequence(20, seed=seed, tries=5000)
|
| 199 |
+
G = nx.Graph(nx.configuration_model(deg_seq, seed=seed))
|
| 200 |
+
G.remove_edges_from(nx.selfloop_edges(G))
|
| 201 |
+
_check_augmentations(G)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def test_shell():
|
| 205 |
+
# seeds = [2057382236, 3331169846, 1840105863, 476020778, 2247498425]
|
| 206 |
+
seeds = [18]
|
| 207 |
+
for seed in seeds:
|
| 208 |
+
constructor = [(12, 70, 0.8), (15, 40, 0.6)]
|
| 209 |
+
G = nx.random_shell_graph(constructor, seed=seed)
|
| 210 |
+
_check_augmentations(G)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def test_karate():
|
| 214 |
+
G = nx.karate_club_graph()
|
| 215 |
+
_check_augmentations(G)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def test_star():
|
| 219 |
+
G = nx.star_graph(3)
|
| 220 |
+
_check_augmentations(G)
|
| 221 |
+
|
| 222 |
+
G = nx.star_graph(5)
|
| 223 |
+
_check_augmentations(G)
|
| 224 |
+
|
| 225 |
+
G = nx.star_graph(10)
|
| 226 |
+
_check_augmentations(G)
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def test_barbell():
|
| 230 |
+
G = nx.barbell_graph(5, 0)
|
| 231 |
+
_check_augmentations(G)
|
| 232 |
+
|
| 233 |
+
G = nx.barbell_graph(5, 2)
|
| 234 |
+
_check_augmentations(G)
|
| 235 |
+
|
| 236 |
+
G = nx.barbell_graph(5, 3)
|
| 237 |
+
_check_augmentations(G)
|
| 238 |
+
|
| 239 |
+
G = nx.barbell_graph(5, 4)
|
| 240 |
+
_check_augmentations(G)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def test_bridge():
|
| 244 |
+
G = nx.Graph([(2393, 2257), (2393, 2685), (2685, 2257), (1758, 2257)])
|
| 245 |
+
_check_augmentations(G)
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def test_gnp_augmentation():
|
| 249 |
+
rng = random.Random(0)
|
| 250 |
+
G = nx.gnp_random_graph(30, 0.005, seed=0)
|
| 251 |
+
# Randomly make edges available
|
| 252 |
+
avail = {
|
| 253 |
+
(u, v): 1 + rng.random() for u, v in complement_edges(G) if rng.random() < 0.25
|
| 254 |
+
}
|
| 255 |
+
_check_augmentations(G, avail)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def _assert_solution_properties(G, aug_edges, avail_dict=None):
|
| 259 |
+
"""Checks that aug_edges are consistently formatted"""
|
| 260 |
+
if avail_dict is not None:
|
| 261 |
+
assert all(
|
| 262 |
+
e in avail_dict for e in aug_edges
|
| 263 |
+
), "when avail is specified aug-edges should be in avail"
|
| 264 |
+
|
| 265 |
+
unique_aug = set(map(tuple, map(sorted, aug_edges)))
|
| 266 |
+
unique_aug = list(map(tuple, map(sorted, aug_edges)))
|
| 267 |
+
assert len(aug_edges) == len(unique_aug), "edges should be unique"
|
| 268 |
+
|
| 269 |
+
assert not any(u == v for u, v in unique_aug), "should be no self-edges"
|
| 270 |
+
|
| 271 |
+
assert not any(
|
| 272 |
+
G.has_edge(u, v) for u, v in unique_aug
|
| 273 |
+
), "aug edges and G.edges should be disjoint"
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def _augment_and_check(
|
| 277 |
+
G, k, avail=None, weight=None, verbose=False, orig_k=None, max_aug_k=None
|
| 278 |
+
):
|
| 279 |
+
"""
|
| 280 |
+
Does one specific augmentation and checks for properties of the result
|
| 281 |
+
"""
|
| 282 |
+
if orig_k is None:
|
| 283 |
+
try:
|
| 284 |
+
orig_k = nx.edge_connectivity(G)
|
| 285 |
+
except nx.NetworkXPointlessConcept:
|
| 286 |
+
orig_k = 0
|
| 287 |
+
info = {}
|
| 288 |
+
try:
|
| 289 |
+
if avail is not None:
|
| 290 |
+
# ensure avail is in dict form
|
| 291 |
+
avail_dict = dict(zip(*_unpack_available_edges(avail, weight=weight)))
|
| 292 |
+
else:
|
| 293 |
+
avail_dict = None
|
| 294 |
+
try:
|
| 295 |
+
# Find the augmentation if possible
|
| 296 |
+
generator = nx.k_edge_augmentation(G, k=k, weight=weight, avail=avail)
|
| 297 |
+
assert not isinstance(generator, list), "should always return an iter"
|
| 298 |
+
aug_edges = []
|
| 299 |
+
for edge in generator:
|
| 300 |
+
aug_edges.append(edge)
|
| 301 |
+
except nx.NetworkXUnfeasible:
|
| 302 |
+
infeasible = True
|
| 303 |
+
info["infeasible"] = True
|
| 304 |
+
assert len(aug_edges) == 0, "should not generate anything if unfeasible"
|
| 305 |
+
|
| 306 |
+
if avail is None:
|
| 307 |
+
n_nodes = G.number_of_nodes()
|
| 308 |
+
assert n_nodes <= k, (
|
| 309 |
+
"unconstrained cases are only unfeasible if |V| <= k. "
|
| 310 |
+
f"Got |V|={n_nodes} and k={k}"
|
| 311 |
+
)
|
| 312 |
+
else:
|
| 313 |
+
if max_aug_k is None:
|
| 314 |
+
G_aug_all = G.copy()
|
| 315 |
+
G_aug_all.add_edges_from(avail_dict.keys())
|
| 316 |
+
try:
|
| 317 |
+
max_aug_k = nx.edge_connectivity(G_aug_all)
|
| 318 |
+
except nx.NetworkXPointlessConcept:
|
| 319 |
+
max_aug_k = 0
|
| 320 |
+
|
| 321 |
+
assert max_aug_k < k, (
|
| 322 |
+
"avail should only be unfeasible if using all edges "
|
| 323 |
+
"does not achieve k-edge-connectivity"
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
# Test for a partial solution
|
| 327 |
+
partial_edges = list(
|
| 328 |
+
nx.k_edge_augmentation(G, k=k, weight=weight, partial=True, avail=avail)
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
info["n_partial_edges"] = len(partial_edges)
|
| 332 |
+
|
| 333 |
+
if avail_dict is None:
|
| 334 |
+
assert set(partial_edges) == set(
|
| 335 |
+
complement_edges(G)
|
| 336 |
+
), "unweighted partial solutions should be the complement"
|
| 337 |
+
elif len(avail_dict) > 0:
|
| 338 |
+
H = G.copy()
|
| 339 |
+
|
| 340 |
+
# Find the partial / full augmented connectivity
|
| 341 |
+
H.add_edges_from(partial_edges)
|
| 342 |
+
partial_conn = nx.edge_connectivity(H)
|
| 343 |
+
|
| 344 |
+
H.add_edges_from(set(avail_dict.keys()))
|
| 345 |
+
full_conn = nx.edge_connectivity(H)
|
| 346 |
+
|
| 347 |
+
# Full connectivity should be no better than our partial
|
| 348 |
+
# solution.
|
| 349 |
+
assert (
|
| 350 |
+
partial_conn == full_conn
|
| 351 |
+
), "adding more edges should not increase k-conn"
|
| 352 |
+
|
| 353 |
+
# Find the new edge-connectivity after adding the augmenting edges
|
| 354 |
+
aug_edges = partial_edges
|
| 355 |
+
else:
|
| 356 |
+
infeasible = False
|
| 357 |
+
|
| 358 |
+
# Find the weight of the augmentation
|
| 359 |
+
num_edges = len(aug_edges)
|
| 360 |
+
if avail is not None:
|
| 361 |
+
total_weight = sum(avail_dict[e] for e in aug_edges)
|
| 362 |
+
else:
|
| 363 |
+
total_weight = num_edges
|
| 364 |
+
|
| 365 |
+
info["total_weight"] = total_weight
|
| 366 |
+
info["num_edges"] = num_edges
|
| 367 |
+
|
| 368 |
+
# Find the new edge-connectivity after adding the augmenting edges
|
| 369 |
+
G_aug = G.copy()
|
| 370 |
+
G_aug.add_edges_from(aug_edges)
|
| 371 |
+
try:
|
| 372 |
+
aug_k = nx.edge_connectivity(G_aug)
|
| 373 |
+
except nx.NetworkXPointlessConcept:
|
| 374 |
+
aug_k = 0
|
| 375 |
+
info["aug_k"] = aug_k
|
| 376 |
+
|
| 377 |
+
# Do checks
|
| 378 |
+
if not infeasible and orig_k < k:
|
| 379 |
+
assert info["aug_k"] >= k, f"connectivity should increase to k={k} or more"
|
| 380 |
+
|
| 381 |
+
assert info["aug_k"] >= orig_k, "augmenting should never reduce connectivity"
|
| 382 |
+
|
| 383 |
+
_assert_solution_properties(G, aug_edges, avail_dict)
|
| 384 |
+
|
| 385 |
+
except Exception:
|
| 386 |
+
info["failed"] = True
|
| 387 |
+
print(f"edges = {list(G.edges())}")
|
| 388 |
+
print(f"nodes = {list(G.nodes())}")
|
| 389 |
+
print(f"aug_edges = {list(aug_edges)}")
|
| 390 |
+
print(f"info = {info}")
|
| 391 |
+
raise
|
| 392 |
+
else:
|
| 393 |
+
if verbose:
|
| 394 |
+
print(f"info = {info}")
|
| 395 |
+
|
| 396 |
+
if infeasible:
|
| 397 |
+
aug_edges = None
|
| 398 |
+
return aug_edges, info
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def _check_augmentations(G, avail=None, max_k=None, weight=None, verbose=False):
|
| 402 |
+
"""Helper to check weighted/unweighted cases with multiple values of k"""
|
| 403 |
+
# Using all available edges, find the maximum edge-connectivity
|
| 404 |
+
try:
|
| 405 |
+
orig_k = nx.edge_connectivity(G)
|
| 406 |
+
except nx.NetworkXPointlessConcept:
|
| 407 |
+
orig_k = 0
|
| 408 |
+
|
| 409 |
+
if avail is not None:
|
| 410 |
+
all_aug_edges = _unpack_available_edges(avail, weight=weight)[0]
|
| 411 |
+
G_aug_all = G.copy()
|
| 412 |
+
G_aug_all.add_edges_from(all_aug_edges)
|
| 413 |
+
try:
|
| 414 |
+
max_aug_k = nx.edge_connectivity(G_aug_all)
|
| 415 |
+
except nx.NetworkXPointlessConcept:
|
| 416 |
+
max_aug_k = 0
|
| 417 |
+
else:
|
| 418 |
+
max_aug_k = G.number_of_nodes() - 1
|
| 419 |
+
|
| 420 |
+
if max_k is None:
|
| 421 |
+
max_k = min(4, max_aug_k)
|
| 422 |
+
|
| 423 |
+
avail_uniform = {e: 1 for e in complement_edges(G)}
|
| 424 |
+
|
| 425 |
+
if verbose:
|
| 426 |
+
print("\n=== CHECK_AUGMENTATION ===")
|
| 427 |
+
print(f"G.number_of_nodes = {G.number_of_nodes()!r}")
|
| 428 |
+
print(f"G.number_of_edges = {G.number_of_edges()!r}")
|
| 429 |
+
print(f"max_k = {max_k!r}")
|
| 430 |
+
print(f"max_aug_k = {max_aug_k!r}")
|
| 431 |
+
print(f"orig_k = {orig_k!r}")
|
| 432 |
+
|
| 433 |
+
# check augmentation for multiple values of k
|
| 434 |
+
for k in range(1, max_k + 1):
|
| 435 |
+
if verbose:
|
| 436 |
+
print("---------------")
|
| 437 |
+
print(f"Checking k = {k}")
|
| 438 |
+
|
| 439 |
+
# Check the unweighted version
|
| 440 |
+
if verbose:
|
| 441 |
+
print("unweighted case")
|
| 442 |
+
aug_edges1, info1 = _augment_and_check(G, k=k, verbose=verbose, orig_k=orig_k)
|
| 443 |
+
|
| 444 |
+
# Check that the weighted version with all available edges and uniform
|
| 445 |
+
# weights gives a similar solution to the unweighted case.
|
| 446 |
+
if verbose:
|
| 447 |
+
print("weighted uniform case")
|
| 448 |
+
aug_edges2, info2 = _augment_and_check(
|
| 449 |
+
G,
|
| 450 |
+
k=k,
|
| 451 |
+
avail=avail_uniform,
|
| 452 |
+
verbose=verbose,
|
| 453 |
+
orig_k=orig_k,
|
| 454 |
+
max_aug_k=G.number_of_nodes() - 1,
|
| 455 |
+
)
|
| 456 |
+
|
| 457 |
+
# Check the weighted version
|
| 458 |
+
if avail is not None:
|
| 459 |
+
if verbose:
|
| 460 |
+
print("weighted case")
|
| 461 |
+
aug_edges3, info3 = _augment_and_check(
|
| 462 |
+
G,
|
| 463 |
+
k=k,
|
| 464 |
+
avail=avail,
|
| 465 |
+
weight=weight,
|
| 466 |
+
verbose=verbose,
|
| 467 |
+
max_aug_k=max_aug_k,
|
| 468 |
+
orig_k=orig_k,
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
if aug_edges1 is not None:
|
| 472 |
+
# Check approximation ratios
|
| 473 |
+
if k == 1:
|
| 474 |
+
# when k=1, both solutions should be optimal
|
| 475 |
+
assert info2["total_weight"] == info1["total_weight"]
|
| 476 |
+
if k == 2:
|
| 477 |
+
# when k=2, the weighted version is an approximation
|
| 478 |
+
if orig_k == 0:
|
| 479 |
+
# the approximation ratio is 3 if G is not connected
|
| 480 |
+
assert info2["total_weight"] <= info1["total_weight"] * 3
|
| 481 |
+
else:
|
| 482 |
+
# the approximation ratio is 2 if G is was connected
|
| 483 |
+
assert info2["total_weight"] <= info1["total_weight"] * 2
|
| 484 |
+
_check_unconstrained_bridge_property(G, info1)
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
def _check_unconstrained_bridge_property(G, info1):
|
| 488 |
+
# Check Theorem 5 from Eswaran and Tarjan. (1975) Augmentation problems
|
| 489 |
+
import math
|
| 490 |
+
|
| 491 |
+
bridge_ccs = list(nx.connectivity.bridge_components(G))
|
| 492 |
+
# condense G into an forest C
|
| 493 |
+
C = collapse(G, bridge_ccs)
|
| 494 |
+
|
| 495 |
+
p = len([n for n, d in C.degree() if d == 1]) # leafs
|
| 496 |
+
q = len([n for n, d in C.degree() if d == 0]) # isolated
|
| 497 |
+
if p + q > 1:
|
| 498 |
+
size_target = math.ceil(p / 2) + q
|
| 499 |
+
size_aug = info1["num_edges"]
|
| 500 |
+
assert (
|
| 501 |
+
size_aug == size_target
|
| 502 |
+
), "augmentation size is different from what theory predicts"
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools as it
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
|
| 5 |
+
import networkx as nx
|
| 6 |
+
from networkx.algorithms.connectivity import EdgeComponentAuxGraph, bridge_components
|
| 7 |
+
from networkx.algorithms.connectivity.edge_kcomponents import general_k_edge_subgraphs
|
| 8 |
+
from networkx.utils import pairwise
|
| 9 |
+
|
| 10 |
+
# ----------------
|
| 11 |
+
# Helper functions
|
| 12 |
+
# ----------------
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def fset(list_of_sets):
|
| 16 |
+
"""allows == to be used for list of sets"""
|
| 17 |
+
return set(map(frozenset, list_of_sets))
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _assert_subgraph_edge_connectivity(G, ccs_subgraph, k):
|
| 21 |
+
"""
|
| 22 |
+
tests properties of k-edge-connected subgraphs
|
| 23 |
+
|
| 24 |
+
the actual edge connectivity should be no less than k unless the cc is a
|
| 25 |
+
single node.
|
| 26 |
+
"""
|
| 27 |
+
for cc in ccs_subgraph:
|
| 28 |
+
C = G.subgraph(cc)
|
| 29 |
+
if len(cc) > 1:
|
| 30 |
+
connectivity = nx.edge_connectivity(C)
|
| 31 |
+
assert connectivity >= k
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _memo_connectivity(G, u, v, memo):
|
| 35 |
+
edge = (u, v)
|
| 36 |
+
if edge in memo:
|
| 37 |
+
return memo[edge]
|
| 38 |
+
if not G.is_directed():
|
| 39 |
+
redge = (v, u)
|
| 40 |
+
if redge in memo:
|
| 41 |
+
return memo[redge]
|
| 42 |
+
memo[edge] = nx.edge_connectivity(G, *edge)
|
| 43 |
+
return memo[edge]
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _all_pairs_connectivity(G, cc, k, memo):
|
| 47 |
+
# Brute force check
|
| 48 |
+
for u, v in it.combinations(cc, 2):
|
| 49 |
+
# Use a memoization dict to save on computation
|
| 50 |
+
connectivity = _memo_connectivity(G, u, v, memo)
|
| 51 |
+
if G.is_directed():
|
| 52 |
+
connectivity = min(connectivity, _memo_connectivity(G, v, u, memo))
|
| 53 |
+
assert connectivity >= k
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _assert_local_cc_edge_connectivity(G, ccs_local, k, memo):
|
| 57 |
+
"""
|
| 58 |
+
tests properties of k-edge-connected components
|
| 59 |
+
|
| 60 |
+
the local edge connectivity between each pair of nodes in the original
|
| 61 |
+
graph should be no less than k unless the cc is a single node.
|
| 62 |
+
"""
|
| 63 |
+
for cc in ccs_local:
|
| 64 |
+
if len(cc) > 1:
|
| 65 |
+
# Strategy for testing a bit faster: If the subgraph has high edge
|
| 66 |
+
# connectivity then it must have local connectivity
|
| 67 |
+
C = G.subgraph(cc)
|
| 68 |
+
connectivity = nx.edge_connectivity(C)
|
| 69 |
+
if connectivity < k:
|
| 70 |
+
# Otherwise do the brute force (with memoization) check
|
| 71 |
+
_all_pairs_connectivity(G, cc, k, memo)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# Helper function
|
| 75 |
+
def _check_edge_connectivity(G):
|
| 76 |
+
"""
|
| 77 |
+
Helper - generates all k-edge-components using the aux graph. Checks the
|
| 78 |
+
both local and subgraph edge connectivity of each cc. Also checks that
|
| 79 |
+
alternate methods of computing the k-edge-ccs generate the same result.
|
| 80 |
+
"""
|
| 81 |
+
# Construct the auxiliary graph that can be used to make each k-cc or k-sub
|
| 82 |
+
aux_graph = EdgeComponentAuxGraph.construct(G)
|
| 83 |
+
|
| 84 |
+
# memoize the local connectivity in this graph
|
| 85 |
+
memo = {}
|
| 86 |
+
|
| 87 |
+
for k in it.count(1):
|
| 88 |
+
# Test "local" k-edge-components and k-edge-subgraphs
|
| 89 |
+
ccs_local = fset(aux_graph.k_edge_components(k))
|
| 90 |
+
ccs_subgraph = fset(aux_graph.k_edge_subgraphs(k))
|
| 91 |
+
|
| 92 |
+
# Check connectivity properties that should be guaranteed by the
|
| 93 |
+
# algorithms.
|
| 94 |
+
_assert_local_cc_edge_connectivity(G, ccs_local, k, memo)
|
| 95 |
+
_assert_subgraph_edge_connectivity(G, ccs_subgraph, k)
|
| 96 |
+
|
| 97 |
+
if k == 1 or k == 2 and not G.is_directed():
|
| 98 |
+
assert (
|
| 99 |
+
ccs_local == ccs_subgraph
|
| 100 |
+
), "Subgraphs and components should be the same when k == 1 or (k == 2 and not G.directed())"
|
| 101 |
+
|
| 102 |
+
if G.is_directed():
|
| 103 |
+
# Test special case methods are the same as the aux graph
|
| 104 |
+
if k == 1:
|
| 105 |
+
alt_sccs = fset(nx.strongly_connected_components(G))
|
| 106 |
+
assert alt_sccs == ccs_local, "k=1 failed alt"
|
| 107 |
+
assert alt_sccs == ccs_subgraph, "k=1 failed alt"
|
| 108 |
+
else:
|
| 109 |
+
# Test special case methods are the same as the aux graph
|
| 110 |
+
if k == 1:
|
| 111 |
+
alt_ccs = fset(nx.connected_components(G))
|
| 112 |
+
assert alt_ccs == ccs_local, "k=1 failed alt"
|
| 113 |
+
assert alt_ccs == ccs_subgraph, "k=1 failed alt"
|
| 114 |
+
elif k == 2:
|
| 115 |
+
alt_bridge_ccs = fset(bridge_components(G))
|
| 116 |
+
assert alt_bridge_ccs == ccs_local, "k=2 failed alt"
|
| 117 |
+
assert alt_bridge_ccs == ccs_subgraph, "k=2 failed alt"
|
| 118 |
+
# if new methods for k == 3 or k == 4 are implemented add them here
|
| 119 |
+
|
| 120 |
+
# Check the general subgraph method works by itself
|
| 121 |
+
alt_subgraph_ccs = fset(
|
| 122 |
+
[set(C.nodes()) for C in general_k_edge_subgraphs(G, k=k)]
|
| 123 |
+
)
|
| 124 |
+
assert alt_subgraph_ccs == ccs_subgraph, "alt subgraph method failed"
|
| 125 |
+
|
| 126 |
+
# Stop once k is larger than all special case methods
|
| 127 |
+
# and we cannot break down ccs any further.
|
| 128 |
+
if k > 2 and all(len(cc) == 1 for cc in ccs_local):
|
| 129 |
+
break
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# ----------------
|
| 133 |
+
# Misc tests
|
| 134 |
+
# ----------------
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def test_zero_k_exception():
|
| 138 |
+
G = nx.Graph()
|
| 139 |
+
# functions that return generators error immediately
|
| 140 |
+
pytest.raises(ValueError, nx.k_edge_components, G, k=0)
|
| 141 |
+
pytest.raises(ValueError, nx.k_edge_subgraphs, G, k=0)
|
| 142 |
+
|
| 143 |
+
# actual generators only error when you get the first item
|
| 144 |
+
aux_graph = EdgeComponentAuxGraph.construct(G)
|
| 145 |
+
pytest.raises(ValueError, list, aux_graph.k_edge_components(k=0))
|
| 146 |
+
pytest.raises(ValueError, list, aux_graph.k_edge_subgraphs(k=0))
|
| 147 |
+
|
| 148 |
+
pytest.raises(ValueError, list, general_k_edge_subgraphs(G, k=0))
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def test_empty_input():
|
| 152 |
+
G = nx.Graph()
|
| 153 |
+
assert [] == list(nx.k_edge_components(G, k=5))
|
| 154 |
+
assert [] == list(nx.k_edge_subgraphs(G, k=5))
|
| 155 |
+
|
| 156 |
+
G = nx.DiGraph()
|
| 157 |
+
assert [] == list(nx.k_edge_components(G, k=5))
|
| 158 |
+
assert [] == list(nx.k_edge_subgraphs(G, k=5))
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def test_not_implemented():
|
| 162 |
+
G = nx.MultiGraph()
|
| 163 |
+
pytest.raises(nx.NetworkXNotImplemented, EdgeComponentAuxGraph.construct, G)
|
| 164 |
+
pytest.raises(nx.NetworkXNotImplemented, nx.k_edge_components, G, k=2)
|
| 165 |
+
pytest.raises(nx.NetworkXNotImplemented, nx.k_edge_subgraphs, G, k=2)
|
| 166 |
+
with pytest.raises(nx.NetworkXNotImplemented):
|
| 167 |
+
next(bridge_components(G))
|
| 168 |
+
with pytest.raises(nx.NetworkXNotImplemented):
|
| 169 |
+
next(bridge_components(nx.DiGraph()))
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def test_general_k_edge_subgraph_quick_return():
|
| 173 |
+
# tests quick return optimization
|
| 174 |
+
G = nx.Graph()
|
| 175 |
+
G.add_node(0)
|
| 176 |
+
subgraphs = list(general_k_edge_subgraphs(G, k=1))
|
| 177 |
+
assert len(subgraphs) == 1
|
| 178 |
+
for subgraph in subgraphs:
|
| 179 |
+
assert subgraph.number_of_nodes() == 1
|
| 180 |
+
|
| 181 |
+
G.add_node(1)
|
| 182 |
+
subgraphs = list(general_k_edge_subgraphs(G, k=1))
|
| 183 |
+
assert len(subgraphs) == 2
|
| 184 |
+
for subgraph in subgraphs:
|
| 185 |
+
assert subgraph.number_of_nodes() == 1
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
# ----------------
|
| 189 |
+
# Undirected tests
|
| 190 |
+
# ----------------
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def test_random_gnp():
|
| 194 |
+
# seeds = [1550709854, 1309423156, 4208992358, 2785630813, 1915069929]
|
| 195 |
+
seeds = [12, 13]
|
| 196 |
+
|
| 197 |
+
for seed in seeds:
|
| 198 |
+
G = nx.gnp_random_graph(20, 0.2, seed=seed)
|
| 199 |
+
_check_edge_connectivity(G)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def test_configuration():
|
| 203 |
+
# seeds = [2718183590, 2470619828, 1694705158, 3001036531, 2401251497]
|
| 204 |
+
seeds = [14, 15]
|
| 205 |
+
for seed in seeds:
|
| 206 |
+
deg_seq = nx.random_powerlaw_tree_sequence(20, seed=seed, tries=5000)
|
| 207 |
+
G = nx.Graph(nx.configuration_model(deg_seq, seed=seed))
|
| 208 |
+
G.remove_edges_from(nx.selfloop_edges(G))
|
| 209 |
+
_check_edge_connectivity(G)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def test_shell():
|
| 213 |
+
# seeds = [2057382236, 3331169846, 1840105863, 476020778, 2247498425]
|
| 214 |
+
seeds = [20]
|
| 215 |
+
for seed in seeds:
|
| 216 |
+
constructor = [(12, 70, 0.8), (15, 40, 0.6)]
|
| 217 |
+
G = nx.random_shell_graph(constructor, seed=seed)
|
| 218 |
+
_check_edge_connectivity(G)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
def test_karate():
|
| 222 |
+
G = nx.karate_club_graph()
|
| 223 |
+
_check_edge_connectivity(G)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def test_tarjan_bridge():
|
| 227 |
+
# graph from tarjan paper
|
| 228 |
+
# RE Tarjan - "A note on finding the bridges of a graph"
|
| 229 |
+
# Information Processing Letters, 1974 - Elsevier
|
| 230 |
+
# doi:10.1016/0020-0190(74)90003-9.
|
| 231 |
+
# define 2-connected components and bridges
|
| 232 |
+
ccs = [
|
| 233 |
+
(1, 2, 4, 3, 1, 4),
|
| 234 |
+
(5, 6, 7, 5),
|
| 235 |
+
(8, 9, 10, 8),
|
| 236 |
+
(17, 18, 16, 15, 17),
|
| 237 |
+
(11, 12, 14, 13, 11, 14),
|
| 238 |
+
]
|
| 239 |
+
bridges = [(4, 8), (3, 5), (3, 17)]
|
| 240 |
+
G = nx.Graph(it.chain(*(pairwise(path) for path in ccs + bridges)))
|
| 241 |
+
_check_edge_connectivity(G)
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def test_bridge_cc():
|
| 245 |
+
# define 2-connected components and bridges
|
| 246 |
+
cc2 = [(1, 2, 4, 3, 1, 4), (8, 9, 10, 8), (11, 12, 13, 11)]
|
| 247 |
+
bridges = [(4, 8), (3, 5), (20, 21), (22, 23, 24)]
|
| 248 |
+
G = nx.Graph(it.chain(*(pairwise(path) for path in cc2 + bridges)))
|
| 249 |
+
bridge_ccs = fset(bridge_components(G))
|
| 250 |
+
target_ccs = fset(
|
| 251 |
+
[{1, 2, 3, 4}, {5}, {8, 9, 10}, {11, 12, 13}, {20}, {21}, {22}, {23}, {24}]
|
| 252 |
+
)
|
| 253 |
+
assert bridge_ccs == target_ccs
|
| 254 |
+
_check_edge_connectivity(G)
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def test_undirected_aux_graph():
|
| 258 |
+
# Graph similar to the one in
|
| 259 |
+
# http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0136264
|
| 260 |
+
a, b, c, d, e, f, g, h, i = "abcdefghi"
|
| 261 |
+
paths = [
|
| 262 |
+
(a, d, b, f, c),
|
| 263 |
+
(a, e, b),
|
| 264 |
+
(a, e, b, c, g, b, a),
|
| 265 |
+
(c, b),
|
| 266 |
+
(f, g, f),
|
| 267 |
+
(h, i),
|
| 268 |
+
]
|
| 269 |
+
G = nx.Graph(it.chain(*[pairwise(path) for path in paths]))
|
| 270 |
+
aux_graph = EdgeComponentAuxGraph.construct(G)
|
| 271 |
+
|
| 272 |
+
components_1 = fset(aux_graph.k_edge_subgraphs(k=1))
|
| 273 |
+
target_1 = fset([{a, b, c, d, e, f, g}, {h, i}])
|
| 274 |
+
assert target_1 == components_1
|
| 275 |
+
|
| 276 |
+
# Check that the undirected case for k=1 agrees with CCs
|
| 277 |
+
alt_1 = fset(nx.k_edge_subgraphs(G, k=1))
|
| 278 |
+
assert alt_1 == components_1
|
| 279 |
+
|
| 280 |
+
components_2 = fset(aux_graph.k_edge_subgraphs(k=2))
|
| 281 |
+
target_2 = fset([{a, b, c, d, e, f, g}, {h}, {i}])
|
| 282 |
+
assert target_2 == components_2
|
| 283 |
+
|
| 284 |
+
# Check that the undirected case for k=2 agrees with bridge components
|
| 285 |
+
alt_2 = fset(nx.k_edge_subgraphs(G, k=2))
|
| 286 |
+
assert alt_2 == components_2
|
| 287 |
+
|
| 288 |
+
components_3 = fset(aux_graph.k_edge_subgraphs(k=3))
|
| 289 |
+
target_3 = fset([{a}, {b, c, f, g}, {d}, {e}, {h}, {i}])
|
| 290 |
+
assert target_3 == components_3
|
| 291 |
+
|
| 292 |
+
components_4 = fset(aux_graph.k_edge_subgraphs(k=4))
|
| 293 |
+
target_4 = fset([{a}, {b}, {c}, {d}, {e}, {f}, {g}, {h}, {i}])
|
| 294 |
+
assert target_4 == components_4
|
| 295 |
+
|
| 296 |
+
_check_edge_connectivity(G)
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def test_local_subgraph_difference():
|
| 300 |
+
paths = [
|
| 301 |
+
(11, 12, 13, 14, 11, 13, 14, 12), # first 4-clique
|
| 302 |
+
(21, 22, 23, 24, 21, 23, 24, 22), # second 4-clique
|
| 303 |
+
# paths connecting each node of the 4 cliques
|
| 304 |
+
(11, 101, 21),
|
| 305 |
+
(12, 102, 22),
|
| 306 |
+
(13, 103, 23),
|
| 307 |
+
(14, 104, 24),
|
| 308 |
+
]
|
| 309 |
+
G = nx.Graph(it.chain(*[pairwise(path) for path in paths]))
|
| 310 |
+
aux_graph = EdgeComponentAuxGraph.construct(G)
|
| 311 |
+
|
| 312 |
+
# Each clique is returned separately in k-edge-subgraphs
|
| 313 |
+
subgraph_ccs = fset(aux_graph.k_edge_subgraphs(3))
|
| 314 |
+
subgraph_target = fset(
|
| 315 |
+
[{101}, {102}, {103}, {104}, {21, 22, 23, 24}, {11, 12, 13, 14}]
|
| 316 |
+
)
|
| 317 |
+
assert subgraph_ccs == subgraph_target
|
| 318 |
+
|
| 319 |
+
# But in k-edge-ccs they are returned together
|
| 320 |
+
# because they are locally 3-edge-connected
|
| 321 |
+
local_ccs = fset(aux_graph.k_edge_components(3))
|
| 322 |
+
local_target = fset([{101}, {102}, {103}, {104}, {11, 12, 13, 14, 21, 22, 23, 24}])
|
| 323 |
+
assert local_ccs == local_target
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def test_local_subgraph_difference_directed():
|
| 327 |
+
dipaths = [(1, 2, 3, 4, 1), (1, 3, 1)]
|
| 328 |
+
G = nx.DiGraph(it.chain(*[pairwise(path) for path in dipaths]))
|
| 329 |
+
|
| 330 |
+
assert fset(nx.k_edge_components(G, k=1)) == fset(nx.k_edge_subgraphs(G, k=1))
|
| 331 |
+
|
| 332 |
+
# Unlike undirected graphs, when k=2, for directed graphs there is a case
|
| 333 |
+
# where the k-edge-ccs are not the same as the k-edge-subgraphs.
|
| 334 |
+
# (in directed graphs ccs and subgraphs are the same when k=2)
|
| 335 |
+
assert fset(nx.k_edge_components(G, k=2)) != fset(nx.k_edge_subgraphs(G, k=2))
|
| 336 |
+
|
| 337 |
+
assert fset(nx.k_edge_components(G, k=3)) == fset(nx.k_edge_subgraphs(G, k=3))
|
| 338 |
+
|
| 339 |
+
_check_edge_connectivity(G)
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def test_triangles():
|
| 343 |
+
paths = [
|
| 344 |
+
(11, 12, 13, 11), # first 3-clique
|
| 345 |
+
(21, 22, 23, 21), # second 3-clique
|
| 346 |
+
(11, 21), # connected by an edge
|
| 347 |
+
]
|
| 348 |
+
G = nx.Graph(it.chain(*[pairwise(path) for path in paths]))
|
| 349 |
+
|
| 350 |
+
# subgraph and ccs are the same in all cases here
|
| 351 |
+
assert fset(nx.k_edge_components(G, k=1)) == fset(nx.k_edge_subgraphs(G, k=1))
|
| 352 |
+
|
| 353 |
+
assert fset(nx.k_edge_components(G, k=2)) == fset(nx.k_edge_subgraphs(G, k=2))
|
| 354 |
+
|
| 355 |
+
assert fset(nx.k_edge_components(G, k=3)) == fset(nx.k_edge_subgraphs(G, k=3))
|
| 356 |
+
|
| 357 |
+
_check_edge_connectivity(G)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def test_four_clique():
|
| 361 |
+
paths = [
|
| 362 |
+
(11, 12, 13, 14, 11, 13, 14, 12), # first 4-clique
|
| 363 |
+
(21, 22, 23, 24, 21, 23, 24, 22), # second 4-clique
|
| 364 |
+
# paths connecting the 4 cliques such that they are
|
| 365 |
+
# 3-connected in G, but not in the subgraph.
|
| 366 |
+
# Case where the nodes bridging them do not have degree less than 3.
|
| 367 |
+
(100, 13),
|
| 368 |
+
(12, 100, 22),
|
| 369 |
+
(13, 200, 23),
|
| 370 |
+
(14, 300, 24),
|
| 371 |
+
]
|
| 372 |
+
G = nx.Graph(it.chain(*[pairwise(path) for path in paths]))
|
| 373 |
+
|
| 374 |
+
# The subgraphs and ccs are different for k=3
|
| 375 |
+
local_ccs = fset(nx.k_edge_components(G, k=3))
|
| 376 |
+
subgraphs = fset(nx.k_edge_subgraphs(G, k=3))
|
| 377 |
+
assert local_ccs != subgraphs
|
| 378 |
+
|
| 379 |
+
# The cliques ares in the same cc
|
| 380 |
+
clique1 = frozenset(paths[0])
|
| 381 |
+
clique2 = frozenset(paths[1])
|
| 382 |
+
assert clique1.union(clique2).union({100}) in local_ccs
|
| 383 |
+
|
| 384 |
+
# but different subgraphs
|
| 385 |
+
assert clique1 in subgraphs
|
| 386 |
+
assert clique2 in subgraphs
|
| 387 |
+
|
| 388 |
+
assert G.degree(100) == 3
|
| 389 |
+
|
| 390 |
+
_check_edge_connectivity(G)
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
def test_five_clique():
|
| 394 |
+
# Make a graph that can be disconnected less than 4 edges, but no node has
|
| 395 |
+
# degree less than 4.
|
| 396 |
+
G = nx.disjoint_union(nx.complete_graph(5), nx.complete_graph(5))
|
| 397 |
+
paths = [
|
| 398 |
+
# add aux-connections
|
| 399 |
+
(1, 100, 6),
|
| 400 |
+
(2, 100, 7),
|
| 401 |
+
(3, 200, 8),
|
| 402 |
+
(4, 200, 100),
|
| 403 |
+
]
|
| 404 |
+
G.add_edges_from(it.chain(*[pairwise(path) for path in paths]))
|
| 405 |
+
assert min(dict(nx.degree(G)).values()) == 4
|
| 406 |
+
|
| 407 |
+
# For k=3 they are the same
|
| 408 |
+
assert fset(nx.k_edge_components(G, k=3)) == fset(nx.k_edge_subgraphs(G, k=3))
|
| 409 |
+
|
| 410 |
+
# For k=4 they are the different
|
| 411 |
+
# the aux nodes are in the same CC as clique 1 but no the same subgraph
|
| 412 |
+
assert fset(nx.k_edge_components(G, k=4)) != fset(nx.k_edge_subgraphs(G, k=4))
|
| 413 |
+
|
| 414 |
+
# For k=5 they are not the same
|
| 415 |
+
assert fset(nx.k_edge_components(G, k=5)) != fset(nx.k_edge_subgraphs(G, k=5))
|
| 416 |
+
|
| 417 |
+
# For k=6 they are the same
|
| 418 |
+
assert fset(nx.k_edge_components(G, k=6)) == fset(nx.k_edge_subgraphs(G, k=6))
|
| 419 |
+
_check_edge_connectivity(G)
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
# ----------------
|
| 423 |
+
# Undirected tests
|
| 424 |
+
# ----------------
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def test_directed_aux_graph():
|
| 428 |
+
# Graph similar to the one in
|
| 429 |
+
# http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0136264
|
| 430 |
+
a, b, c, d, e, f, g, h, i = "abcdefghi"
|
| 431 |
+
dipaths = [
|
| 432 |
+
(a, d, b, f, c),
|
| 433 |
+
(a, e, b),
|
| 434 |
+
(a, e, b, c, g, b, a),
|
| 435 |
+
(c, b),
|
| 436 |
+
(f, g, f),
|
| 437 |
+
(h, i),
|
| 438 |
+
]
|
| 439 |
+
G = nx.DiGraph(it.chain(*[pairwise(path) for path in dipaths]))
|
| 440 |
+
aux_graph = EdgeComponentAuxGraph.construct(G)
|
| 441 |
+
|
| 442 |
+
components_1 = fset(aux_graph.k_edge_subgraphs(k=1))
|
| 443 |
+
target_1 = fset([{a, b, c, d, e, f, g}, {h}, {i}])
|
| 444 |
+
assert target_1 == components_1
|
| 445 |
+
|
| 446 |
+
# Check that the directed case for k=1 agrees with SCCs
|
| 447 |
+
alt_1 = fset(nx.strongly_connected_components(G))
|
| 448 |
+
assert alt_1 == components_1
|
| 449 |
+
|
| 450 |
+
components_2 = fset(aux_graph.k_edge_subgraphs(k=2))
|
| 451 |
+
target_2 = fset([{i}, {e}, {d}, {b, c, f, g}, {h}, {a}])
|
| 452 |
+
assert target_2 == components_2
|
| 453 |
+
|
| 454 |
+
components_3 = fset(aux_graph.k_edge_subgraphs(k=3))
|
| 455 |
+
target_3 = fset([{a}, {b}, {c}, {d}, {e}, {f}, {g}, {h}, {i}])
|
| 456 |
+
assert target_3 == components_3
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
def test_random_gnp_directed():
|
| 460 |
+
# seeds = [3894723670, 500186844, 267231174, 2181982262, 1116750056]
|
| 461 |
+
seeds = [21]
|
| 462 |
+
for seed in seeds:
|
| 463 |
+
G = nx.gnp_random_graph(20, 0.2, directed=True, seed=seed)
|
| 464 |
+
_check_edge_connectivity(G)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def test_configuration_directed():
|
| 468 |
+
# seeds = [671221681, 2403749451, 124433910, 672335939, 1193127215]
|
| 469 |
+
seeds = [67]
|
| 470 |
+
for seed in seeds:
|
| 471 |
+
deg_seq = nx.random_powerlaw_tree_sequence(20, seed=seed, tries=5000)
|
| 472 |
+
G = nx.DiGraph(nx.configuration_model(deg_seq, seed=seed))
|
| 473 |
+
G.remove_edges_from(nx.selfloop_edges(G))
|
| 474 |
+
_check_edge_connectivity(G)
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def test_shell_directed():
|
| 478 |
+
# seeds = [3134027055, 4079264063, 1350769518, 1405643020, 530038094]
|
| 479 |
+
seeds = [31]
|
| 480 |
+
for seed in seeds:
|
| 481 |
+
constructor = [(12, 70, 0.8), (15, 40, 0.6)]
|
| 482 |
+
G = nx.random_shell_graph(constructor, seed=seed).to_directed()
|
| 483 |
+
_check_edge_connectivity(G)
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def test_karate_directed():
|
| 487 |
+
G = nx.karate_club_graph().to_directed()
|
| 488 |
+
_check_edge_connectivity(G)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_kcomponents.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Test for Moody and White k-components algorithm
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
import networkx as nx
|
| 5 |
+
from networkx.algorithms.connectivity.kcomponents import (
|
| 6 |
+
_consolidate,
|
| 7 |
+
build_k_number_dict,
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
##
|
| 11 |
+
# A nice synthetic graph
|
| 12 |
+
##
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def torrents_and_ferraro_graph():
|
| 16 |
+
# Graph from https://arxiv.org/pdf/1503.04476v1 p.26
|
| 17 |
+
G = nx.convert_node_labels_to_integers(
|
| 18 |
+
nx.grid_graph([5, 5]), label_attribute="labels"
|
| 19 |
+
)
|
| 20 |
+
rlabels = nx.get_node_attributes(G, "labels")
|
| 21 |
+
labels = {v: k for k, v in rlabels.items()}
|
| 22 |
+
|
| 23 |
+
for nodes in [(labels[(0, 4)], labels[(1, 4)]), (labels[(3, 4)], labels[(4, 4)])]:
|
| 24 |
+
new_node = G.order() + 1
|
| 25 |
+
# Petersen graph is triconnected
|
| 26 |
+
P = nx.petersen_graph()
|
| 27 |
+
G = nx.disjoint_union(G, P)
|
| 28 |
+
# Add two edges between the grid and P
|
| 29 |
+
G.add_edge(new_node + 1, nodes[0])
|
| 30 |
+
G.add_edge(new_node, nodes[1])
|
| 31 |
+
# K5 is 4-connected
|
| 32 |
+
K = nx.complete_graph(5)
|
| 33 |
+
G = nx.disjoint_union(G, K)
|
| 34 |
+
# Add three edges between P and K5
|
| 35 |
+
G.add_edge(new_node + 2, new_node + 11)
|
| 36 |
+
G.add_edge(new_node + 3, new_node + 12)
|
| 37 |
+
G.add_edge(new_node + 4, new_node + 13)
|
| 38 |
+
# Add another K5 sharing a node
|
| 39 |
+
G = nx.disjoint_union(G, K)
|
| 40 |
+
nbrs = G[new_node + 10]
|
| 41 |
+
G.remove_node(new_node + 10)
|
| 42 |
+
for nbr in nbrs:
|
| 43 |
+
G.add_edge(new_node + 17, nbr)
|
| 44 |
+
# This edge makes the graph biconnected; it's
|
| 45 |
+
# needed because K5s share only one node.
|
| 46 |
+
G.add_edge(new_node + 16, new_node + 8)
|
| 47 |
+
|
| 48 |
+
for nodes in [(labels[(0, 0)], labels[(1, 0)]), (labels[(3, 0)], labels[(4, 0)])]:
|
| 49 |
+
new_node = G.order() + 1
|
| 50 |
+
# Petersen graph is triconnected
|
| 51 |
+
P = nx.petersen_graph()
|
| 52 |
+
G = nx.disjoint_union(G, P)
|
| 53 |
+
# Add two edges between the grid and P
|
| 54 |
+
G.add_edge(new_node + 1, nodes[0])
|
| 55 |
+
G.add_edge(new_node, nodes[1])
|
| 56 |
+
# K5 is 4-connected
|
| 57 |
+
K = nx.complete_graph(5)
|
| 58 |
+
G = nx.disjoint_union(G, K)
|
| 59 |
+
# Add three edges between P and K5
|
| 60 |
+
G.add_edge(new_node + 2, new_node + 11)
|
| 61 |
+
G.add_edge(new_node + 3, new_node + 12)
|
| 62 |
+
G.add_edge(new_node + 4, new_node + 13)
|
| 63 |
+
# Add another K5 sharing two nodes
|
| 64 |
+
G = nx.disjoint_union(G, K)
|
| 65 |
+
nbrs = G[new_node + 10]
|
| 66 |
+
G.remove_node(new_node + 10)
|
| 67 |
+
for nbr in nbrs:
|
| 68 |
+
G.add_edge(new_node + 17, nbr)
|
| 69 |
+
nbrs2 = G[new_node + 9]
|
| 70 |
+
G.remove_node(new_node + 9)
|
| 71 |
+
for nbr in nbrs2:
|
| 72 |
+
G.add_edge(new_node + 18, nbr)
|
| 73 |
+
return G
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def test_directed():
|
| 77 |
+
with pytest.raises(nx.NetworkXNotImplemented):
|
| 78 |
+
G = nx.gnp_random_graph(10, 0.2, directed=True, seed=42)
|
| 79 |
+
nx.k_components(G)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
# Helper function
|
| 83 |
+
def _check_connectivity(G, k_components):
|
| 84 |
+
for k, components in k_components.items():
|
| 85 |
+
if k < 3:
|
| 86 |
+
continue
|
| 87 |
+
# check that k-components have node connectivity >= k.
|
| 88 |
+
for component in components:
|
| 89 |
+
C = G.subgraph(component)
|
| 90 |
+
K = nx.node_connectivity(C)
|
| 91 |
+
assert K >= k
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@pytest.mark.slow
|
| 95 |
+
def test_torrents_and_ferraro_graph():
|
| 96 |
+
G = torrents_and_ferraro_graph()
|
| 97 |
+
result = nx.k_components(G)
|
| 98 |
+
_check_connectivity(G, result)
|
| 99 |
+
|
| 100 |
+
# In this example graph there are 8 3-components, 4 with 15 nodes
|
| 101 |
+
# and 4 with 5 nodes.
|
| 102 |
+
assert len(result[3]) == 8
|
| 103 |
+
assert len([c for c in result[3] if len(c) == 15]) == 4
|
| 104 |
+
assert len([c for c in result[3] if len(c) == 5]) == 4
|
| 105 |
+
# There are also 8 4-components all with 5 nodes.
|
| 106 |
+
assert len(result[4]) == 8
|
| 107 |
+
assert all(len(c) == 5 for c in result[4])
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
@pytest.mark.slow
|
| 111 |
+
def test_random_gnp():
|
| 112 |
+
G = nx.gnp_random_graph(50, 0.2, seed=42)
|
| 113 |
+
result = nx.k_components(G)
|
| 114 |
+
_check_connectivity(G, result)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
@pytest.mark.slow
|
| 118 |
+
def test_shell():
|
| 119 |
+
constructor = [(20, 80, 0.8), (80, 180, 0.6)]
|
| 120 |
+
G = nx.random_shell_graph(constructor, seed=42)
|
| 121 |
+
result = nx.k_components(G)
|
| 122 |
+
_check_connectivity(G, result)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def test_configuration():
|
| 126 |
+
deg_seq = nx.random_powerlaw_tree_sequence(100, tries=5, seed=72)
|
| 127 |
+
G = nx.Graph(nx.configuration_model(deg_seq))
|
| 128 |
+
G.remove_edges_from(nx.selfloop_edges(G))
|
| 129 |
+
result = nx.k_components(G)
|
| 130 |
+
_check_connectivity(G, result)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def test_karate():
|
| 134 |
+
G = nx.karate_club_graph()
|
| 135 |
+
result = nx.k_components(G)
|
| 136 |
+
_check_connectivity(G, result)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_karate_component_number():
|
| 140 |
+
karate_k_num = {
|
| 141 |
+
0: 4,
|
| 142 |
+
1: 4,
|
| 143 |
+
2: 4,
|
| 144 |
+
3: 4,
|
| 145 |
+
4: 3,
|
| 146 |
+
5: 3,
|
| 147 |
+
6: 3,
|
| 148 |
+
7: 4,
|
| 149 |
+
8: 4,
|
| 150 |
+
9: 2,
|
| 151 |
+
10: 3,
|
| 152 |
+
11: 1,
|
| 153 |
+
12: 2,
|
| 154 |
+
13: 4,
|
| 155 |
+
14: 2,
|
| 156 |
+
15: 2,
|
| 157 |
+
16: 2,
|
| 158 |
+
17: 2,
|
| 159 |
+
18: 2,
|
| 160 |
+
19: 3,
|
| 161 |
+
20: 2,
|
| 162 |
+
21: 2,
|
| 163 |
+
22: 2,
|
| 164 |
+
23: 3,
|
| 165 |
+
24: 3,
|
| 166 |
+
25: 3,
|
| 167 |
+
26: 2,
|
| 168 |
+
27: 3,
|
| 169 |
+
28: 3,
|
| 170 |
+
29: 3,
|
| 171 |
+
30: 4,
|
| 172 |
+
31: 3,
|
| 173 |
+
32: 4,
|
| 174 |
+
33: 4,
|
| 175 |
+
}
|
| 176 |
+
G = nx.karate_club_graph()
|
| 177 |
+
k_components = nx.k_components(G)
|
| 178 |
+
k_num = build_k_number_dict(k_components)
|
| 179 |
+
assert karate_k_num == k_num
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def test_davis_southern_women():
|
| 183 |
+
G = nx.davis_southern_women_graph()
|
| 184 |
+
result = nx.k_components(G)
|
| 185 |
+
_check_connectivity(G, result)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def test_davis_southern_women_detail_3_and_4():
|
| 189 |
+
solution = {
|
| 190 |
+
3: [
|
| 191 |
+
{
|
| 192 |
+
"Nora Fayette",
|
| 193 |
+
"E10",
|
| 194 |
+
"Myra Liddel",
|
| 195 |
+
"E12",
|
| 196 |
+
"E14",
|
| 197 |
+
"Frances Anderson",
|
| 198 |
+
"Evelyn Jefferson",
|
| 199 |
+
"Ruth DeSand",
|
| 200 |
+
"Helen Lloyd",
|
| 201 |
+
"Eleanor Nye",
|
| 202 |
+
"E9",
|
| 203 |
+
"E8",
|
| 204 |
+
"E5",
|
| 205 |
+
"E4",
|
| 206 |
+
"E7",
|
| 207 |
+
"E6",
|
| 208 |
+
"E1",
|
| 209 |
+
"Verne Sanderson",
|
| 210 |
+
"E3",
|
| 211 |
+
"E2",
|
| 212 |
+
"Theresa Anderson",
|
| 213 |
+
"Pearl Oglethorpe",
|
| 214 |
+
"Katherina Rogers",
|
| 215 |
+
"Brenda Rogers",
|
| 216 |
+
"E13",
|
| 217 |
+
"Charlotte McDowd",
|
| 218 |
+
"Sylvia Avondale",
|
| 219 |
+
"Laura Mandeville",
|
| 220 |
+
}
|
| 221 |
+
],
|
| 222 |
+
4: [
|
| 223 |
+
{
|
| 224 |
+
"Nora Fayette",
|
| 225 |
+
"E10",
|
| 226 |
+
"Verne Sanderson",
|
| 227 |
+
"E12",
|
| 228 |
+
"Frances Anderson",
|
| 229 |
+
"Evelyn Jefferson",
|
| 230 |
+
"Ruth DeSand",
|
| 231 |
+
"Helen Lloyd",
|
| 232 |
+
"Eleanor Nye",
|
| 233 |
+
"E9",
|
| 234 |
+
"E8",
|
| 235 |
+
"E5",
|
| 236 |
+
"E4",
|
| 237 |
+
"E7",
|
| 238 |
+
"E6",
|
| 239 |
+
"Myra Liddel",
|
| 240 |
+
"E3",
|
| 241 |
+
"Theresa Anderson",
|
| 242 |
+
"Katherina Rogers",
|
| 243 |
+
"Brenda Rogers",
|
| 244 |
+
"Charlotte McDowd",
|
| 245 |
+
"Sylvia Avondale",
|
| 246 |
+
"Laura Mandeville",
|
| 247 |
+
}
|
| 248 |
+
],
|
| 249 |
+
}
|
| 250 |
+
G = nx.davis_southern_women_graph()
|
| 251 |
+
result = nx.k_components(G)
|
| 252 |
+
for k, components in result.items():
|
| 253 |
+
if k < 3:
|
| 254 |
+
continue
|
| 255 |
+
assert len(components) == len(solution[k])
|
| 256 |
+
for component in components:
|
| 257 |
+
assert component in solution[k]
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def test_set_consolidation_rosettacode():
|
| 261 |
+
# Tests from http://rosettacode.org/wiki/Set_consolidation
|
| 262 |
+
def list_of_sets_equal(result, solution):
|
| 263 |
+
assert {frozenset(s) for s in result} == {frozenset(s) for s in solution}
|
| 264 |
+
|
| 265 |
+
question = [{"A", "B"}, {"C", "D"}]
|
| 266 |
+
solution = [{"A", "B"}, {"C", "D"}]
|
| 267 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
| 268 |
+
question = [{"A", "B"}, {"B", "C"}]
|
| 269 |
+
solution = [{"A", "B", "C"}]
|
| 270 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
| 271 |
+
question = [{"A", "B"}, {"C", "D"}, {"D", "B"}]
|
| 272 |
+
solution = [{"A", "C", "B", "D"}]
|
| 273 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
| 274 |
+
question = [{"H", "I", "K"}, {"A", "B"}, {"C", "D"}, {"D", "B"}, {"F", "G", "H"}]
|
| 275 |
+
solution = [{"A", "C", "B", "D"}, {"G", "F", "I", "H", "K"}]
|
| 276 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
| 277 |
+
question = [
|
| 278 |
+
{"A", "H"},
|
| 279 |
+
{"H", "I", "K"},
|
| 280 |
+
{"A", "B"},
|
| 281 |
+
{"C", "D"},
|
| 282 |
+
{"D", "B"},
|
| 283 |
+
{"F", "G", "H"},
|
| 284 |
+
]
|
| 285 |
+
solution = [{"A", "C", "B", "D", "G", "F", "I", "H", "K"}]
|
| 286 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
| 287 |
+
question = [
|
| 288 |
+
{"H", "I", "K"},
|
| 289 |
+
{"A", "B"},
|
| 290 |
+
{"C", "D"},
|
| 291 |
+
{"D", "B"},
|
| 292 |
+
{"F", "G", "H"},
|
| 293 |
+
{"A", "H"},
|
| 294 |
+
]
|
| 295 |
+
solution = [{"A", "C", "B", "D", "G", "F", "I", "H", "K"}]
|
| 296 |
+
list_of_sets_equal(_consolidate(question, 1), solution)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/connectivity/tests/test_kcutsets.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Jordi Torrents
|
| 2 |
+
# Test for k-cutsets
|
| 3 |
+
import itertools
|
| 4 |
+
|
| 5 |
+
import pytest
|
| 6 |
+
|
| 7 |
+
import networkx as nx
|
| 8 |
+
from networkx.algorithms import flow
|
| 9 |
+
from networkx.algorithms.connectivity.kcutsets import _is_separating_set
|
| 10 |
+
|
| 11 |
+
MAX_CUTSETS_TO_TEST = 4 # originally 100. cut to decrease testing time
|
| 12 |
+
|
| 13 |
+
flow_funcs = [
|
| 14 |
+
flow.boykov_kolmogorov,
|
| 15 |
+
flow.dinitz,
|
| 16 |
+
flow.edmonds_karp,
|
| 17 |
+
flow.preflow_push,
|
| 18 |
+
flow.shortest_augmenting_path,
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
##
|
| 23 |
+
# Some nice synthetic graphs
|
| 24 |
+
##
|
| 25 |
+
def graph_example_1():
|
| 26 |
+
G = nx.convert_node_labels_to_integers(
|
| 27 |
+
nx.grid_graph([5, 5]), label_attribute="labels"
|
| 28 |
+
)
|
| 29 |
+
rlabels = nx.get_node_attributes(G, "labels")
|
| 30 |
+
labels = {v: k for k, v in rlabels.items()}
|
| 31 |
+
|
| 32 |
+
for nodes in [
|
| 33 |
+
(labels[(0, 0)], labels[(1, 0)]),
|
| 34 |
+
(labels[(0, 4)], labels[(1, 4)]),
|
| 35 |
+
(labels[(3, 0)], labels[(4, 0)]),
|
| 36 |
+
(labels[(3, 4)], labels[(4, 4)]),
|
| 37 |
+
]:
|
| 38 |
+
new_node = G.order() + 1
|
| 39 |
+
# Petersen graph is triconnected
|
| 40 |
+
P = nx.petersen_graph()
|
| 41 |
+
G = nx.disjoint_union(G, P)
|
| 42 |
+
# Add two edges between the grid and P
|
| 43 |
+
G.add_edge(new_node + 1, nodes[0])
|
| 44 |
+
G.add_edge(new_node, nodes[1])
|
| 45 |
+
# K5 is 4-connected
|
| 46 |
+
K = nx.complete_graph(5)
|
| 47 |
+
G = nx.disjoint_union(G, K)
|
| 48 |
+
# Add three edges between P and K5
|
| 49 |
+
G.add_edge(new_node + 2, new_node + 11)
|
| 50 |
+
G.add_edge(new_node + 3, new_node + 12)
|
| 51 |
+
G.add_edge(new_node + 4, new_node + 13)
|
| 52 |
+
# Add another K5 sharing a node
|
| 53 |
+
G = nx.disjoint_union(G, K)
|
| 54 |
+
nbrs = G[new_node + 10]
|
| 55 |
+
G.remove_node(new_node + 10)
|
| 56 |
+
for nbr in nbrs:
|
| 57 |
+
G.add_edge(new_node + 17, nbr)
|
| 58 |
+
G.add_edge(new_node + 16, new_node + 5)
|
| 59 |
+
return G
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def torrents_and_ferraro_graph():
|
| 63 |
+
G = nx.convert_node_labels_to_integers(
|
| 64 |
+
nx.grid_graph([5, 5]), label_attribute="labels"
|
| 65 |
+
)
|
| 66 |
+
rlabels = nx.get_node_attributes(G, "labels")
|
| 67 |
+
labels = {v: k for k, v in rlabels.items()}
|
| 68 |
+
|
| 69 |
+
for nodes in [(labels[(0, 4)], labels[(1, 4)]), (labels[(3, 4)], labels[(4, 4)])]:
|
| 70 |
+
new_node = G.order() + 1
|
| 71 |
+
# Petersen graph is triconnected
|
| 72 |
+
P = nx.petersen_graph()
|
| 73 |
+
G = nx.disjoint_union(G, P)
|
| 74 |
+
# Add two edges between the grid and P
|
| 75 |
+
G.add_edge(new_node + 1, nodes[0])
|
| 76 |
+
G.add_edge(new_node, nodes[1])
|
| 77 |
+
# K5 is 4-connected
|
| 78 |
+
K = nx.complete_graph(5)
|
| 79 |
+
G = nx.disjoint_union(G, K)
|
| 80 |
+
# Add three edges between P and K5
|
| 81 |
+
G.add_edge(new_node + 2, new_node + 11)
|
| 82 |
+
G.add_edge(new_node + 3, new_node + 12)
|
| 83 |
+
G.add_edge(new_node + 4, new_node + 13)
|
| 84 |
+
# Add another K5 sharing a node
|
| 85 |
+
G = nx.disjoint_union(G, K)
|
| 86 |
+
nbrs = G[new_node + 10]
|
| 87 |
+
G.remove_node(new_node + 10)
|
| 88 |
+
for nbr in nbrs:
|
| 89 |
+
G.add_edge(new_node + 17, nbr)
|
| 90 |
+
# Commenting this makes the graph not biconnected !!
|
| 91 |
+
# This stupid mistake make one reviewer very angry :P
|
| 92 |
+
G.add_edge(new_node + 16, new_node + 8)
|
| 93 |
+
|
| 94 |
+
for nodes in [(labels[(0, 0)], labels[(1, 0)]), (labels[(3, 0)], labels[(4, 0)])]:
|
| 95 |
+
new_node = G.order() + 1
|
| 96 |
+
# Petersen graph is triconnected
|
| 97 |
+
P = nx.petersen_graph()
|
| 98 |
+
G = nx.disjoint_union(G, P)
|
| 99 |
+
# Add two edges between the grid and P
|
| 100 |
+
G.add_edge(new_node + 1, nodes[0])
|
| 101 |
+
G.add_edge(new_node, nodes[1])
|
| 102 |
+
# K5 is 4-connected
|
| 103 |
+
K = nx.complete_graph(5)
|
| 104 |
+
G = nx.disjoint_union(G, K)
|
| 105 |
+
# Add three edges between P and K5
|
| 106 |
+
G.add_edge(new_node + 2, new_node + 11)
|
| 107 |
+
G.add_edge(new_node + 3, new_node + 12)
|
| 108 |
+
G.add_edge(new_node + 4, new_node + 13)
|
| 109 |
+
# Add another K5 sharing two nodes
|
| 110 |
+
G = nx.disjoint_union(G, K)
|
| 111 |
+
nbrs = G[new_node + 10]
|
| 112 |
+
G.remove_node(new_node + 10)
|
| 113 |
+
for nbr in nbrs:
|
| 114 |
+
G.add_edge(new_node + 17, nbr)
|
| 115 |
+
nbrs2 = G[new_node + 9]
|
| 116 |
+
G.remove_node(new_node + 9)
|
| 117 |
+
for nbr in nbrs2:
|
| 118 |
+
G.add_edge(new_node + 18, nbr)
|
| 119 |
+
return G
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
# Helper function
|
| 123 |
+
def _check_separating_sets(G):
|
| 124 |
+
for cc in nx.connected_components(G):
|
| 125 |
+
if len(cc) < 3:
|
| 126 |
+
continue
|
| 127 |
+
Gc = G.subgraph(cc)
|
| 128 |
+
node_conn = nx.node_connectivity(Gc)
|
| 129 |
+
all_cuts = nx.all_node_cuts(Gc)
|
| 130 |
+
# Only test a limited number of cut sets to reduce test time.
|
| 131 |
+
for cut in itertools.islice(all_cuts, MAX_CUTSETS_TO_TEST):
|
| 132 |
+
assert node_conn == len(cut)
|
| 133 |
+
assert not nx.is_connected(nx.restricted_view(G, cut, []))
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
@pytest.mark.slow
|
| 137 |
+
def test_torrents_and_ferraro_graph():
|
| 138 |
+
G = torrents_and_ferraro_graph()
|
| 139 |
+
_check_separating_sets(G)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_example_1():
|
| 143 |
+
G = graph_example_1()
|
| 144 |
+
_check_separating_sets(G)
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def test_random_gnp():
|
| 148 |
+
G = nx.gnp_random_graph(100, 0.1, seed=42)
|
| 149 |
+
_check_separating_sets(G)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def test_shell():
|
| 153 |
+
constructor = [(20, 80, 0.8), (80, 180, 0.6)]
|
| 154 |
+
G = nx.random_shell_graph(constructor, seed=42)
|
| 155 |
+
_check_separating_sets(G)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def test_configuration():
|
| 159 |
+
deg_seq = nx.random_powerlaw_tree_sequence(100, tries=5, seed=72)
|
| 160 |
+
G = nx.Graph(nx.configuration_model(deg_seq))
|
| 161 |
+
G.remove_edges_from(nx.selfloop_edges(G))
|
| 162 |
+
_check_separating_sets(G)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def test_karate():
|
| 166 |
+
G = nx.karate_club_graph()
|
| 167 |
+
_check_separating_sets(G)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def _generate_no_biconnected(max_attempts=50):
|
| 171 |
+
attempts = 0
|
| 172 |
+
while True:
|
| 173 |
+
G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
|
| 174 |
+
if nx.is_connected(G) and not nx.is_biconnected(G):
|
| 175 |
+
attempts = 0
|
| 176 |
+
yield G
|
| 177 |
+
else:
|
| 178 |
+
if attempts >= max_attempts:
|
| 179 |
+
msg = f"Tried {attempts} times: no suitable Graph."
|
| 180 |
+
raise Exception(msg)
|
| 181 |
+
else:
|
| 182 |
+
attempts += 1
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def test_articulation_points():
|
| 186 |
+
Ggen = _generate_no_biconnected()
|
| 187 |
+
for i in range(1): # change 1 to 3 or more for more realizations.
|
| 188 |
+
G = next(Ggen)
|
| 189 |
+
articulation_points = [{a} for a in nx.articulation_points(G)]
|
| 190 |
+
for cut in nx.all_node_cuts(G):
|
| 191 |
+
assert cut in articulation_points
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def test_grid_2d_graph():
|
| 195 |
+
# All minimum node cuts of a 2d grid
|
| 196 |
+
# are the four pairs of nodes that are
|
| 197 |
+
# neighbors of the four corner nodes.
|
| 198 |
+
G = nx.grid_2d_graph(5, 5)
|
| 199 |
+
solution = [{(0, 1), (1, 0)}, {(3, 0), (4, 1)}, {(3, 4), (4, 3)}, {(0, 3), (1, 4)}]
|
| 200 |
+
for cut in nx.all_node_cuts(G):
|
| 201 |
+
assert cut in solution
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def test_disconnected_graph():
|
| 205 |
+
G = nx.fast_gnp_random_graph(100, 0.01, seed=42)
|
| 206 |
+
cuts = nx.all_node_cuts(G)
|
| 207 |
+
pytest.raises(nx.NetworkXError, next, cuts)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
@pytest.mark.slow
|
| 211 |
+
def test_alternative_flow_functions():
|
| 212 |
+
graphs = [nx.grid_2d_graph(4, 4), nx.cycle_graph(5)]
|
| 213 |
+
for G in graphs:
|
| 214 |
+
node_conn = nx.node_connectivity(G)
|
| 215 |
+
for flow_func in flow_funcs:
|
| 216 |
+
all_cuts = nx.all_node_cuts(G, flow_func=flow_func)
|
| 217 |
+
# Only test a limited number of cut sets to reduce test time.
|
| 218 |
+
for cut in itertools.islice(all_cuts, MAX_CUTSETS_TO_TEST):
|
| 219 |
+
assert node_conn == len(cut)
|
| 220 |
+
assert not nx.is_connected(nx.restricted_view(G, cut, []))
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def test_is_separating_set_complete_graph():
|
| 224 |
+
G = nx.complete_graph(5)
|
| 225 |
+
assert _is_separating_set(G, {0, 1, 2, 3})
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def test_is_separating_set():
|
| 229 |
+
for i in [5, 10, 15]:
|
| 230 |
+
G = nx.star_graph(i)
|
| 231 |
+
max_degree_node = max(G, key=G.degree)
|
| 232 |
+
assert _is_separating_set(G, {max_degree_node})
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def test_non_repeated_cuts():
|
| 236 |
+
# The algorithm was repeating the cut {0, 1} for the giant biconnected
|
| 237 |
+
# component of the Karate club graph.
|
| 238 |
+
K = nx.karate_club_graph()
|
| 239 |
+
bcc = max(list(nx.biconnected_components(K)), key=len)
|
| 240 |
+
G = K.subgraph(bcc)
|
| 241 |
+
solution = [{32, 33}, {2, 33}, {0, 3}, {0, 1}, {29, 33}]
|
| 242 |
+
cuts = list(nx.all_node_cuts(G))
|
| 243 |
+
if len(solution) != len(cuts):
|
| 244 |
+
print(f"Solution: {solution}")
|
| 245 |
+
print(f"Result: {cuts}")
|
| 246 |
+
assert len(solution) == len(cuts)
|
| 247 |
+
for cut in cuts:
|
| 248 |
+
assert cut in solution
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def test_cycle_graph():
|
| 252 |
+
G = nx.cycle_graph(5)
|
| 253 |
+
solution = [{0, 2}, {0, 3}, {1, 3}, {1, 4}, {2, 4}]
|
| 254 |
+
cuts = list(nx.all_node_cuts(G))
|
| 255 |
+
assert len(solution) == len(cuts)
|
| 256 |
+
for cut in cuts:
|
| 257 |
+
assert cut in solution
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def test_complete_graph():
|
| 261 |
+
G = nx.complete_graph(5)
|
| 262 |
+
solution = [{0, 1, 2, 3}, {0, 1, 2, 4}, {0, 1, 3, 4}, {0, 2, 3, 4}, {1, 2, 3, 4}]
|
| 263 |
+
cuts = list(nx.all_node_cuts(G))
|
| 264 |
+
assert len(solution) == len(cuts)
|
| 265 |
+
for cut in cuts:
|
| 266 |
+
assert cut in solution
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from networkx.algorithms.shortest_paths.generic import *
|
| 2 |
+
from networkx.algorithms.shortest_paths.unweighted import *
|
| 3 |
+
from networkx.algorithms.shortest_paths.weighted import *
|
| 4 |
+
from networkx.algorithms.shortest_paths.astar import *
|
| 5 |
+
from networkx.algorithms.shortest_paths.dense import *
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (558 Bytes). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/astar.cpython-311.pyc
ADDED
|
Binary file (7.82 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/dense.cpython-311.pyc
ADDED
|
Binary file (9.79 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/generic.cpython-311.pyc
ADDED
|
Binary file (27.1 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/__pycache__/weighted.cpython-311.pyc
ADDED
|
Binary file (90.3 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/astar.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Shortest paths and path lengths using the A* ("A star") algorithm.
|
| 2 |
+
"""
|
| 3 |
+
from heapq import heappop, heappush
|
| 4 |
+
from itertools import count
|
| 5 |
+
|
| 6 |
+
import networkx as nx
|
| 7 |
+
from networkx.algorithms.shortest_paths.weighted import _weight_function
|
| 8 |
+
|
| 9 |
+
__all__ = ["astar_path", "astar_path_length"]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@nx._dispatch(edge_attrs="weight", preserve_node_attrs="heuristic")
|
| 13 |
+
def astar_path(G, source, target, heuristic=None, weight="weight"):
|
| 14 |
+
"""Returns a list of nodes in a shortest path between source and target
|
| 15 |
+
using the A* ("A-star") algorithm.
|
| 16 |
+
|
| 17 |
+
There may be more than one shortest path. This returns only one.
|
| 18 |
+
|
| 19 |
+
Parameters
|
| 20 |
+
----------
|
| 21 |
+
G : NetworkX graph
|
| 22 |
+
|
| 23 |
+
source : node
|
| 24 |
+
Starting node for path
|
| 25 |
+
|
| 26 |
+
target : node
|
| 27 |
+
Ending node for path
|
| 28 |
+
|
| 29 |
+
heuristic : function
|
| 30 |
+
A function to evaluate the estimate of the distance
|
| 31 |
+
from the a node to the target. The function takes
|
| 32 |
+
two nodes arguments and must return a number.
|
| 33 |
+
If the heuristic is inadmissible (if it might
|
| 34 |
+
overestimate the cost of reaching the goal from a node),
|
| 35 |
+
the result may not be a shortest path.
|
| 36 |
+
The algorithm does not support updating heuristic
|
| 37 |
+
values for the same node due to caching the first
|
| 38 |
+
heuristic calculation per node.
|
| 39 |
+
|
| 40 |
+
weight : string or function
|
| 41 |
+
If this is a string, then edge weights will be accessed via the
|
| 42 |
+
edge attribute with this key (that is, the weight of the edge
|
| 43 |
+
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
| 44 |
+
such edge attribute exists, the weight of the edge is assumed to
|
| 45 |
+
be one.
|
| 46 |
+
If this is a function, the weight of an edge is the value
|
| 47 |
+
returned by the function. The function must accept exactly three
|
| 48 |
+
positional arguments: the two endpoints of an edge and the
|
| 49 |
+
dictionary of edge attributes for that edge. The function must
|
| 50 |
+
return a number or None to indicate a hidden edge.
|
| 51 |
+
|
| 52 |
+
Raises
|
| 53 |
+
------
|
| 54 |
+
NetworkXNoPath
|
| 55 |
+
If no path exists between source and target.
|
| 56 |
+
|
| 57 |
+
Examples
|
| 58 |
+
--------
|
| 59 |
+
>>> G = nx.path_graph(5)
|
| 60 |
+
>>> print(nx.astar_path(G, 0, 4))
|
| 61 |
+
[0, 1, 2, 3, 4]
|
| 62 |
+
>>> G = nx.grid_graph(dim=[3, 3]) # nodes are two-tuples (x,y)
|
| 63 |
+
>>> nx.set_edge_attributes(G, {e: e[1][0] * 2 for e in G.edges()}, "cost")
|
| 64 |
+
>>> def dist(a, b):
|
| 65 |
+
... (x1, y1) = a
|
| 66 |
+
... (x2, y2) = b
|
| 67 |
+
... return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
|
| 68 |
+
>>> print(nx.astar_path(G, (0, 0), (2, 2), heuristic=dist, weight="cost"))
|
| 69 |
+
[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]
|
| 70 |
+
|
| 71 |
+
Notes
|
| 72 |
+
-----
|
| 73 |
+
Edge weight attributes must be numerical.
|
| 74 |
+
Distances are calculated as sums of weighted edges traversed.
|
| 75 |
+
|
| 76 |
+
The weight function can be used to hide edges by returning None.
|
| 77 |
+
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
| 78 |
+
will find the shortest red path.
|
| 79 |
+
|
| 80 |
+
See Also
|
| 81 |
+
--------
|
| 82 |
+
shortest_path, dijkstra_path
|
| 83 |
+
|
| 84 |
+
"""
|
| 85 |
+
if source not in G or target not in G:
|
| 86 |
+
msg = f"Either source {source} or target {target} is not in G"
|
| 87 |
+
raise nx.NodeNotFound(msg)
|
| 88 |
+
|
| 89 |
+
if heuristic is None:
|
| 90 |
+
# The default heuristic is h=0 - same as Dijkstra's algorithm
|
| 91 |
+
def heuristic(u, v):
|
| 92 |
+
return 0
|
| 93 |
+
|
| 94 |
+
push = heappush
|
| 95 |
+
pop = heappop
|
| 96 |
+
weight = _weight_function(G, weight)
|
| 97 |
+
|
| 98 |
+
G_succ = G._adj # For speed-up (and works for both directed and undirected graphs)
|
| 99 |
+
|
| 100 |
+
# The queue stores priority, node, cost to reach, and parent.
|
| 101 |
+
# Uses Python heapq to keep in priority order.
|
| 102 |
+
# Add a counter to the queue to prevent the underlying heap from
|
| 103 |
+
# attempting to compare the nodes themselves. The hash breaks ties in the
|
| 104 |
+
# priority and is guaranteed unique for all nodes in the graph.
|
| 105 |
+
c = count()
|
| 106 |
+
queue = [(0, next(c), source, 0, None)]
|
| 107 |
+
|
| 108 |
+
# Maps enqueued nodes to distance of discovered paths and the
|
| 109 |
+
# computed heuristics to target. We avoid computing the heuristics
|
| 110 |
+
# more than once and inserting the node into the queue too many times.
|
| 111 |
+
enqueued = {}
|
| 112 |
+
# Maps explored nodes to parent closest to the source.
|
| 113 |
+
explored = {}
|
| 114 |
+
|
| 115 |
+
while queue:
|
| 116 |
+
# Pop the smallest item from queue.
|
| 117 |
+
_, __, curnode, dist, parent = pop(queue)
|
| 118 |
+
|
| 119 |
+
if curnode == target:
|
| 120 |
+
path = [curnode]
|
| 121 |
+
node = parent
|
| 122 |
+
while node is not None:
|
| 123 |
+
path.append(node)
|
| 124 |
+
node = explored[node]
|
| 125 |
+
path.reverse()
|
| 126 |
+
return path
|
| 127 |
+
|
| 128 |
+
if curnode in explored:
|
| 129 |
+
# Do not override the parent of starting node
|
| 130 |
+
if explored[curnode] is None:
|
| 131 |
+
continue
|
| 132 |
+
|
| 133 |
+
# Skip bad paths that were enqueued before finding a better one
|
| 134 |
+
qcost, h = enqueued[curnode]
|
| 135 |
+
if qcost < dist:
|
| 136 |
+
continue
|
| 137 |
+
|
| 138 |
+
explored[curnode] = parent
|
| 139 |
+
|
| 140 |
+
for neighbor, w in G_succ[curnode].items():
|
| 141 |
+
cost = weight(curnode, neighbor, w)
|
| 142 |
+
if cost is None:
|
| 143 |
+
continue
|
| 144 |
+
ncost = dist + cost
|
| 145 |
+
if neighbor in enqueued:
|
| 146 |
+
qcost, h = enqueued[neighbor]
|
| 147 |
+
# if qcost <= ncost, a less costly path from the
|
| 148 |
+
# neighbor to the source was already determined.
|
| 149 |
+
# Therefore, we won't attempt to push this neighbor
|
| 150 |
+
# to the queue
|
| 151 |
+
if qcost <= ncost:
|
| 152 |
+
continue
|
| 153 |
+
else:
|
| 154 |
+
h = heuristic(neighbor, target)
|
| 155 |
+
enqueued[neighbor] = ncost, h
|
| 156 |
+
push(queue, (ncost + h, next(c), neighbor, ncost, curnode))
|
| 157 |
+
|
| 158 |
+
raise nx.NetworkXNoPath(f"Node {target} not reachable from {source}")
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
@nx._dispatch(edge_attrs="weight", preserve_node_attrs="heuristic")
|
| 162 |
+
def astar_path_length(G, source, target, heuristic=None, weight="weight"):
|
| 163 |
+
"""Returns the length of the shortest path between source and target using
|
| 164 |
+
the A* ("A-star") algorithm.
|
| 165 |
+
|
| 166 |
+
Parameters
|
| 167 |
+
----------
|
| 168 |
+
G : NetworkX graph
|
| 169 |
+
|
| 170 |
+
source : node
|
| 171 |
+
Starting node for path
|
| 172 |
+
|
| 173 |
+
target : node
|
| 174 |
+
Ending node for path
|
| 175 |
+
|
| 176 |
+
heuristic : function
|
| 177 |
+
A function to evaluate the estimate of the distance
|
| 178 |
+
from the a node to the target. The function takes
|
| 179 |
+
two nodes arguments and must return a number.
|
| 180 |
+
If the heuristic is inadmissible (if it might
|
| 181 |
+
overestimate the cost of reaching the goal from a node),
|
| 182 |
+
the result may not be a shortest path.
|
| 183 |
+
The algorithm does not support updating heuristic
|
| 184 |
+
values for the same node due to caching the first
|
| 185 |
+
heuristic calculation per node.
|
| 186 |
+
|
| 187 |
+
weight : string or function
|
| 188 |
+
If this is a string, then edge weights will be accessed via the
|
| 189 |
+
edge attribute with this key (that is, the weight of the edge
|
| 190 |
+
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
| 191 |
+
such edge attribute exists, the weight of the edge is assumed to
|
| 192 |
+
be one.
|
| 193 |
+
If this is a function, the weight of an edge is the value
|
| 194 |
+
returned by the function. The function must accept exactly three
|
| 195 |
+
positional arguments: the two endpoints of an edge and the
|
| 196 |
+
dictionary of edge attributes for that edge. The function must
|
| 197 |
+
return a number or None to indicate a hidden edge.
|
| 198 |
+
Raises
|
| 199 |
+
------
|
| 200 |
+
NetworkXNoPath
|
| 201 |
+
If no path exists between source and target.
|
| 202 |
+
|
| 203 |
+
See Also
|
| 204 |
+
--------
|
| 205 |
+
astar_path
|
| 206 |
+
|
| 207 |
+
"""
|
| 208 |
+
if source not in G or target not in G:
|
| 209 |
+
msg = f"Either source {source} or target {target} is not in G"
|
| 210 |
+
raise nx.NodeNotFound(msg)
|
| 211 |
+
|
| 212 |
+
weight = _weight_function(G, weight)
|
| 213 |
+
path = astar_path(G, source, target, heuristic, weight)
|
| 214 |
+
return sum(weight(u, v, G[u][v]) for u, v in zip(path[:-1], path[1:]))
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/dense.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Floyd-Warshall algorithm for shortest paths.
|
| 2 |
+
"""
|
| 3 |
+
import networkx as nx
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
"floyd_warshall",
|
| 7 |
+
"floyd_warshall_predecessor_and_distance",
|
| 8 |
+
"reconstruct_path",
|
| 9 |
+
"floyd_warshall_numpy",
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@nx._dispatch(edge_attrs="weight")
|
| 14 |
+
def floyd_warshall_numpy(G, nodelist=None, weight="weight"):
|
| 15 |
+
"""Find all-pairs shortest path lengths using Floyd's algorithm.
|
| 16 |
+
|
| 17 |
+
This algorithm for finding shortest paths takes advantage of
|
| 18 |
+
matrix representations of a graph and works well for dense
|
| 19 |
+
graphs where all-pairs shortest path lengths are desired.
|
| 20 |
+
The results are returned as a NumPy array, distance[i, j],
|
| 21 |
+
where i and j are the indexes of two nodes in nodelist.
|
| 22 |
+
The entry distance[i, j] is the distance along a shortest
|
| 23 |
+
path from i to j. If no path exists the distance is Inf.
|
| 24 |
+
|
| 25 |
+
Parameters
|
| 26 |
+
----------
|
| 27 |
+
G : NetworkX graph
|
| 28 |
+
|
| 29 |
+
nodelist : list, optional (default=G.nodes)
|
| 30 |
+
The rows and columns are ordered by the nodes in nodelist.
|
| 31 |
+
If nodelist is None then the ordering is produced by G.nodes.
|
| 32 |
+
Nodelist should include all nodes in G.
|
| 33 |
+
|
| 34 |
+
weight: string, optional (default='weight')
|
| 35 |
+
Edge data key corresponding to the edge weight.
|
| 36 |
+
|
| 37 |
+
Returns
|
| 38 |
+
-------
|
| 39 |
+
distance : 2D numpy.ndarray
|
| 40 |
+
A numpy array of shortest path distances between nodes.
|
| 41 |
+
If there is no path between two nodes the value is Inf.
|
| 42 |
+
|
| 43 |
+
Examples
|
| 44 |
+
--------
|
| 45 |
+
>>> G = nx.DiGraph()
|
| 46 |
+
>>> G.add_weighted_edges_from([(0, 1, 5), (1, 2, 2), (2, 3, -3), (1, 3, 10), (3, 2, 8)])
|
| 47 |
+
>>> nx.floyd_warshall_numpy(G)
|
| 48 |
+
array([[ 0., 5., 7., 4.],
|
| 49 |
+
[inf, 0., 2., -1.],
|
| 50 |
+
[inf, inf, 0., -3.],
|
| 51 |
+
[inf, inf, 8., 0.]])
|
| 52 |
+
|
| 53 |
+
Notes
|
| 54 |
+
-----
|
| 55 |
+
Floyd's algorithm is appropriate for finding shortest paths in
|
| 56 |
+
dense graphs or graphs with negative weights when Dijkstra's
|
| 57 |
+
algorithm fails. This algorithm can still fail if there are negative
|
| 58 |
+
cycles. It has running time $O(n^3)$ with running space of $O(n^2)$.
|
| 59 |
+
|
| 60 |
+
Raises
|
| 61 |
+
------
|
| 62 |
+
NetworkXError
|
| 63 |
+
If nodelist is not a list of the nodes in G.
|
| 64 |
+
"""
|
| 65 |
+
import numpy as np
|
| 66 |
+
|
| 67 |
+
if nodelist is not None:
|
| 68 |
+
if not (len(nodelist) == len(G) == len(set(nodelist))):
|
| 69 |
+
raise nx.NetworkXError(
|
| 70 |
+
"nodelist must contain every node in G with no repeats."
|
| 71 |
+
"If you wanted a subgraph of G use G.subgraph(nodelist)"
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# To handle cases when an edge has weight=0, we must make sure that
|
| 75 |
+
# nonedges are not given the value 0 as well.
|
| 76 |
+
A = nx.to_numpy_array(
|
| 77 |
+
G, nodelist, multigraph_weight=min, weight=weight, nonedge=np.inf
|
| 78 |
+
)
|
| 79 |
+
n, m = A.shape
|
| 80 |
+
np.fill_diagonal(A, 0) # diagonal elements should be zero
|
| 81 |
+
for i in range(n):
|
| 82 |
+
# The second term has the same shape as A due to broadcasting
|
| 83 |
+
A = np.minimum(A, A[i, :][np.newaxis, :] + A[:, i][:, np.newaxis])
|
| 84 |
+
return A
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@nx._dispatch(edge_attrs="weight")
|
| 88 |
+
def floyd_warshall_predecessor_and_distance(G, weight="weight"):
|
| 89 |
+
"""Find all-pairs shortest path lengths using Floyd's algorithm.
|
| 90 |
+
|
| 91 |
+
Parameters
|
| 92 |
+
----------
|
| 93 |
+
G : NetworkX graph
|
| 94 |
+
|
| 95 |
+
weight: string, optional (default= 'weight')
|
| 96 |
+
Edge data key corresponding to the edge weight.
|
| 97 |
+
|
| 98 |
+
Returns
|
| 99 |
+
-------
|
| 100 |
+
predecessor,distance : dictionaries
|
| 101 |
+
Dictionaries, keyed by source and target, of predecessors and distances
|
| 102 |
+
in the shortest path.
|
| 103 |
+
|
| 104 |
+
Examples
|
| 105 |
+
--------
|
| 106 |
+
>>> G = nx.DiGraph()
|
| 107 |
+
>>> G.add_weighted_edges_from(
|
| 108 |
+
... [
|
| 109 |
+
... ("s", "u", 10),
|
| 110 |
+
... ("s", "x", 5),
|
| 111 |
+
... ("u", "v", 1),
|
| 112 |
+
... ("u", "x", 2),
|
| 113 |
+
... ("v", "y", 1),
|
| 114 |
+
... ("x", "u", 3),
|
| 115 |
+
... ("x", "v", 5),
|
| 116 |
+
... ("x", "y", 2),
|
| 117 |
+
... ("y", "s", 7),
|
| 118 |
+
... ("y", "v", 6),
|
| 119 |
+
... ]
|
| 120 |
+
... )
|
| 121 |
+
>>> predecessors, _ = nx.floyd_warshall_predecessor_and_distance(G)
|
| 122 |
+
>>> print(nx.reconstruct_path("s", "v", predecessors))
|
| 123 |
+
['s', 'x', 'u', 'v']
|
| 124 |
+
|
| 125 |
+
Notes
|
| 126 |
+
-----
|
| 127 |
+
Floyd's algorithm is appropriate for finding shortest paths
|
| 128 |
+
in dense graphs or graphs with negative weights when Dijkstra's algorithm
|
| 129 |
+
fails. This algorithm can still fail if there are negative cycles.
|
| 130 |
+
It has running time $O(n^3)$ with running space of $O(n^2)$.
|
| 131 |
+
|
| 132 |
+
See Also
|
| 133 |
+
--------
|
| 134 |
+
floyd_warshall
|
| 135 |
+
floyd_warshall_numpy
|
| 136 |
+
all_pairs_shortest_path
|
| 137 |
+
all_pairs_shortest_path_length
|
| 138 |
+
"""
|
| 139 |
+
from collections import defaultdict
|
| 140 |
+
|
| 141 |
+
# dictionary-of-dictionaries representation for dist and pred
|
| 142 |
+
# use some defaultdict magick here
|
| 143 |
+
# for dist the default is the floating point inf value
|
| 144 |
+
dist = defaultdict(lambda: defaultdict(lambda: float("inf")))
|
| 145 |
+
for u in G:
|
| 146 |
+
dist[u][u] = 0
|
| 147 |
+
pred = defaultdict(dict)
|
| 148 |
+
# initialize path distance dictionary to be the adjacency matrix
|
| 149 |
+
# also set the distance to self to 0 (zero diagonal)
|
| 150 |
+
undirected = not G.is_directed()
|
| 151 |
+
for u, v, d in G.edges(data=True):
|
| 152 |
+
e_weight = d.get(weight, 1.0)
|
| 153 |
+
dist[u][v] = min(e_weight, dist[u][v])
|
| 154 |
+
pred[u][v] = u
|
| 155 |
+
if undirected:
|
| 156 |
+
dist[v][u] = min(e_weight, dist[v][u])
|
| 157 |
+
pred[v][u] = v
|
| 158 |
+
for w in G:
|
| 159 |
+
dist_w = dist[w] # save recomputation
|
| 160 |
+
for u in G:
|
| 161 |
+
dist_u = dist[u] # save recomputation
|
| 162 |
+
for v in G:
|
| 163 |
+
d = dist_u[w] + dist_w[v]
|
| 164 |
+
if dist_u[v] > d:
|
| 165 |
+
dist_u[v] = d
|
| 166 |
+
pred[u][v] = pred[w][v]
|
| 167 |
+
return dict(pred), dict(dist)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
@nx._dispatch(graphs=None)
|
| 171 |
+
def reconstruct_path(source, target, predecessors):
|
| 172 |
+
"""Reconstruct a path from source to target using the predecessors
|
| 173 |
+
dict as returned by floyd_warshall_predecessor_and_distance
|
| 174 |
+
|
| 175 |
+
Parameters
|
| 176 |
+
----------
|
| 177 |
+
source : node
|
| 178 |
+
Starting node for path
|
| 179 |
+
|
| 180 |
+
target : node
|
| 181 |
+
Ending node for path
|
| 182 |
+
|
| 183 |
+
predecessors: dictionary
|
| 184 |
+
Dictionary, keyed by source and target, of predecessors in the
|
| 185 |
+
shortest path, as returned by floyd_warshall_predecessor_and_distance
|
| 186 |
+
|
| 187 |
+
Returns
|
| 188 |
+
-------
|
| 189 |
+
path : list
|
| 190 |
+
A list of nodes containing the shortest path from source to target
|
| 191 |
+
|
| 192 |
+
If source and target are the same, an empty list is returned
|
| 193 |
+
|
| 194 |
+
Notes
|
| 195 |
+
-----
|
| 196 |
+
This function is meant to give more applicability to the
|
| 197 |
+
floyd_warshall_predecessor_and_distance function
|
| 198 |
+
|
| 199 |
+
See Also
|
| 200 |
+
--------
|
| 201 |
+
floyd_warshall_predecessor_and_distance
|
| 202 |
+
"""
|
| 203 |
+
if source == target:
|
| 204 |
+
return []
|
| 205 |
+
prev = predecessors[source]
|
| 206 |
+
curr = prev[target]
|
| 207 |
+
path = [target, curr]
|
| 208 |
+
while curr != source:
|
| 209 |
+
curr = prev[curr]
|
| 210 |
+
path.append(curr)
|
| 211 |
+
return list(reversed(path))
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
@nx._dispatch(edge_attrs="weight")
|
| 215 |
+
def floyd_warshall(G, weight="weight"):
|
| 216 |
+
"""Find all-pairs shortest path lengths using Floyd's algorithm.
|
| 217 |
+
|
| 218 |
+
Parameters
|
| 219 |
+
----------
|
| 220 |
+
G : NetworkX graph
|
| 221 |
+
|
| 222 |
+
weight: string, optional (default= 'weight')
|
| 223 |
+
Edge data key corresponding to the edge weight.
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
Returns
|
| 227 |
+
-------
|
| 228 |
+
distance : dict
|
| 229 |
+
A dictionary, keyed by source and target, of shortest paths distances
|
| 230 |
+
between nodes.
|
| 231 |
+
|
| 232 |
+
Examples
|
| 233 |
+
--------
|
| 234 |
+
>>> G = nx.DiGraph()
|
| 235 |
+
>>> G.add_weighted_edges_from([(0, 1, 5), (1, 2, 2), (2, 3, -3), (1, 3, 10), (3, 2, 8)])
|
| 236 |
+
>>> fw = nx.floyd_warshall(G, weight='weight')
|
| 237 |
+
>>> results = {a: dict(b) for a, b in fw.items()}
|
| 238 |
+
>>> print(results)
|
| 239 |
+
{0: {0: 0, 1: 5, 2: 7, 3: 4}, 1: {1: 0, 2: 2, 3: -1, 0: inf}, 2: {2: 0, 3: -3, 0: inf, 1: inf}, 3: {3: 0, 2: 8, 0: inf, 1: inf}}
|
| 240 |
+
|
| 241 |
+
Notes
|
| 242 |
+
-----
|
| 243 |
+
Floyd's algorithm is appropriate for finding shortest paths
|
| 244 |
+
in dense graphs or graphs with negative weights when Dijkstra's algorithm
|
| 245 |
+
fails. This algorithm can still fail if there are negative cycles.
|
| 246 |
+
It has running time $O(n^3)$ with running space of $O(n^2)$.
|
| 247 |
+
|
| 248 |
+
See Also
|
| 249 |
+
--------
|
| 250 |
+
floyd_warshall_predecessor_and_distance
|
| 251 |
+
floyd_warshall_numpy
|
| 252 |
+
all_pairs_shortest_path
|
| 253 |
+
all_pairs_shortest_path_length
|
| 254 |
+
"""
|
| 255 |
+
# could make this its own function to reduce memory costs
|
| 256 |
+
return floyd_warshall_predecessor_and_distance(G, weight=weight)[1]
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_dense.cpython-311.pyc
ADDED
|
Binary file (8.71 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_generic.cpython-311.pyc
ADDED
|
Binary file (34.2 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_unweighted.cpython-311.pyc
ADDED
|
Binary file (13 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/__pycache__/test_weighted.cpython-311.pyc
ADDED
|
Binary file (61.2 kB). View file
|
|
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
import networkx as nx
|
| 4 |
+
from networkx.utils import pairwise
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class TestAStar:
|
| 8 |
+
@classmethod
|
| 9 |
+
def setup_class(cls):
|
| 10 |
+
edges = [
|
| 11 |
+
("s", "u", 10),
|
| 12 |
+
("s", "x", 5),
|
| 13 |
+
("u", "v", 1),
|
| 14 |
+
("u", "x", 2),
|
| 15 |
+
("v", "y", 1),
|
| 16 |
+
("x", "u", 3),
|
| 17 |
+
("x", "v", 5),
|
| 18 |
+
("x", "y", 2),
|
| 19 |
+
("y", "s", 7),
|
| 20 |
+
("y", "v", 6),
|
| 21 |
+
]
|
| 22 |
+
cls.XG = nx.DiGraph()
|
| 23 |
+
cls.XG.add_weighted_edges_from(edges)
|
| 24 |
+
|
| 25 |
+
def test_multiple_optimal_paths(self):
|
| 26 |
+
"""Tests that A* algorithm finds any of multiple optimal paths"""
|
| 27 |
+
heuristic_values = {"a": 1.35, "b": 1.18, "c": 0.67, "d": 0}
|
| 28 |
+
|
| 29 |
+
def h(u, v):
|
| 30 |
+
return heuristic_values[u]
|
| 31 |
+
|
| 32 |
+
graph = nx.Graph()
|
| 33 |
+
points = ["a", "b", "c", "d"]
|
| 34 |
+
edges = [("a", "b", 0.18), ("a", "c", 0.68), ("b", "c", 0.50), ("c", "d", 0.67)]
|
| 35 |
+
|
| 36 |
+
graph.add_nodes_from(points)
|
| 37 |
+
graph.add_weighted_edges_from(edges)
|
| 38 |
+
|
| 39 |
+
path1 = ["a", "c", "d"]
|
| 40 |
+
path2 = ["a", "b", "c", "d"]
|
| 41 |
+
assert nx.astar_path(graph, "a", "d", h) in (path1, path2)
|
| 42 |
+
|
| 43 |
+
def test_astar_directed(self):
|
| 44 |
+
assert nx.astar_path(self.XG, "s", "v") == ["s", "x", "u", "v"]
|
| 45 |
+
assert nx.astar_path_length(self.XG, "s", "v") == 9
|
| 46 |
+
|
| 47 |
+
def test_astar_directed_weight_function(self):
|
| 48 |
+
w1 = lambda u, v, d: d["weight"]
|
| 49 |
+
assert nx.astar_path(self.XG, "x", "u", weight=w1) == ["x", "u"]
|
| 50 |
+
assert nx.astar_path_length(self.XG, "x", "u", weight=w1) == 3
|
| 51 |
+
assert nx.astar_path(self.XG, "s", "v", weight=w1) == ["s", "x", "u", "v"]
|
| 52 |
+
assert nx.astar_path_length(self.XG, "s", "v", weight=w1) == 9
|
| 53 |
+
|
| 54 |
+
w2 = lambda u, v, d: None if (u, v) == ("x", "u") else d["weight"]
|
| 55 |
+
assert nx.astar_path(self.XG, "x", "u", weight=w2) == ["x", "y", "s", "u"]
|
| 56 |
+
assert nx.astar_path_length(self.XG, "x", "u", weight=w2) == 19
|
| 57 |
+
assert nx.astar_path(self.XG, "s", "v", weight=w2) == ["s", "x", "v"]
|
| 58 |
+
assert nx.astar_path_length(self.XG, "s", "v", weight=w2) == 10
|
| 59 |
+
|
| 60 |
+
w3 = lambda u, v, d: d["weight"] + 10
|
| 61 |
+
assert nx.astar_path(self.XG, "x", "u", weight=w3) == ["x", "u"]
|
| 62 |
+
assert nx.astar_path_length(self.XG, "x", "u", weight=w3) == 13
|
| 63 |
+
assert nx.astar_path(self.XG, "s", "v", weight=w3) == ["s", "x", "v"]
|
| 64 |
+
assert nx.astar_path_length(self.XG, "s", "v", weight=w3) == 30
|
| 65 |
+
|
| 66 |
+
def test_astar_multigraph(self):
|
| 67 |
+
G = nx.MultiDiGraph(self.XG)
|
| 68 |
+
G.add_weighted_edges_from((u, v, 1000) for (u, v) in list(G.edges()))
|
| 69 |
+
assert nx.astar_path(G, "s", "v") == ["s", "x", "u", "v"]
|
| 70 |
+
assert nx.astar_path_length(G, "s", "v") == 9
|
| 71 |
+
|
| 72 |
+
def test_astar_undirected(self):
|
| 73 |
+
GG = self.XG.to_undirected()
|
| 74 |
+
# make sure we get lower weight
|
| 75 |
+
# to_undirected might choose either edge with weight 2 or weight 3
|
| 76 |
+
GG["u"]["x"]["weight"] = 2
|
| 77 |
+
GG["y"]["v"]["weight"] = 2
|
| 78 |
+
assert nx.astar_path(GG, "s", "v") == ["s", "x", "u", "v"]
|
| 79 |
+
assert nx.astar_path_length(GG, "s", "v") == 8
|
| 80 |
+
|
| 81 |
+
def test_astar_directed2(self):
|
| 82 |
+
XG2 = nx.DiGraph()
|
| 83 |
+
edges = [
|
| 84 |
+
(1, 4, 1),
|
| 85 |
+
(4, 5, 1),
|
| 86 |
+
(5, 6, 1),
|
| 87 |
+
(6, 3, 1),
|
| 88 |
+
(1, 3, 50),
|
| 89 |
+
(1, 2, 100),
|
| 90 |
+
(2, 3, 100),
|
| 91 |
+
]
|
| 92 |
+
XG2.add_weighted_edges_from(edges)
|
| 93 |
+
assert nx.astar_path(XG2, 1, 3) == [1, 4, 5, 6, 3]
|
| 94 |
+
|
| 95 |
+
def test_astar_undirected2(self):
|
| 96 |
+
XG3 = nx.Graph()
|
| 97 |
+
edges = [(0, 1, 2), (1, 2, 12), (2, 3, 1), (3, 4, 5), (4, 5, 1), (5, 0, 10)]
|
| 98 |
+
XG3.add_weighted_edges_from(edges)
|
| 99 |
+
assert nx.astar_path(XG3, 0, 3) == [0, 1, 2, 3]
|
| 100 |
+
assert nx.astar_path_length(XG3, 0, 3) == 15
|
| 101 |
+
|
| 102 |
+
def test_astar_undirected3(self):
|
| 103 |
+
XG4 = nx.Graph()
|
| 104 |
+
edges = [
|
| 105 |
+
(0, 1, 2),
|
| 106 |
+
(1, 2, 2),
|
| 107 |
+
(2, 3, 1),
|
| 108 |
+
(3, 4, 1),
|
| 109 |
+
(4, 5, 1),
|
| 110 |
+
(5, 6, 1),
|
| 111 |
+
(6, 7, 1),
|
| 112 |
+
(7, 0, 1),
|
| 113 |
+
]
|
| 114 |
+
XG4.add_weighted_edges_from(edges)
|
| 115 |
+
assert nx.astar_path(XG4, 0, 2) == [0, 1, 2]
|
| 116 |
+
assert nx.astar_path_length(XG4, 0, 2) == 4
|
| 117 |
+
|
| 118 |
+
""" Tests that A* finds correct path when multiple paths exist
|
| 119 |
+
and the best one is not expanded first (GH issue #3464)
|
| 120 |
+
"""
|
| 121 |
+
|
| 122 |
+
def test_astar_directed3(self):
|
| 123 |
+
heuristic_values = {"n5": 36, "n2": 4, "n1": 0, "n0": 0}
|
| 124 |
+
|
| 125 |
+
def h(u, v):
|
| 126 |
+
return heuristic_values[u]
|
| 127 |
+
|
| 128 |
+
edges = [("n5", "n1", 11), ("n5", "n2", 9), ("n2", "n1", 1), ("n1", "n0", 32)]
|
| 129 |
+
graph = nx.DiGraph()
|
| 130 |
+
graph.add_weighted_edges_from(edges)
|
| 131 |
+
answer = ["n5", "n2", "n1", "n0"]
|
| 132 |
+
assert nx.astar_path(graph, "n5", "n0", h) == answer
|
| 133 |
+
|
| 134 |
+
""" Tests that parent is not wrongly overridden when a node
|
| 135 |
+
is re-explored multiple times.
|
| 136 |
+
"""
|
| 137 |
+
|
| 138 |
+
def test_astar_directed4(self):
|
| 139 |
+
edges = [
|
| 140 |
+
("a", "b", 1),
|
| 141 |
+
("a", "c", 1),
|
| 142 |
+
("b", "d", 2),
|
| 143 |
+
("c", "d", 1),
|
| 144 |
+
("d", "e", 1),
|
| 145 |
+
]
|
| 146 |
+
graph = nx.DiGraph()
|
| 147 |
+
graph.add_weighted_edges_from(edges)
|
| 148 |
+
assert nx.astar_path(graph, "a", "e") == ["a", "c", "d", "e"]
|
| 149 |
+
|
| 150 |
+
# >>> MXG4=NX.MultiGraph(XG4)
|
| 151 |
+
# >>> MXG4.add_edge(0,1,3)
|
| 152 |
+
# >>> NX.dijkstra_path(MXG4,0,2)
|
| 153 |
+
# [0, 1, 2]
|
| 154 |
+
|
| 155 |
+
def test_astar_w1(self):
|
| 156 |
+
G = nx.DiGraph()
|
| 157 |
+
G.add_edges_from(
|
| 158 |
+
[
|
| 159 |
+
("s", "u"),
|
| 160 |
+
("s", "x"),
|
| 161 |
+
("u", "v"),
|
| 162 |
+
("u", "x"),
|
| 163 |
+
("v", "y"),
|
| 164 |
+
("x", "u"),
|
| 165 |
+
("x", "w"),
|
| 166 |
+
("w", "v"),
|
| 167 |
+
("x", "y"),
|
| 168 |
+
("y", "s"),
|
| 169 |
+
("y", "v"),
|
| 170 |
+
]
|
| 171 |
+
)
|
| 172 |
+
assert nx.astar_path(G, "s", "v") == ["s", "u", "v"]
|
| 173 |
+
assert nx.astar_path_length(G, "s", "v") == 2
|
| 174 |
+
|
| 175 |
+
def test_astar_nopath(self):
|
| 176 |
+
with pytest.raises(nx.NodeNotFound):
|
| 177 |
+
nx.astar_path(self.XG, "s", "moon")
|
| 178 |
+
|
| 179 |
+
def test_cycle(self):
|
| 180 |
+
C = nx.cycle_graph(7)
|
| 181 |
+
assert nx.astar_path(C, 0, 3) == [0, 1, 2, 3]
|
| 182 |
+
assert nx.dijkstra_path(C, 0, 4) == [0, 6, 5, 4]
|
| 183 |
+
|
| 184 |
+
def test_unorderable_nodes(self):
|
| 185 |
+
"""Tests that A* accommodates nodes that are not orderable.
|
| 186 |
+
|
| 187 |
+
For more information, see issue #554.
|
| 188 |
+
|
| 189 |
+
"""
|
| 190 |
+
# Create the cycle graph on four nodes, with nodes represented
|
| 191 |
+
# as (unorderable) Python objects.
|
| 192 |
+
nodes = [object() for n in range(4)]
|
| 193 |
+
G = nx.Graph()
|
| 194 |
+
G.add_edges_from(pairwise(nodes, cyclic=True))
|
| 195 |
+
path = nx.astar_path(G, nodes[0], nodes[2])
|
| 196 |
+
assert len(path) == 3
|
| 197 |
+
|
| 198 |
+
def test_astar_NetworkXNoPath(self):
|
| 199 |
+
"""Tests that exception is raised when there exists no
|
| 200 |
+
path between source and target"""
|
| 201 |
+
G = nx.gnp_random_graph(10, 0.2, seed=10)
|
| 202 |
+
with pytest.raises(nx.NetworkXNoPath):
|
| 203 |
+
nx.astar_path(G, 4, 9)
|
| 204 |
+
|
| 205 |
+
def test_astar_NodeNotFound(self):
|
| 206 |
+
"""Tests that exception is raised when either
|
| 207 |
+
source or target is not in graph"""
|
| 208 |
+
G = nx.gnp_random_graph(10, 0.2, seed=10)
|
| 209 |
+
with pytest.raises(nx.NodeNotFound):
|
| 210 |
+
nx.astar_path_length(G, 11, 9)
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
np = pytest.importorskip("numpy")
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
import networkx as nx
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def test_cycle_numpy():
|
| 10 |
+
dist = nx.floyd_warshall_numpy(nx.cycle_graph(7))
|
| 11 |
+
assert dist[0, 3] == 3
|
| 12 |
+
assert dist[0, 4] == 3
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def test_weighted_numpy_three_edges():
|
| 16 |
+
XG3 = nx.Graph()
|
| 17 |
+
XG3.add_weighted_edges_from(
|
| 18 |
+
[[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
|
| 19 |
+
)
|
| 20 |
+
dist = nx.floyd_warshall_numpy(XG3)
|
| 21 |
+
assert dist[0, 3] == 15
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def test_weighted_numpy_two_edges():
|
| 25 |
+
XG4 = nx.Graph()
|
| 26 |
+
XG4.add_weighted_edges_from(
|
| 27 |
+
[
|
| 28 |
+
[0, 1, 2],
|
| 29 |
+
[1, 2, 2],
|
| 30 |
+
[2, 3, 1],
|
| 31 |
+
[3, 4, 1],
|
| 32 |
+
[4, 5, 1],
|
| 33 |
+
[5, 6, 1],
|
| 34 |
+
[6, 7, 1],
|
| 35 |
+
[7, 0, 1],
|
| 36 |
+
]
|
| 37 |
+
)
|
| 38 |
+
dist = nx.floyd_warshall_numpy(XG4)
|
| 39 |
+
assert dist[0, 2] == 4
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_weight_parameter_numpy():
|
| 43 |
+
XG4 = nx.Graph()
|
| 44 |
+
XG4.add_edges_from(
|
| 45 |
+
[
|
| 46 |
+
(0, 1, {"heavy": 2}),
|
| 47 |
+
(1, 2, {"heavy": 2}),
|
| 48 |
+
(2, 3, {"heavy": 1}),
|
| 49 |
+
(3, 4, {"heavy": 1}),
|
| 50 |
+
(4, 5, {"heavy": 1}),
|
| 51 |
+
(5, 6, {"heavy": 1}),
|
| 52 |
+
(6, 7, {"heavy": 1}),
|
| 53 |
+
(7, 0, {"heavy": 1}),
|
| 54 |
+
]
|
| 55 |
+
)
|
| 56 |
+
dist = nx.floyd_warshall_numpy(XG4, weight="heavy")
|
| 57 |
+
assert dist[0, 2] == 4
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def test_directed_cycle_numpy():
|
| 61 |
+
G = nx.DiGraph()
|
| 62 |
+
nx.add_cycle(G, [0, 1, 2, 3])
|
| 63 |
+
pred, dist = nx.floyd_warshall_predecessor_and_distance(G)
|
| 64 |
+
D = nx.utils.dict_to_numpy_array(dist)
|
| 65 |
+
np.testing.assert_equal(nx.floyd_warshall_numpy(G), D)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_zero_weight():
|
| 69 |
+
G = nx.DiGraph()
|
| 70 |
+
edges = [(1, 2, -2), (2, 3, -4), (1, 5, 1), (5, 4, 0), (4, 3, -5), (2, 5, -7)]
|
| 71 |
+
G.add_weighted_edges_from(edges)
|
| 72 |
+
dist = nx.floyd_warshall_numpy(G)
|
| 73 |
+
assert int(np.min(dist)) == -14
|
| 74 |
+
|
| 75 |
+
G = nx.MultiDiGraph()
|
| 76 |
+
edges.append((2, 5, -7))
|
| 77 |
+
G.add_weighted_edges_from(edges)
|
| 78 |
+
dist = nx.floyd_warshall_numpy(G)
|
| 79 |
+
assert int(np.min(dist)) == -14
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_nodelist():
|
| 83 |
+
G = nx.path_graph(7)
|
| 84 |
+
dist = nx.floyd_warshall_numpy(G, nodelist=[3, 5, 4, 6, 2, 1, 0])
|
| 85 |
+
assert dist[0, 3] == 3
|
| 86 |
+
assert dist[0, 1] == 2
|
| 87 |
+
assert dist[6, 2] == 4
|
| 88 |
+
pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, [1, 3])
|
| 89 |
+
pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, list(range(9)))
|
tuning-competition-baseline/.venv/lib/python3.11/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
|
| 3 |
+
import networkx as nx
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def validate_grid_path(r, c, s, t, p):
|
| 7 |
+
assert isinstance(p, list)
|
| 8 |
+
assert p[0] == s
|
| 9 |
+
assert p[-1] == t
|
| 10 |
+
s = ((s - 1) // c, (s - 1) % c)
|
| 11 |
+
t = ((t - 1) // c, (t - 1) % c)
|
| 12 |
+
assert len(p) == abs(t[0] - s[0]) + abs(t[1] - s[1]) + 1
|
| 13 |
+
p = [((u - 1) // c, (u - 1) % c) for u in p]
|
| 14 |
+
for u in p:
|
| 15 |
+
assert 0 <= u[0] < r
|
| 16 |
+
assert 0 <= u[1] < c
|
| 17 |
+
for u, v in zip(p[:-1], p[1:]):
|
| 18 |
+
assert (abs(v[0] - u[0]), abs(v[1] - u[1])) in [(0, 1), (1, 0)]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class TestUnweightedPath:
|
| 22 |
+
@classmethod
|
| 23 |
+
def setup_class(cls):
|
| 24 |
+
from networkx import convert_node_labels_to_integers as cnlti
|
| 25 |
+
|
| 26 |
+
cls.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
|
| 27 |
+
cls.cycle = nx.cycle_graph(7)
|
| 28 |
+
cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
|
| 29 |
+
|
| 30 |
+
def test_bidirectional_shortest_path(self):
|
| 31 |
+
assert nx.bidirectional_shortest_path(self.cycle, 0, 3) == [0, 1, 2, 3]
|
| 32 |
+
assert nx.bidirectional_shortest_path(self.cycle, 0, 4) == [0, 6, 5, 4]
|
| 33 |
+
validate_grid_path(
|
| 34 |
+
4, 4, 1, 12, nx.bidirectional_shortest_path(self.grid, 1, 12)
|
| 35 |
+
)
|
| 36 |
+
assert nx.bidirectional_shortest_path(self.directed_cycle, 0, 3) == [0, 1, 2, 3]
|
| 37 |
+
# test source = target
|
| 38 |
+
assert nx.bidirectional_shortest_path(self.cycle, 3, 3) == [3]
|
| 39 |
+
|
| 40 |
+
@pytest.mark.parametrize(
|
| 41 |
+
("src", "tgt"),
|
| 42 |
+
(
|
| 43 |
+
(8, 3), # source not in graph
|
| 44 |
+
(3, 8), # target not in graph
|
| 45 |
+
(8, 10), # neither source nor target in graph
|
| 46 |
+
(8, 8), # src == tgt, neither in graph - tests order of input checks
|
| 47 |
+
),
|
| 48 |
+
)
|
| 49 |
+
def test_bidirectional_shortest_path_src_tgt_not_in_graph(self, src, tgt):
|
| 50 |
+
with pytest.raises(
|
| 51 |
+
nx.NodeNotFound,
|
| 52 |
+
match=f"Either source {src} or target {tgt} is not in G",
|
| 53 |
+
):
|
| 54 |
+
nx.bidirectional_shortest_path(self.cycle, src, tgt)
|
| 55 |
+
|
| 56 |
+
def test_shortest_path_length(self):
|
| 57 |
+
assert nx.shortest_path_length(self.cycle, 0, 3) == 3
|
| 58 |
+
assert nx.shortest_path_length(self.grid, 1, 12) == 5
|
| 59 |
+
assert nx.shortest_path_length(self.directed_cycle, 0, 4) == 4
|
| 60 |
+
# now with weights
|
| 61 |
+
assert nx.shortest_path_length(self.cycle, 0, 3, weight=True) == 3
|
| 62 |
+
assert nx.shortest_path_length(self.grid, 1, 12, weight=True) == 5
|
| 63 |
+
assert nx.shortest_path_length(self.directed_cycle, 0, 4, weight=True) == 4
|
| 64 |
+
|
| 65 |
+
def test_single_source_shortest_path(self):
|
| 66 |
+
p = nx.single_source_shortest_path(self.directed_cycle, 3)
|
| 67 |
+
assert p[0] == [3, 4, 5, 6, 0]
|
| 68 |
+
p = nx.single_source_shortest_path(self.cycle, 0)
|
| 69 |
+
assert p[3] == [0, 1, 2, 3]
|
| 70 |
+
p = nx.single_source_shortest_path(self.cycle, 0, cutoff=0)
|
| 71 |
+
assert p == {0: [0]}
|
| 72 |
+
|
| 73 |
+
def test_single_source_shortest_path_length(self):
|
| 74 |
+
pl = nx.single_source_shortest_path_length
|
| 75 |
+
lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
|
| 76 |
+
assert dict(pl(self.cycle, 0)) == lengths
|
| 77 |
+
lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
|
| 78 |
+
assert dict(pl(self.directed_cycle, 0)) == lengths
|
| 79 |
+
|
| 80 |
+
def test_single_target_shortest_path(self):
|
| 81 |
+
p = nx.single_target_shortest_path(self.directed_cycle, 0)
|
| 82 |
+
assert p[3] == [3, 4, 5, 6, 0]
|
| 83 |
+
p = nx.single_target_shortest_path(self.cycle, 0)
|
| 84 |
+
assert p[3] == [3, 2, 1, 0]
|
| 85 |
+
p = nx.single_target_shortest_path(self.cycle, 0, cutoff=0)
|
| 86 |
+
assert p == {0: [0]}
|
| 87 |
+
# test missing targets
|
| 88 |
+
target = 8
|
| 89 |
+
with pytest.raises(nx.NodeNotFound, match=f"Target {target} not in G"):
|
| 90 |
+
nx.single_target_shortest_path(self.cycle, target)
|
| 91 |
+
|
| 92 |
+
def test_single_target_shortest_path_length(self):
|
| 93 |
+
pl = nx.single_target_shortest_path_length
|
| 94 |
+
lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
|
| 95 |
+
assert dict(pl(self.cycle, 0)) == lengths
|
| 96 |
+
lengths = {0: 0, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1}
|
| 97 |
+
assert dict(pl(self.directed_cycle, 0)) == lengths
|
| 98 |
+
# test missing targets
|
| 99 |
+
target = 8
|
| 100 |
+
with pytest.raises(nx.NodeNotFound, match=f"Target {target} is not in G"):
|
| 101 |
+
nx.single_target_shortest_path_length(self.cycle, target)
|
| 102 |
+
|
| 103 |
+
def test_all_pairs_shortest_path(self):
|
| 104 |
+
p = dict(nx.all_pairs_shortest_path(self.cycle))
|
| 105 |
+
assert p[0][3] == [0, 1, 2, 3]
|
| 106 |
+
p = dict(nx.all_pairs_shortest_path(self.grid))
|
| 107 |
+
validate_grid_path(4, 4, 1, 12, p[1][12])
|
| 108 |
+
|
| 109 |
+
def test_all_pairs_shortest_path_length(self):
|
| 110 |
+
l = dict(nx.all_pairs_shortest_path_length(self.cycle))
|
| 111 |
+
assert l[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
|
| 112 |
+
l = dict(nx.all_pairs_shortest_path_length(self.grid))
|
| 113 |
+
assert l[1][16] == 6
|
| 114 |
+
|
| 115 |
+
def test_predecessor_path(self):
|
| 116 |
+
G = nx.path_graph(4)
|
| 117 |
+
assert nx.predecessor(G, 0) == {0: [], 1: [0], 2: [1], 3: [2]}
|
| 118 |
+
assert nx.predecessor(G, 0, 3) == [2]
|
| 119 |
+
|
| 120 |
+
def test_predecessor_cycle(self):
|
| 121 |
+
G = nx.cycle_graph(4)
|
| 122 |
+
pred = nx.predecessor(G, 0)
|
| 123 |
+
assert pred[0] == []
|
| 124 |
+
assert pred[1] == [0]
|
| 125 |
+
assert pred[2] in [[1, 3], [3, 1]]
|
| 126 |
+
assert pred[3] == [0]
|
| 127 |
+
|
| 128 |
+
def test_predecessor_cutoff(self):
|
| 129 |
+
G = nx.path_graph(4)
|
| 130 |
+
p = nx.predecessor(G, 0, 3)
|
| 131 |
+
assert 4 not in p
|
| 132 |
+
|
| 133 |
+
def test_predecessor_target(self):
|
| 134 |
+
G = nx.path_graph(4)
|
| 135 |
+
p = nx.predecessor(G, 0, 3)
|
| 136 |
+
assert p == [2]
|
| 137 |
+
p = nx.predecessor(G, 0, 3, cutoff=2)
|
| 138 |
+
assert p == []
|
| 139 |
+
p, s = nx.predecessor(G, 0, 3, return_seen=True)
|
| 140 |
+
assert p == [2]
|
| 141 |
+
assert s == 3
|
| 142 |
+
p, s = nx.predecessor(G, 0, 3, cutoff=2, return_seen=True)
|
| 143 |
+
assert p == []
|
| 144 |
+
assert s == -1
|
| 145 |
+
|
| 146 |
+
def test_predecessor_missing_source(self):
|
| 147 |
+
source = 8
|
| 148 |
+
with pytest.raises(nx.NodeNotFound, match=f"Source {source} not in G"):
|
| 149 |
+
nx.predecessor(self.cycle, source)
|