update
This commit is contained in:
33
.CondaPkg/env/Lib/site-packages/networkx/generators/__init__.py
vendored
Normal file
33
.CondaPkg/env/Lib/site-packages/networkx/generators/__init__.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
A package for generating various graphs in networkx.
|
||||
|
||||
"""
|
||||
from networkx.generators.atlas import *
|
||||
from networkx.generators.classic import *
|
||||
from networkx.generators.cographs import *
|
||||
from networkx.generators.community import *
|
||||
from networkx.generators.degree_seq import *
|
||||
from networkx.generators.directed import *
|
||||
from networkx.generators.duplication import *
|
||||
from networkx.generators.ego import *
|
||||
from networkx.generators.expanders import *
|
||||
from networkx.generators.geometric import *
|
||||
from networkx.generators.harary_graph import *
|
||||
from networkx.generators.internet_as_graphs import *
|
||||
from networkx.generators.intersection import *
|
||||
from networkx.generators.interval_graph import *
|
||||
from networkx.generators.joint_degree_seq import *
|
||||
from networkx.generators.lattice import *
|
||||
from networkx.generators.line import *
|
||||
from networkx.generators.mycielski import *
|
||||
from networkx.generators.nonisomorphic_trees import *
|
||||
from networkx.generators.random_clustered import *
|
||||
from networkx.generators.random_graphs import *
|
||||
from networkx.generators.small import *
|
||||
from networkx.generators.social import *
|
||||
from networkx.generators.spectral_graph_forge import *
|
||||
from networkx.generators.stochastic import *
|
||||
from networkx.generators.sudoku import *
|
||||
from networkx.generators.time_series import *
|
||||
from networkx.generators.trees import *
|
||||
from networkx.generators.triads import *
|
||||
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/atlas.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/atlas.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/classic.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/classic.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/cographs.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/cographs.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/community.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/community.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/degree_seq.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/degree_seq.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/directed.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/directed.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/duplication.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/duplication.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/ego.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/ego.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/expanders.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/expanders.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/geometric.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/geometric.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/harary_graph.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/harary_graph.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/internet_as_graphs.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/internet_as_graphs.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/intersection.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/intersection.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/interval_graph.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/interval_graph.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/joint_degree_seq.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/joint_degree_seq.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/lattice.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/lattice.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/line.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/line.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/mycielski.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/mycielski.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/nonisomorphic_trees.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/nonisomorphic_trees.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/random_clustered.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/random_clustered.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/random_graphs.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/random_graphs.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/small.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/small.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/social.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/social.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/spectral_graph_forge.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/spectral_graph_forge.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/stochastic.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/stochastic.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/sudoku.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/sudoku.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/time_series.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/time_series.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/trees.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/trees.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/triads.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/__pycache__/triads.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/atlas.dat.gz
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/atlas.dat.gz
vendored
Normal file
Binary file not shown.
179
.CondaPkg/env/Lib/site-packages/networkx/generators/atlas.py
vendored
Normal file
179
.CondaPkg/env/Lib/site-packages/networkx/generators/atlas.py
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Generators for the small graph atlas.
|
||||
"""
|
||||
import gzip
|
||||
import importlib.resources
|
||||
import os
|
||||
import os.path
|
||||
from itertools import islice
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["graph_atlas", "graph_atlas_g"]
|
||||
|
||||
#: The total number of graphs in the atlas.
|
||||
#:
|
||||
#: The graphs are labeled starting from 0 and extending to (but not
|
||||
#: including) this number.
|
||||
NUM_GRAPHS = 1253
|
||||
|
||||
#: The path to the data file containing the graph edge lists.
|
||||
#:
|
||||
#: This is the absolute path of the gzipped text file containing the
|
||||
#: edge list for each graph in the atlas. The file contains one entry
|
||||
#: per graph in the atlas, in sequential order, starting from graph
|
||||
#: number 0 and extending through graph number 1252 (see
|
||||
#: :data:`NUM_GRAPHS`). Each entry looks like
|
||||
#:
|
||||
#: .. sourcecode:: text
|
||||
#:
|
||||
#: GRAPH 6
|
||||
#: NODES 3
|
||||
#: 0 1
|
||||
#: 0 2
|
||||
#:
|
||||
#: where the first two lines are the graph's index in the atlas and the
|
||||
#: number of nodes in the graph, and the remaining lines are the edge
|
||||
#: list.
|
||||
#:
|
||||
#: This file was generated from a Python list of graphs via code like
|
||||
#: the following::
|
||||
#:
|
||||
#: import gzip
|
||||
#: from networkx.generators.atlas import graph_atlas_g
|
||||
#: from networkx.readwrite.edgelist import write_edgelist
|
||||
#:
|
||||
#: with gzip.open('atlas.dat.gz', 'wb') as f:
|
||||
#: for i, G in enumerate(graph_atlas_g()):
|
||||
#: f.write(bytes(f'GRAPH {i}\n', encoding='utf-8'))
|
||||
#: f.write(bytes(f'NODES {len(G)}\n', encoding='utf-8'))
|
||||
#: write_edgelist(G, f, data=False)
|
||||
#:
|
||||
|
||||
# Path to the atlas file
|
||||
ATLAS_FILE = importlib.resources.files("networkx.generators") / "atlas.dat.gz"
|
||||
|
||||
|
||||
def _generate_graphs():
|
||||
"""Sequentially read the file containing the edge list data for the
|
||||
graphs in the atlas and generate the graphs one at a time.
|
||||
|
||||
This function reads the file given in :data:`.ATLAS_FILE`.
|
||||
|
||||
"""
|
||||
with gzip.open(ATLAS_FILE, "rb") as f:
|
||||
line = f.readline()
|
||||
while line and line.startswith(b"GRAPH"):
|
||||
# The first two lines of each entry tell us the index of the
|
||||
# graph in the list and the number of nodes in the graph.
|
||||
# They look like this:
|
||||
#
|
||||
# GRAPH 3
|
||||
# NODES 2
|
||||
#
|
||||
graph_index = int(line[6:].rstrip())
|
||||
line = f.readline()
|
||||
num_nodes = int(line[6:].rstrip())
|
||||
# The remaining lines contain the edge list, until the next
|
||||
# GRAPH line (or until the end of the file).
|
||||
edgelist = []
|
||||
line = f.readline()
|
||||
while line and not line.startswith(b"GRAPH"):
|
||||
edgelist.append(line.rstrip())
|
||||
line = f.readline()
|
||||
G = nx.Graph()
|
||||
G.name = f"G{graph_index}"
|
||||
G.add_nodes_from(range(num_nodes))
|
||||
G.add_edges_from(tuple(map(int, e.split())) for e in edgelist)
|
||||
yield G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def graph_atlas(i):
|
||||
"""Returns graph number `i` from the Graph Atlas.
|
||||
|
||||
For more information, see :func:`.graph_atlas_g`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i : int
|
||||
The index of the graph from the atlas to get. The graph at index
|
||||
0 is assumed to be the null graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
A list of :class:`~networkx.Graph` objects, the one at index *i*
|
||||
corresponding to the graph *i* in the Graph Atlas.
|
||||
|
||||
See also
|
||||
--------
|
||||
graph_atlas_g
|
||||
|
||||
Notes
|
||||
-----
|
||||
The time required by this function increases linearly with the
|
||||
argument `i`, since it reads a large file sequentially in order to
|
||||
generate the graph [1]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ronald C. Read and Robin J. Wilson, *An Atlas of Graphs*.
|
||||
Oxford University Press, 1998.
|
||||
|
||||
"""
|
||||
if not (0 <= i < NUM_GRAPHS):
|
||||
raise ValueError(f"index must be between 0 and {NUM_GRAPHS}")
|
||||
return next(islice(_generate_graphs(), i, None))
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def graph_atlas_g():
|
||||
"""Returns the list of all graphs with up to seven nodes named in the
|
||||
Graph Atlas.
|
||||
|
||||
The graphs are listed in increasing order by
|
||||
|
||||
1. number of nodes,
|
||||
2. number of edges,
|
||||
3. degree sequence (for example 111223 < 112222),
|
||||
4. number of automorphisms,
|
||||
|
||||
in that order, with three exceptions as described in the *Notes*
|
||||
section below. This causes the list to correspond with the index of
|
||||
the graphs in the Graph Atlas [atlas]_, with the first graph,
|
||||
``G[0]``, being the null graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
A list of :class:`~networkx.Graph` objects, the one at index *i*
|
||||
corresponding to the graph *i* in the Graph Atlas.
|
||||
|
||||
See also
|
||||
--------
|
||||
graph_atlas
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function may be expensive in both time and space, since it
|
||||
reads a large file sequentially in order to populate the list.
|
||||
|
||||
Although the NetworkX atlas functions match the order of graphs
|
||||
given in the "Atlas of Graphs" book, there are (at least) three
|
||||
errors in the ordering described in the book. The following three
|
||||
pairs of nodes violate the lexicographically nondecreasing sorted
|
||||
degree sequence rule:
|
||||
|
||||
- graphs 55 and 56 with degree sequences 001111 and 000112,
|
||||
- graphs 1007 and 1008 with degree sequences 3333444 and 3333336,
|
||||
- graphs 1012 and 1213 with degree sequences 1244555 and 1244456.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [atlas] Ronald C. Read and Robin J. Wilson,
|
||||
*An Atlas of Graphs*.
|
||||
Oxford University Press, 1998.
|
||||
|
||||
"""
|
||||
return list(_generate_graphs())
|
||||
1054
.CondaPkg/env/Lib/site-packages/networkx/generators/classic.py
vendored
Normal file
1054
.CondaPkg/env/Lib/site-packages/networkx/generators/classic.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
.CondaPkg/env/Lib/site-packages/networkx/generators/cographs.py
vendored
Normal file
67
.CondaPkg/env/Lib/site-packages/networkx/generators/cographs.py
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
r"""Generators for cographs
|
||||
|
||||
A cograph is a graph containing no path on four vertices.
|
||||
Cographs or $P_4$-free graphs can be obtained from a single vertex
|
||||
by disjoint union and complementation operations.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [0] D.G. Corneil, H. Lerchs, L.Stewart Burlingham,
|
||||
"Complement reducible graphs",
|
||||
Discrete Applied Mathematics, Volume 3, Issue 3, 1981, Pages 163-174,
|
||||
ISSN 0166-218X.
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = ["random_cograph"]
|
||||
|
||||
|
||||
@py_random_state(1)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_cograph(n, seed=None):
|
||||
r"""Returns a random cograph with $2 ^ n$ nodes.
|
||||
|
||||
A cograph is a graph containing no path on four vertices.
|
||||
Cographs or $P_4$-free graphs can be obtained from a single vertex
|
||||
by disjoint union and complementation operations.
|
||||
|
||||
This generator starts off from a single vertex and performs disjoint
|
||||
union and full join operations on itself.
|
||||
The decision on which operation will take place is random.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The order of the cograph.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : A random graph containing no path on four vertices.
|
||||
|
||||
See Also
|
||||
--------
|
||||
full_join
|
||||
union
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] D.G. Corneil, H. Lerchs, L.Stewart Burlingham,
|
||||
"Complement reducible graphs",
|
||||
Discrete Applied Mathematics, Volume 3, Issue 3, 1981, Pages 163-174,
|
||||
ISSN 0166-218X.
|
||||
"""
|
||||
R = nx.empty_graph(1)
|
||||
|
||||
for i in range(n):
|
||||
RR = nx.relabel_nodes(R.copy(), lambda x: x + len(R))
|
||||
|
||||
if seed.randint(0, 1) == 0:
|
||||
R = nx.full_join(R, RR)
|
||||
else:
|
||||
R = nx.disjoint_union(R, RR)
|
||||
|
||||
return R
|
||||
1069
.CondaPkg/env/Lib/site-packages/networkx/generators/community.py
vendored
Normal file
1069
.CondaPkg/env/Lib/site-packages/networkx/generators/community.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
868
.CondaPkg/env/Lib/site-packages/networkx/generators/degree_seq.py
vendored
Normal file
868
.CondaPkg/env/Lib/site-packages/networkx/generators/degree_seq.py
vendored
Normal file
@@ -0,0 +1,868 @@
|
||||
"""Generate graphs with a given degree sequence or expected degree sequence.
|
||||
"""
|
||||
|
||||
import heapq
|
||||
import math
|
||||
from itertools import chain, combinations, zip_longest
|
||||
from operator import itemgetter
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state, random_weighted_sample
|
||||
|
||||
__all__ = [
|
||||
"configuration_model",
|
||||
"directed_configuration_model",
|
||||
"expected_degree_graph",
|
||||
"havel_hakimi_graph",
|
||||
"directed_havel_hakimi_graph",
|
||||
"degree_sequence_tree",
|
||||
"random_degree_sequence_graph",
|
||||
]
|
||||
|
||||
chaini = chain.from_iterable
|
||||
|
||||
|
||||
def _to_stublist(degree_sequence):
|
||||
"""Returns a list of degree-repeated node numbers.
|
||||
|
||||
``degree_sequence`` is a list of nonnegative integers representing
|
||||
the degrees of nodes in a graph.
|
||||
|
||||
This function returns a list of node numbers with multiplicities
|
||||
according to the given degree sequence. For example, if the first
|
||||
element of ``degree_sequence`` is ``3``, then the first node number,
|
||||
``0``, will appear at the head of the returned list three times. The
|
||||
node numbers are assumed to be the numbers zero through
|
||||
``len(degree_sequence) - 1``.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> degree_sequence = [1, 2, 3]
|
||||
>>> _to_stublist(degree_sequence)
|
||||
[0, 1, 1, 2, 2, 2]
|
||||
|
||||
If a zero appears in the sequence, that means the node exists but
|
||||
has degree zero, so that number will be skipped in the returned
|
||||
list::
|
||||
|
||||
>>> degree_sequence = [2, 0, 1]
|
||||
>>> _to_stublist(degree_sequence)
|
||||
[0, 0, 2]
|
||||
|
||||
"""
|
||||
return list(chaini([n] * d for n, d in enumerate(degree_sequence)))
|
||||
|
||||
|
||||
def _configuration_model(
|
||||
deg_sequence, create_using, directed=False, in_deg_sequence=None, seed=None
|
||||
):
|
||||
"""Helper function for generating either undirected or directed
|
||||
configuration model graphs.
|
||||
|
||||
``deg_sequence`` is a list of nonnegative integers representing the
|
||||
degree of the node whose label is the index of the list element.
|
||||
|
||||
``create_using`` see :func:`~networkx.empty_graph`.
|
||||
|
||||
``directed`` and ``in_deg_sequence`` are required if you want the
|
||||
returned graph to be generated using the directed configuration
|
||||
model algorithm. If ``directed`` is ``False``, then ``deg_sequence``
|
||||
is interpreted as the degree sequence of an undirected graph and
|
||||
``in_deg_sequence`` is ignored. Otherwise, if ``directed`` is
|
||||
``True``, then ``deg_sequence`` is interpreted as the out-degree
|
||||
sequence and ``in_deg_sequence`` as the in-degree sequence of a
|
||||
directed graph.
|
||||
|
||||
.. note::
|
||||
|
||||
``deg_sequence`` and ``in_deg_sequence`` need not be the same
|
||||
length.
|
||||
|
||||
``seed`` is a random.Random or numpy.random.RandomState instance
|
||||
|
||||
This function returns a graph, directed if and only if ``directed``
|
||||
is ``True``, generated according to the configuration model
|
||||
algorithm. For more information on the algorithm, see the
|
||||
:func:`configuration_model` or :func:`directed_configuration_model`
|
||||
functions.
|
||||
|
||||
"""
|
||||
n = len(deg_sequence)
|
||||
G = nx.empty_graph(n, create_using)
|
||||
# If empty, return the null graph immediately.
|
||||
if n == 0:
|
||||
return G
|
||||
# Build a list of available degree-repeated nodes. For example,
|
||||
# for degree sequence [3, 2, 1, 1, 1], the "stub list" is
|
||||
# initially [0, 0, 0, 1, 1, 2, 3, 4], that is, node 0 has degree
|
||||
# 3 and thus is repeated 3 times, etc.
|
||||
#
|
||||
# Also, shuffle the stub list in order to get a random sequence of
|
||||
# node pairs.
|
||||
if directed:
|
||||
pairs = zip_longest(deg_sequence, in_deg_sequence, fillvalue=0)
|
||||
# Unzip the list of pairs into a pair of lists.
|
||||
out_deg, in_deg = zip(*pairs)
|
||||
|
||||
out_stublist = _to_stublist(out_deg)
|
||||
in_stublist = _to_stublist(in_deg)
|
||||
|
||||
seed.shuffle(out_stublist)
|
||||
seed.shuffle(in_stublist)
|
||||
else:
|
||||
stublist = _to_stublist(deg_sequence)
|
||||
# Choose a random balanced bipartition of the stublist, which
|
||||
# gives a random pairing of nodes. In this implementation, we
|
||||
# shuffle the list and then split it in half.
|
||||
n = len(stublist)
|
||||
half = n // 2
|
||||
seed.shuffle(stublist)
|
||||
out_stublist, in_stublist = stublist[:half], stublist[half:]
|
||||
G.add_edges_from(zip(out_stublist, in_stublist))
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(2)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def configuration_model(deg_sequence, create_using=None, seed=None):
|
||||
"""Returns a random graph with the given degree sequence.
|
||||
|
||||
The configuration model generates a random pseudograph (graph with
|
||||
parallel edges and self loops) by randomly assigning edges to
|
||||
match the given degree sequence.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
deg_sequence : list of nonnegative integers
|
||||
Each list entry corresponds to the degree of a node.
|
||||
create_using : NetworkX graph constructor, optional (default MultiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : MultiGraph
|
||||
A graph with the specified degree sequence.
|
||||
Nodes are labeled starting at 0 with an index
|
||||
corresponding to the position in deg_sequence.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the degree sequence does not have an even sum.
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_graphical
|
||||
|
||||
Notes
|
||||
-----
|
||||
As described by Newman [1]_.
|
||||
|
||||
A non-graphical degree sequence (not realizable by some simple
|
||||
graph) is allowed since this function returns graphs with self
|
||||
loops and parallel edges. An exception is raised if the degree
|
||||
sequence does not have an even sum.
|
||||
|
||||
This configuration model construction process can lead to
|
||||
duplicate edges and loops. You can remove the self-loops and
|
||||
parallel edges (see below) which will likely result in a graph
|
||||
that doesn't have the exact degree sequence specified.
|
||||
|
||||
The density of self-loops and parallel edges tends to decrease as
|
||||
the number of nodes increases. However, typically the number of
|
||||
self-loops will approach a Poisson distribution with a nonzero mean,
|
||||
and similarly for the number of parallel edges. Consider a node
|
||||
with *k* stubs. The probability of being joined to another stub of
|
||||
the same node is basically (*k* - *1*) / *N*, where *k* is the
|
||||
degree and *N* is the number of nodes. So the probability of a
|
||||
self-loop scales like *c* / *N* for some constant *c*. As *N* grows,
|
||||
this means we expect *c* self-loops. Similarly for parallel edges.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M.E.J. Newman, "The structure and function of complex networks",
|
||||
SIAM REVIEW 45-2, pp 167-256, 2003.
|
||||
|
||||
Examples
|
||||
--------
|
||||
You can create a degree sequence following a particular distribution
|
||||
by using the one of the distribution functions in
|
||||
:mod:`~networkx.utils.random_sequence` (or one of your own). For
|
||||
example, to create an undirected multigraph on one hundred nodes
|
||||
with degree sequence chosen from the power law distribution:
|
||||
|
||||
>>> sequence = nx.random_powerlaw_tree_sequence(100, tries=5000)
|
||||
>>> G = nx.configuration_model(sequence)
|
||||
>>> len(G)
|
||||
100
|
||||
>>> actual_degrees = [d for v, d in G.degree()]
|
||||
>>> actual_degrees == sequence
|
||||
True
|
||||
|
||||
The returned graph is a multigraph, which may have parallel
|
||||
edges. To remove any parallel edges from the returned graph:
|
||||
|
||||
>>> G = nx.Graph(G)
|
||||
|
||||
Similarly, to remove self-loops:
|
||||
|
||||
>>> G.remove_edges_from(nx.selfloop_edges(G))
|
||||
|
||||
"""
|
||||
if sum(deg_sequence) % 2 != 0:
|
||||
msg = "Invalid degree sequence: sum of degrees must be even, not odd"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
|
||||
if G.is_directed():
|
||||
raise nx.NetworkXNotImplemented("not implemented for directed graphs")
|
||||
|
||||
G = _configuration_model(deg_sequence, G, seed=seed)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def directed_configuration_model(
|
||||
in_degree_sequence, out_degree_sequence, create_using=None, seed=None
|
||||
):
|
||||
"""Returns a directed_random graph with the given degree sequences.
|
||||
|
||||
The configuration model generates a random directed pseudograph
|
||||
(graph with parallel edges and self loops) by randomly assigning
|
||||
edges to match the given degree sequences.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
in_degree_sequence : list of nonnegative integers
|
||||
Each list entry corresponds to the in-degree of a node.
|
||||
out_degree_sequence : list of nonnegative integers
|
||||
Each list entry corresponds to the out-degree of a node.
|
||||
create_using : NetworkX graph constructor, optional (default MultiDiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : MultiDiGraph
|
||||
A graph with the specified degree sequences.
|
||||
Nodes are labeled starting at 0 with an index
|
||||
corresponding to the position in deg_sequence.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the degree sequences do not have the same sum.
|
||||
|
||||
See Also
|
||||
--------
|
||||
configuration_model
|
||||
|
||||
Notes
|
||||
-----
|
||||
Algorithm as described by Newman [1]_.
|
||||
|
||||
A non-graphical degree sequence (not realizable by some simple
|
||||
graph) is allowed since this function returns graphs with self
|
||||
loops and parallel edges. An exception is raised if the degree
|
||||
sequences does not have the same sum.
|
||||
|
||||
This configuration model construction process can lead to
|
||||
duplicate edges and loops. You can remove the self-loops and
|
||||
parallel edges (see below) which will likely result in a graph
|
||||
that doesn't have the exact degree sequence specified. This
|
||||
"finite-size effect" decreases as the size of the graph increases.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Newman, M. E. J. and Strogatz, S. H. and Watts, D. J.
|
||||
Random graphs with arbitrary degree distributions and their applications
|
||||
Phys. Rev. E, 64, 026118 (2001)
|
||||
|
||||
Examples
|
||||
--------
|
||||
One can modify the in- and out-degree sequences from an existing
|
||||
directed graph in order to create a new directed graph. For example,
|
||||
here we modify the directed path graph:
|
||||
|
||||
>>> D = nx.DiGraph([(0, 1), (1, 2), (2, 3)])
|
||||
>>> din = list(d for n, d in D.in_degree())
|
||||
>>> dout = list(d for n, d in D.out_degree())
|
||||
>>> din.append(1)
|
||||
>>> dout[0] = 2
|
||||
>>> # We now expect an edge from node 0 to a new node, node 3.
|
||||
... D = nx.directed_configuration_model(din, dout)
|
||||
|
||||
The returned graph is a directed multigraph, which may have parallel
|
||||
edges. To remove any parallel edges from the returned graph:
|
||||
|
||||
>>> D = nx.DiGraph(D)
|
||||
|
||||
Similarly, to remove self-loops:
|
||||
|
||||
>>> D.remove_edges_from(nx.selfloop_edges(D))
|
||||
|
||||
"""
|
||||
if sum(in_degree_sequence) != sum(out_degree_sequence):
|
||||
msg = "Invalid degree sequences: sequences must have equal sums"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
if create_using is None:
|
||||
create_using = nx.MultiDiGraph
|
||||
|
||||
G = _configuration_model(
|
||||
out_degree_sequence,
|
||||
create_using,
|
||||
directed=True,
|
||||
in_deg_sequence=in_degree_sequence,
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
name = "directed configuration_model {} nodes {} edges"
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(1)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def expected_degree_graph(w, seed=None, selfloops=True):
|
||||
r"""Returns a random graph with given expected degrees.
|
||||
|
||||
Given a sequence of expected degrees $W=(w_0,w_1,\ldots,w_{n-1})$
|
||||
of length $n$ this algorithm assigns an edge between node $u$ and
|
||||
node $v$ with probability
|
||||
|
||||
.. math::
|
||||
|
||||
p_{uv} = \frac{w_u w_v}{\sum_k w_k} .
|
||||
|
||||
Parameters
|
||||
----------
|
||||
w : list
|
||||
The list of expected degrees.
|
||||
selfloops: bool (default=True)
|
||||
Set to False to remove the possibility of self-loop edges.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> z = [10 for i in range(100)]
|
||||
>>> G = nx.expected_degree_graph(z)
|
||||
|
||||
Notes
|
||||
-----
|
||||
The nodes have integer labels corresponding to index of expected degrees
|
||||
input sequence.
|
||||
|
||||
The complexity of this algorithm is $\mathcal{O}(n+m)$ where $n$ is the
|
||||
number of nodes and $m$ is the expected number of edges.
|
||||
|
||||
The model in [1]_ includes the possibility of self-loop edges.
|
||||
Set selfloops=False to produce a graph without self loops.
|
||||
|
||||
For finite graphs this model doesn't produce exactly the given
|
||||
expected degree sequence. Instead the expected degrees are as
|
||||
follows.
|
||||
|
||||
For the case without self loops (selfloops=False),
|
||||
|
||||
.. math::
|
||||
|
||||
E[deg(u)] = \sum_{v \ne u} p_{uv}
|
||||
= w_u \left( 1 - \frac{w_u}{\sum_k w_k} \right) .
|
||||
|
||||
|
||||
NetworkX uses the standard convention that a self-loop edge counts 2
|
||||
in the degree of a node, so with self loops (selfloops=True),
|
||||
|
||||
.. math::
|
||||
|
||||
E[deg(u)] = \sum_{v \ne u} p_{uv} + 2 p_{uu}
|
||||
= w_u \left( 1 + \frac{w_u}{\sum_k w_k} \right) .
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Fan Chung and L. Lu, Connected components in random graphs with
|
||||
given expected degree sequences, Ann. Combinatorics, 6,
|
||||
pp. 125-145, 2002.
|
||||
.. [2] Joel Miller and Aric Hagberg,
|
||||
Efficient generation of networks with given expected degrees,
|
||||
in Algorithms and Models for the Web-Graph (WAW 2011),
|
||||
Alan Frieze, Paul Horn, and Paweł Prałat (Eds), LNCS 6732,
|
||||
pp. 115-126, 2011.
|
||||
"""
|
||||
n = len(w)
|
||||
G = nx.empty_graph(n)
|
||||
|
||||
# If there are no nodes are no edges in the graph, return the empty graph.
|
||||
if n == 0 or max(w) == 0:
|
||||
return G
|
||||
|
||||
rho = 1 / sum(w)
|
||||
# Sort the weights in decreasing order. The original order of the
|
||||
# weights dictates the order of the (integer) node labels, so we
|
||||
# need to remember the permutation applied in the sorting.
|
||||
order = sorted(enumerate(w), key=itemgetter(1), reverse=True)
|
||||
mapping = {c: u for c, (u, v) in enumerate(order)}
|
||||
seq = [v for u, v in order]
|
||||
last = n
|
||||
if not selfloops:
|
||||
last -= 1
|
||||
for u in range(last):
|
||||
v = u
|
||||
if not selfloops:
|
||||
v += 1
|
||||
factor = seq[u] * rho
|
||||
p = min(seq[v] * factor, 1)
|
||||
while v < n and p > 0:
|
||||
if p != 1:
|
||||
r = seed.random()
|
||||
v += math.floor(math.log(r, 1 - p))
|
||||
if v < n:
|
||||
q = min(seq[v] * factor, 1)
|
||||
if seed.random() < q / p:
|
||||
G.add_edge(mapping[u], mapping[v])
|
||||
v += 1
|
||||
p = q
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def havel_hakimi_graph(deg_sequence, create_using=None):
|
||||
"""Returns a simple graph with given degree sequence constructed
|
||||
using the Havel-Hakimi algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
deg_sequence: list of integers
|
||||
Each integer corresponds to the degree of a node (need not be sorted).
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
Directed graphs are not allowed.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXException
|
||||
For a non-graphical degree sequence (i.e. one
|
||||
not realizable by some simple graph).
|
||||
|
||||
Notes
|
||||
-----
|
||||
The Havel-Hakimi algorithm constructs a simple graph by
|
||||
successively connecting the node of highest degree to other nodes
|
||||
of highest degree, resorting remaining nodes by degree, and
|
||||
repeating the process. The resulting graph has a high
|
||||
degree-associativity. Nodes are labeled 1,.., len(deg_sequence),
|
||||
corresponding to their position in deg_sequence.
|
||||
|
||||
The basic algorithm is from Hakimi [1]_ and was generalized by
|
||||
Kleitman and Wang [2]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Hakimi S., On Realizability of a Set of Integers as
|
||||
Degrees of the Vertices of a Linear Graph. I,
|
||||
Journal of SIAM, 10(3), pp. 496-506 (1962)
|
||||
.. [2] Kleitman D.J. and Wang D.L.
|
||||
Algorithms for Constructing Graphs and Digraphs with Given Valences
|
||||
and Factors Discrete Mathematics, 6(1), pp. 79-88 (1973)
|
||||
"""
|
||||
if not nx.is_graphical(deg_sequence):
|
||||
raise nx.NetworkXError("Invalid degree sequence")
|
||||
|
||||
p = len(deg_sequence)
|
||||
G = nx.empty_graph(p, create_using)
|
||||
if G.is_directed():
|
||||
raise nx.NetworkXError("Directed graphs are not supported")
|
||||
num_degs = [[] for i in range(p)]
|
||||
dmax, dsum, n = 0, 0, 0
|
||||
for d in deg_sequence:
|
||||
# Process only the non-zero integers
|
||||
if d > 0:
|
||||
num_degs[d].append(n)
|
||||
dmax, dsum, n = max(dmax, d), dsum + d, n + 1
|
||||
# Return graph if no edges
|
||||
if n == 0:
|
||||
return G
|
||||
|
||||
modstubs = [(0, 0)] * (dmax + 1)
|
||||
# Successively reduce degree sequence by removing the maximum degree
|
||||
while n > 0:
|
||||
# Retrieve the maximum degree in the sequence
|
||||
while len(num_degs[dmax]) == 0:
|
||||
dmax -= 1
|
||||
# If there are not enough stubs to connect to, then the sequence is
|
||||
# not graphical
|
||||
if dmax > n - 1:
|
||||
raise nx.NetworkXError("Non-graphical integer sequence")
|
||||
|
||||
# Remove largest stub in list
|
||||
source = num_degs[dmax].pop()
|
||||
n -= 1
|
||||
# Reduce the next dmax largest stubs
|
||||
mslen = 0
|
||||
k = dmax
|
||||
for i in range(dmax):
|
||||
while len(num_degs[k]) == 0:
|
||||
k -= 1
|
||||
target = num_degs[k].pop()
|
||||
G.add_edge(source, target)
|
||||
n -= 1
|
||||
if k > 1:
|
||||
modstubs[mslen] = (k - 1, target)
|
||||
mslen += 1
|
||||
# Add back to the list any nonzero stubs that were removed
|
||||
for i in range(mslen):
|
||||
(stubval, stubtarget) = modstubs[i]
|
||||
num_degs[stubval].append(stubtarget)
|
||||
n += 1
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def directed_havel_hakimi_graph(in_deg_sequence, out_deg_sequence, create_using=None):
|
||||
"""Returns a directed graph with the given degree sequences.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
in_deg_sequence : list of integers
|
||||
Each list entry corresponds to the in-degree of a node.
|
||||
out_deg_sequence : list of integers
|
||||
Each list entry corresponds to the out-degree of a node.
|
||||
create_using : NetworkX graph constructor, optional (default DiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : DiGraph
|
||||
A graph with the specified degree sequences.
|
||||
Nodes are labeled starting at 0 with an index
|
||||
corresponding to the position in deg_sequence
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the degree sequences are not digraphical.
|
||||
|
||||
See Also
|
||||
--------
|
||||
configuration_model
|
||||
|
||||
Notes
|
||||
-----
|
||||
Algorithm as described by Kleitman and Wang [1]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] D.J. Kleitman and D.L. Wang
|
||||
Algorithms for Constructing Graphs and Digraphs with Given Valences
|
||||
and Factors Discrete Mathematics, 6(1), pp. 79-88 (1973)
|
||||
"""
|
||||
in_deg_sequence = nx.utils.make_list_of_ints(in_deg_sequence)
|
||||
out_deg_sequence = nx.utils.make_list_of_ints(out_deg_sequence)
|
||||
|
||||
# Process the sequences and form two heaps to store degree pairs with
|
||||
# either zero or nonzero out degrees
|
||||
sumin, sumout = 0, 0
|
||||
nin, nout = len(in_deg_sequence), len(out_deg_sequence)
|
||||
maxn = max(nin, nout)
|
||||
G = nx.empty_graph(maxn, create_using, default=nx.DiGraph)
|
||||
if maxn == 0:
|
||||
return G
|
||||
maxin = 0
|
||||
stubheap, zeroheap = [], []
|
||||
for n in range(maxn):
|
||||
in_deg, out_deg = 0, 0
|
||||
if n < nout:
|
||||
out_deg = out_deg_sequence[n]
|
||||
if n < nin:
|
||||
in_deg = in_deg_sequence[n]
|
||||
if in_deg < 0 or out_deg < 0:
|
||||
raise nx.NetworkXError(
|
||||
"Invalid degree sequences. Sequence values must be positive."
|
||||
)
|
||||
sumin, sumout, maxin = sumin + in_deg, sumout + out_deg, max(maxin, in_deg)
|
||||
if in_deg > 0:
|
||||
stubheap.append((-1 * out_deg, -1 * in_deg, n))
|
||||
elif out_deg > 0:
|
||||
zeroheap.append((-1 * out_deg, n))
|
||||
if sumin != sumout:
|
||||
raise nx.NetworkXError(
|
||||
"Invalid degree sequences. Sequences must have equal sums."
|
||||
)
|
||||
heapq.heapify(stubheap)
|
||||
heapq.heapify(zeroheap)
|
||||
|
||||
modstubs = [(0, 0, 0)] * (maxin + 1)
|
||||
# Successively reduce degree sequence by removing the maximum
|
||||
while stubheap:
|
||||
# Remove first value in the sequence with a non-zero in degree
|
||||
(freeout, freein, target) = heapq.heappop(stubheap)
|
||||
freein *= -1
|
||||
if freein > len(stubheap) + len(zeroheap):
|
||||
raise nx.NetworkXError("Non-digraphical integer sequence")
|
||||
|
||||
# Attach arcs from the nodes with the most stubs
|
||||
mslen = 0
|
||||
for i in range(freein):
|
||||
if zeroheap and (not stubheap or stubheap[0][0] > zeroheap[0][0]):
|
||||
(stubout, stubsource) = heapq.heappop(zeroheap)
|
||||
stubin = 0
|
||||
else:
|
||||
(stubout, stubin, stubsource) = heapq.heappop(stubheap)
|
||||
if stubout == 0:
|
||||
raise nx.NetworkXError("Non-digraphical integer sequence")
|
||||
G.add_edge(stubsource, target)
|
||||
# Check if source is now totally connected
|
||||
if stubout + 1 < 0 or stubin < 0:
|
||||
modstubs[mslen] = (stubout + 1, stubin, stubsource)
|
||||
mslen += 1
|
||||
|
||||
# Add the nodes back to the heaps that still have available stubs
|
||||
for i in range(mslen):
|
||||
stub = modstubs[i]
|
||||
if stub[1] < 0:
|
||||
heapq.heappush(stubheap, stub)
|
||||
else:
|
||||
heapq.heappush(zeroheap, (stub[0], stub[2]))
|
||||
if freeout < 0:
|
||||
heapq.heappush(zeroheap, (freeout, target))
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def degree_sequence_tree(deg_sequence, create_using=None):
|
||||
"""Make a tree for the given degree sequence.
|
||||
|
||||
A tree has #nodes-#edges=1 so
|
||||
the degree sequence must have
|
||||
len(deg_sequence)-sum(deg_sequence)/2=1
|
||||
"""
|
||||
# The sum of the degree sequence must be even (for any undirected graph).
|
||||
degree_sum = sum(deg_sequence)
|
||||
if degree_sum % 2 != 0:
|
||||
msg = "Invalid degree sequence: sum of degrees must be even, not odd"
|
||||
raise nx.NetworkXError(msg)
|
||||
if len(deg_sequence) - degree_sum // 2 != 1:
|
||||
msg = (
|
||||
"Invalid degree sequence: tree must have number of nodes equal"
|
||||
" to one less than the number of edges"
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
G = nx.empty_graph(0, create_using)
|
||||
if G.is_directed():
|
||||
raise nx.NetworkXError("Directed Graph not supported")
|
||||
|
||||
# Sort all degrees greater than 1 in decreasing order.
|
||||
#
|
||||
# TODO Does this need to be sorted in reverse order?
|
||||
deg = sorted((s for s in deg_sequence if s > 1), reverse=True)
|
||||
|
||||
# make path graph as backbone
|
||||
n = len(deg) + 2
|
||||
nx.add_path(G, range(n))
|
||||
last = n
|
||||
|
||||
# add the leaves
|
||||
for source in range(1, n - 1):
|
||||
nedges = deg.pop() - 2
|
||||
for target in range(last, last + nedges):
|
||||
G.add_edge(source, target)
|
||||
last += nedges
|
||||
|
||||
# in case we added one too many
|
||||
if len(G) > len(deg_sequence):
|
||||
G.remove_node(0)
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(1)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_degree_sequence_graph(sequence, seed=None, tries=10):
|
||||
r"""Returns a simple random graph with the given degree sequence.
|
||||
|
||||
If the maximum degree $d_m$ in the sequence is $O(m^{1/4})$ then the
|
||||
algorithm produces almost uniform random graphs in $O(m d_m)$ time
|
||||
where $m$ is the number of edges.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sequence : list of integers
|
||||
Sequence of degrees
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
tries : int, optional
|
||||
Maximum number of tries to create a graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
A graph with the specified degree sequence.
|
||||
Nodes are labeled starting at 0 with an index
|
||||
corresponding to the position in the sequence.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXUnfeasible
|
||||
If the degree sequence is not graphical.
|
||||
NetworkXError
|
||||
If a graph is not produced in specified number of tries
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_graphical, configuration_model
|
||||
|
||||
Notes
|
||||
-----
|
||||
The generator algorithm [1]_ is not guaranteed to produce a graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Moshen Bayati, Jeong Han Kim, and Amin Saberi,
|
||||
A sequential algorithm for generating random graphs.
|
||||
Algorithmica, Volume 58, Number 4, 860-910,
|
||||
DOI: 10.1007/s00453-009-9340-1
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> sequence = [1, 2, 2, 3]
|
||||
>>> G = nx.random_degree_sequence_graph(sequence, seed=42)
|
||||
>>> sorted(d for n, d in G.degree())
|
||||
[1, 2, 2, 3]
|
||||
"""
|
||||
DSRG = DegreeSequenceRandomGraph(sequence, seed)
|
||||
for try_n in range(tries):
|
||||
try:
|
||||
return DSRG.generate()
|
||||
except nx.NetworkXUnfeasible:
|
||||
pass
|
||||
raise nx.NetworkXError(f"failed to generate graph in {tries} tries")
|
||||
|
||||
|
||||
class DegreeSequenceRandomGraph:
|
||||
# class to generate random graphs with a given degree sequence
|
||||
# use random_degree_sequence_graph()
|
||||
def __init__(self, degree, rng):
|
||||
if not nx.is_graphical(degree):
|
||||
raise nx.NetworkXUnfeasible("degree sequence is not graphical")
|
||||
self.rng = rng
|
||||
self.degree = list(degree)
|
||||
# node labels are integers 0,...,n-1
|
||||
self.m = sum(self.degree) / 2.0 # number of edges
|
||||
try:
|
||||
self.dmax = max(self.degree) # maximum degree
|
||||
except ValueError:
|
||||
self.dmax = 0
|
||||
|
||||
def generate(self):
|
||||
# remaining_degree is mapping from int->remaining degree
|
||||
self.remaining_degree = dict(enumerate(self.degree))
|
||||
# add all nodes to make sure we get isolated nodes
|
||||
self.graph = nx.Graph()
|
||||
self.graph.add_nodes_from(self.remaining_degree)
|
||||
# remove zero degree nodes
|
||||
for n, d in list(self.remaining_degree.items()):
|
||||
if d == 0:
|
||||
del self.remaining_degree[n]
|
||||
if len(self.remaining_degree) > 0:
|
||||
# build graph in three phases according to how many unmatched edges
|
||||
self.phase1()
|
||||
self.phase2()
|
||||
self.phase3()
|
||||
return self.graph
|
||||
|
||||
def update_remaining(self, u, v, aux_graph=None):
|
||||
# decrement remaining nodes, modify auxiliary graph if in phase3
|
||||
if aux_graph is not None:
|
||||
# remove edges from auxiliary graph
|
||||
aux_graph.remove_edge(u, v)
|
||||
if self.remaining_degree[u] == 1:
|
||||
del self.remaining_degree[u]
|
||||
if aux_graph is not None:
|
||||
aux_graph.remove_node(u)
|
||||
else:
|
||||
self.remaining_degree[u] -= 1
|
||||
if self.remaining_degree[v] == 1:
|
||||
del self.remaining_degree[v]
|
||||
if aux_graph is not None:
|
||||
aux_graph.remove_node(v)
|
||||
else:
|
||||
self.remaining_degree[v] -= 1
|
||||
|
||||
def p(self, u, v):
|
||||
# degree probability
|
||||
return 1 - self.degree[u] * self.degree[v] / (4.0 * self.m)
|
||||
|
||||
def q(self, u, v):
|
||||
# remaining degree probability
|
||||
norm = max(self.remaining_degree.values()) ** 2
|
||||
return self.remaining_degree[u] * self.remaining_degree[v] / norm
|
||||
|
||||
def suitable_edge(self):
|
||||
"""Returns True if and only if an arbitrary remaining node can
|
||||
potentially be joined with some other remaining node.
|
||||
|
||||
"""
|
||||
nodes = iter(self.remaining_degree)
|
||||
u = next(nodes)
|
||||
return any(v not in self.graph[u] for v in nodes)
|
||||
|
||||
def phase1(self):
|
||||
# choose node pairs from (degree) weighted distribution
|
||||
rem_deg = self.remaining_degree
|
||||
while sum(rem_deg.values()) >= 2 * self.dmax**2:
|
||||
u, v = sorted(random_weighted_sample(rem_deg, 2, self.rng))
|
||||
if self.graph.has_edge(u, v):
|
||||
continue
|
||||
if self.rng.random() < self.p(u, v): # accept edge
|
||||
self.graph.add_edge(u, v)
|
||||
self.update_remaining(u, v)
|
||||
|
||||
def phase2(self):
|
||||
# choose remaining nodes uniformly at random and use rejection sampling
|
||||
remaining_deg = self.remaining_degree
|
||||
rng = self.rng
|
||||
while len(remaining_deg) >= 2 * self.dmax:
|
||||
while True:
|
||||
u, v = sorted(rng.sample(list(remaining_deg.keys()), 2))
|
||||
if self.graph.has_edge(u, v):
|
||||
continue
|
||||
if rng.random() < self.q(u, v):
|
||||
break
|
||||
if rng.random() < self.p(u, v): # accept edge
|
||||
self.graph.add_edge(u, v)
|
||||
self.update_remaining(u, v)
|
||||
|
||||
def phase3(self):
|
||||
# build potential remaining edges and choose with rejection sampling
|
||||
potential_edges = combinations(self.remaining_degree, 2)
|
||||
# build auxiliary graph of potential edges not already in graph
|
||||
H = nx.Graph(
|
||||
[(u, v) for (u, v) in potential_edges if not self.graph.has_edge(u, v)]
|
||||
)
|
||||
rng = self.rng
|
||||
while self.remaining_degree:
|
||||
if not self.suitable_edge():
|
||||
raise nx.NetworkXUnfeasible("no suitable edges left")
|
||||
while True:
|
||||
u, v = sorted(rng.choice(list(H.edges())))
|
||||
if rng.random() < self.q(u, v):
|
||||
break
|
||||
if rng.random() < self.p(u, v): # accept edge
|
||||
self.graph.add_edge(u, v)
|
||||
self.update_remaining(u, v, aux_graph=H)
|
||||
501
.CondaPkg/env/Lib/site-packages/networkx/generators/directed.py
vendored
Normal file
501
.CondaPkg/env/Lib/site-packages/networkx/generators/directed.py
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
"""
|
||||
Generators for some directed graphs, including growing network (GN) graphs and
|
||||
scale-free graphs.
|
||||
|
||||
"""
|
||||
|
||||
import numbers
|
||||
from collections import Counter
|
||||
|
||||
import networkx as nx
|
||||
from networkx.generators.classic import empty_graph
|
||||
from networkx.utils import discrete_sequence, py_random_state, weighted_choice
|
||||
|
||||
__all__ = [
|
||||
"gn_graph",
|
||||
"gnc_graph",
|
||||
"gnr_graph",
|
||||
"random_k_out_graph",
|
||||
"scale_free_graph",
|
||||
]
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def gn_graph(n, kernel=None, create_using=None, seed=None):
|
||||
"""Returns the growing network (GN) digraph with `n` nodes.
|
||||
|
||||
The GN graph is built by adding nodes one at a time with a link to one
|
||||
previously added node. The target node for the link is chosen with
|
||||
probability based on degree. The default attachment kernel is a linear
|
||||
function of the degree of a node.
|
||||
|
||||
The graph is always a (directed) tree.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes for the generated graph.
|
||||
kernel : function
|
||||
The attachment kernel.
|
||||
create_using : NetworkX graph constructor, optional (default DiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To create the undirected GN graph, use the :meth:`~DiGraph.to_directed`
|
||||
method::
|
||||
|
||||
>>> D = nx.gn_graph(10) # the GN graph
|
||||
>>> G = D.to_undirected() # the undirected version
|
||||
|
||||
To specify an attachment kernel, use the `kernel` keyword argument::
|
||||
|
||||
>>> D = nx.gn_graph(10, kernel=lambda x: x**1.5) # A_k = k^1.5
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] P. L. Krapivsky and S. Redner,
|
||||
Organization of Growing Random Networks,
|
||||
Phys. Rev. E, 63, 066123, 2001.
|
||||
"""
|
||||
G = empty_graph(1, create_using, default=nx.DiGraph)
|
||||
if not G.is_directed():
|
||||
raise nx.NetworkXError("create_using must indicate a Directed Graph")
|
||||
|
||||
if kernel is None:
|
||||
|
||||
def kernel(x):
|
||||
return x
|
||||
|
||||
if n == 1:
|
||||
return G
|
||||
|
||||
G.add_edge(1, 0) # get started
|
||||
ds = [1, 1] # degree sequence
|
||||
|
||||
for source in range(2, n):
|
||||
# compute distribution from kernel and degree
|
||||
dist = [kernel(d) for d in ds]
|
||||
# choose target from discrete distribution
|
||||
target = discrete_sequence(1, distribution=dist, seed=seed)[0]
|
||||
G.add_edge(source, target)
|
||||
ds.append(1) # the source has only one link (degree one)
|
||||
ds[target] += 1 # add one to the target link degree
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def gnr_graph(n, p, create_using=None, seed=None):
|
||||
"""Returns the growing network with redirection (GNR) digraph with `n`
|
||||
nodes and redirection probability `p`.
|
||||
|
||||
The GNR graph is built by adding nodes one at a time with a link to one
|
||||
previously added node. The previous target node is chosen uniformly at
|
||||
random. With probability `p` the link is instead "redirected" to the
|
||||
successor node of the target.
|
||||
|
||||
The graph is always a (directed) tree.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes for the generated graph.
|
||||
p : float
|
||||
The redirection probability.
|
||||
create_using : NetworkX graph constructor, optional (default DiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To create the undirected GNR graph, use the :meth:`~DiGraph.to_directed`
|
||||
method::
|
||||
|
||||
>>> D = nx.gnr_graph(10, 0.5) # the GNR graph
|
||||
>>> G = D.to_undirected() # the undirected version
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] P. L. Krapivsky and S. Redner,
|
||||
Organization of Growing Random Networks,
|
||||
Phys. Rev. E, 63, 066123, 2001.
|
||||
"""
|
||||
G = empty_graph(1, create_using, default=nx.DiGraph)
|
||||
if not G.is_directed():
|
||||
raise nx.NetworkXError("create_using must indicate a Directed Graph")
|
||||
|
||||
if n == 1:
|
||||
return G
|
||||
|
||||
for source in range(1, n):
|
||||
target = seed.randrange(0, source)
|
||||
if seed.random() < p and target != 0:
|
||||
target = next(G.successors(target))
|
||||
G.add_edge(source, target)
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(2)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def gnc_graph(n, create_using=None, seed=None):
|
||||
"""Returns the growing network with copying (GNC) digraph with `n` nodes.
|
||||
|
||||
The GNC graph is built by adding nodes one at a time with a link to one
|
||||
previously added node (chosen uniformly at random) and to all of that
|
||||
node's successors.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes for the generated graph.
|
||||
create_using : NetworkX graph constructor, optional (default DiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] P. L. Krapivsky and S. Redner,
|
||||
Network Growth by Copying,
|
||||
Phys. Rev. E, 71, 036118, 2005k.},
|
||||
"""
|
||||
G = empty_graph(1, create_using, default=nx.DiGraph)
|
||||
if not G.is_directed():
|
||||
raise nx.NetworkXError("create_using must indicate a Directed Graph")
|
||||
|
||||
if n == 1:
|
||||
return G
|
||||
|
||||
for source in range(1, n):
|
||||
target = seed.randrange(0, source)
|
||||
for succ in G.successors(target):
|
||||
G.add_edge(source, succ)
|
||||
G.add_edge(source, target)
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(6)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def scale_free_graph(
|
||||
n,
|
||||
alpha=0.41,
|
||||
beta=0.54,
|
||||
gamma=0.05,
|
||||
delta_in=0.2,
|
||||
delta_out=0,
|
||||
seed=None,
|
||||
initial_graph=None,
|
||||
):
|
||||
"""Returns a scale-free directed graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : integer
|
||||
Number of nodes in graph
|
||||
alpha : float
|
||||
Probability for adding a new node connected to an existing node
|
||||
chosen randomly according to the in-degree distribution.
|
||||
beta : float
|
||||
Probability for adding an edge between two existing nodes.
|
||||
One existing node is chosen randomly according the in-degree
|
||||
distribution and the other chosen randomly according to the out-degree
|
||||
distribution.
|
||||
gamma : float
|
||||
Probability for adding a new node connected to an existing node
|
||||
chosen randomly according to the out-degree distribution.
|
||||
delta_in : float
|
||||
Bias for choosing nodes from in-degree distribution.
|
||||
delta_out : float
|
||||
Bias for choosing nodes from out-degree distribution.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
initial_graph : MultiDiGraph instance, optional
|
||||
Build the scale-free graph starting from this initial MultiDiGraph,
|
||||
if provided.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MultiDiGraph
|
||||
|
||||
Examples
|
||||
--------
|
||||
Create a scale-free graph on one hundred nodes::
|
||||
|
||||
>>> G = nx.scale_free_graph(100)
|
||||
|
||||
Notes
|
||||
-----
|
||||
The sum of `alpha`, `beta`, and `gamma` must be 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] B. Bollobás, C. Borgs, J. Chayes, and O. Riordan,
|
||||
Directed scale-free graphs,
|
||||
Proceedings of the fourteenth annual ACM-SIAM Symposium on
|
||||
Discrete Algorithms, 132--139, 2003.
|
||||
"""
|
||||
|
||||
def _choose_node(candidates, node_list, delta):
|
||||
if delta > 0:
|
||||
bias_sum = len(node_list) * delta
|
||||
p_delta = bias_sum / (bias_sum + len(candidates))
|
||||
if seed.random() < p_delta:
|
||||
return seed.choice(node_list)
|
||||
return seed.choice(candidates)
|
||||
|
||||
if initial_graph is not None and hasattr(initial_graph, "_adj"):
|
||||
if not isinstance(initial_graph, nx.MultiDiGraph):
|
||||
raise nx.NetworkXError("initial_graph must be a MultiDiGraph.")
|
||||
G = initial_graph
|
||||
else:
|
||||
# Start with 3-cycle
|
||||
G = nx.MultiDiGraph([(0, 1), (1, 2), (2, 0)])
|
||||
|
||||
if alpha <= 0:
|
||||
raise ValueError("alpha must be > 0.")
|
||||
if beta <= 0:
|
||||
raise ValueError("beta must be > 0.")
|
||||
if gamma <= 0:
|
||||
raise ValueError("gamma must be > 0.")
|
||||
|
||||
if abs(alpha + beta + gamma - 1.0) >= 1e-9:
|
||||
raise ValueError("alpha+beta+gamma must equal 1.")
|
||||
|
||||
if delta_in < 0:
|
||||
raise ValueError("delta_in must be >= 0.")
|
||||
|
||||
if delta_out < 0:
|
||||
raise ValueError("delta_out must be >= 0.")
|
||||
|
||||
# pre-populate degree states
|
||||
vs = sum((count * [idx] for idx, count in G.out_degree()), [])
|
||||
ws = sum((count * [idx] for idx, count in G.in_degree()), [])
|
||||
|
||||
# pre-populate node state
|
||||
node_list = list(G.nodes())
|
||||
|
||||
# see if there already are number-based nodes
|
||||
numeric_nodes = [n for n in node_list if isinstance(n, numbers.Number)]
|
||||
if len(numeric_nodes) > 0:
|
||||
# set cursor for new nodes appropriately
|
||||
cursor = max(int(n.real) for n in numeric_nodes) + 1
|
||||
else:
|
||||
# or start at zero
|
||||
cursor = 0
|
||||
|
||||
while len(G) < n:
|
||||
r = seed.random()
|
||||
|
||||
# random choice in alpha,beta,gamma ranges
|
||||
if r < alpha:
|
||||
# alpha
|
||||
# add new node v
|
||||
v = cursor
|
||||
cursor += 1
|
||||
# also add to node state
|
||||
node_list.append(v)
|
||||
# choose w according to in-degree and delta_in
|
||||
w = _choose_node(ws, node_list, delta_in)
|
||||
|
||||
elif r < alpha + beta:
|
||||
# beta
|
||||
# choose v according to out-degree and delta_out
|
||||
v = _choose_node(vs, node_list, delta_out)
|
||||
# choose w according to in-degree and delta_in
|
||||
w = _choose_node(ws, node_list, delta_in)
|
||||
|
||||
else:
|
||||
# gamma
|
||||
# choose v according to out-degree and delta_out
|
||||
v = _choose_node(vs, node_list, delta_out)
|
||||
# add new node w
|
||||
w = cursor
|
||||
cursor += 1
|
||||
# also add to node state
|
||||
node_list.append(w)
|
||||
|
||||
# add edge to graph
|
||||
G.add_edge(v, w)
|
||||
|
||||
# update degree states
|
||||
vs.append(v)
|
||||
ws.append(w)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(4)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_uniform_k_out_graph(n, k, self_loops=True, with_replacement=True, seed=None):
|
||||
"""Returns a random `k`-out graph with uniform attachment.
|
||||
|
||||
A random `k`-out graph with uniform attachment is a multidigraph
|
||||
generated by the following algorithm. For each node *u*, choose
|
||||
`k` nodes *v* uniformly at random (with replacement). Add a
|
||||
directed edge joining *u* to *v*.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes in the returned graph.
|
||||
|
||||
k : int
|
||||
The out-degree of each node in the returned graph.
|
||||
|
||||
self_loops : bool
|
||||
If True, self-loops are allowed when generating the graph.
|
||||
|
||||
with_replacement : bool
|
||||
If True, neighbors are chosen with replacement and the
|
||||
returned graph will be a directed multigraph. Otherwise,
|
||||
neighbors are chosen without replacement and the returned graph
|
||||
will be a directed graph.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
A `k`-out-regular directed graph generated according to the
|
||||
above algorithm. It will be a multigraph if and only if
|
||||
`with_replacement` is True.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `with_replacement` is False and `k` is greater than
|
||||
`n`.
|
||||
|
||||
See also
|
||||
--------
|
||||
random_k_out_graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
The return digraph or multidigraph may not be strongly connected, or
|
||||
even weakly connected.
|
||||
|
||||
If `with_replacement` is True, this function is similar to
|
||||
:func:`random_k_out_graph`, if that function had parameter `alpha`
|
||||
set to positive infinity.
|
||||
|
||||
"""
|
||||
if with_replacement:
|
||||
create_using = nx.MultiDiGraph()
|
||||
|
||||
def sample(v, nodes):
|
||||
if not self_loops:
|
||||
nodes = nodes - {v}
|
||||
return (seed.choice(list(nodes)) for i in range(k))
|
||||
|
||||
else:
|
||||
create_using = nx.DiGraph()
|
||||
|
||||
def sample(v, nodes):
|
||||
if not self_loops:
|
||||
nodes = nodes - {v}
|
||||
return seed.sample(list(nodes), k)
|
||||
|
||||
G = nx.empty_graph(n, create_using)
|
||||
nodes = set(G)
|
||||
for u in G:
|
||||
G.add_edges_from((u, v) for v in sample(u, nodes))
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(4)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_k_out_graph(n, k, alpha, self_loops=True, seed=None):
|
||||
"""Returns a random `k`-out graph with preferential attachment.
|
||||
|
||||
A random `k`-out graph with preferential attachment is a
|
||||
multidigraph generated by the following algorithm.
|
||||
|
||||
1. Begin with an empty digraph, and initially set each node to have
|
||||
weight `alpha`.
|
||||
2. Choose a node `u` with out-degree less than `k` uniformly at
|
||||
random.
|
||||
3. Choose a node `v` from with probability proportional to its
|
||||
weight.
|
||||
4. Add a directed edge from `u` to `v`, and increase the weight
|
||||
of `v` by one.
|
||||
5. If each node has out-degree `k`, halt, otherwise repeat from
|
||||
step 2.
|
||||
|
||||
For more information on this model of random graph, see [1].
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes in the returned graph.
|
||||
|
||||
k : int
|
||||
The out-degree of each node in the returned graph.
|
||||
|
||||
alpha : float
|
||||
A positive :class:`float` representing the initial weight of
|
||||
each vertex. A higher number means that in step 3 above, nodes
|
||||
will be chosen more like a true uniformly random sample, and a
|
||||
lower number means that nodes are more likely to be chosen as
|
||||
their in-degree increases. If this parameter is not positive, a
|
||||
:exc:`ValueError` is raised.
|
||||
|
||||
self_loops : bool
|
||||
If True, self-loops are allowed when generating the graph.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`~networkx.classes.MultiDiGraph`
|
||||
A `k`-out-regular multidigraph generated according to the above
|
||||
algorithm.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `alpha` is not positive.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The returned multidigraph may not be strongly connected, or even
|
||||
weakly connected.
|
||||
|
||||
References
|
||||
----------
|
||||
[1]: Peterson, Nicholas R., and Boris Pittel.
|
||||
"Distance between two random `k`-out digraphs, with and without
|
||||
preferential attachment."
|
||||
arXiv preprint arXiv:1311.5961 (2013).
|
||||
<https://arxiv.org/abs/1311.5961>
|
||||
|
||||
"""
|
||||
if alpha < 0:
|
||||
raise ValueError("alpha must be positive")
|
||||
G = nx.empty_graph(n, create_using=nx.MultiDiGraph)
|
||||
weights = Counter({v: alpha for v in G})
|
||||
for i in range(k * n):
|
||||
u = seed.choice([v for v, d in G.out_degree() if d < k])
|
||||
# If self-loops are not allowed, make the source node `u` have
|
||||
# weight zero.
|
||||
if not self_loops:
|
||||
adjustment = Counter({u: weights[u]})
|
||||
else:
|
||||
adjustment = Counter()
|
||||
v = weighted_choice(weights - adjustment, seed=seed)
|
||||
G.add_edge(u, v)
|
||||
weights[v] += 1
|
||||
return G
|
||||
163
.CondaPkg/env/Lib/site-packages/networkx/generators/duplication.py
vendored
Normal file
163
.CondaPkg/env/Lib/site-packages/networkx/generators/duplication.py
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
"""Functions for generating graphs based on the "duplication" method.
|
||||
|
||||
These graph generators start with a small initial graph then duplicate
|
||||
nodes and (partially) duplicate their edges. These functions are
|
||||
generally inspired by biological networks.
|
||||
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = ["partial_duplication_graph", "duplication_divergence_graph"]
|
||||
|
||||
|
||||
@py_random_state(4)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def partial_duplication_graph(N, n, p, q, seed=None):
|
||||
"""Returns a random graph using the partial duplication model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
N : int
|
||||
The total number of nodes in the final graph.
|
||||
|
||||
n : int
|
||||
The number of nodes in the initial clique.
|
||||
|
||||
p : float
|
||||
The probability of joining each neighbor of a node to the
|
||||
duplicate node. Must be a number in the between zero and one,
|
||||
inclusive.
|
||||
|
||||
q : float
|
||||
The probability of joining the source node to the duplicate
|
||||
node. Must be a number in the between zero and one, inclusive.
|
||||
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
A graph of nodes is grown by creating a fully connected graph
|
||||
of size `n`. The following procedure is then repeated until
|
||||
a total of `N` nodes have been reached.
|
||||
|
||||
1. A random node, *u*, is picked and a new node, *v*, is created.
|
||||
2. For each neighbor of *u* an edge from the neighbor to *v* is created
|
||||
with probability `p`.
|
||||
3. An edge from *u* to *v* is created with probability `q`.
|
||||
|
||||
This algorithm appears in [1].
|
||||
|
||||
This implementation allows the possibility of generating
|
||||
disconnected graphs.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Knudsen Michael, and Carsten Wiuf. "A Markov chain approach to
|
||||
randomly grown graphs." Journal of Applied Mathematics 2008.
|
||||
<https://doi.org/10.1155/2008/190836>
|
||||
|
||||
"""
|
||||
if p < 0 or p > 1 or q < 0 or q > 1:
|
||||
msg = "partial duplication graph must have 0 <= p, q <= 1."
|
||||
raise NetworkXError(msg)
|
||||
if n > N:
|
||||
raise NetworkXError("partial duplication graph must have n <= N.")
|
||||
|
||||
G = nx.complete_graph(n)
|
||||
for new_node in range(n, N):
|
||||
# Pick a random vertex, u, already in the graph.
|
||||
src_node = seed.randint(0, new_node - 1)
|
||||
|
||||
# Add a new vertex, v, to the graph.
|
||||
G.add_node(new_node)
|
||||
|
||||
# For each neighbor of u...
|
||||
for nbr_node in list(nx.all_neighbors(G, src_node)):
|
||||
# Add the neighbor to v with probability p.
|
||||
if seed.random() < p:
|
||||
G.add_edge(new_node, nbr_node)
|
||||
|
||||
# Join v and u with probability q.
|
||||
if seed.random() < q:
|
||||
G.add_edge(new_node, src_node)
|
||||
return G
|
||||
|
||||
|
||||
@py_random_state(2)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def duplication_divergence_graph(n, p, seed=None):
|
||||
"""Returns an undirected graph using the duplication-divergence model.
|
||||
|
||||
A graph of `n` nodes is created by duplicating the initial nodes
|
||||
and retaining edges incident to the original nodes with a retention
|
||||
probability `p`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The desired number of nodes in the graph.
|
||||
p : float
|
||||
The probability for retaining the edge of the replicated node.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If `p` is not a valid probability.
|
||||
If `n` is less than 2.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This algorithm appears in [1].
|
||||
|
||||
This implementation disallows the possibility of generating
|
||||
disconnected graphs.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] I. Ispolatov, P. L. Krapivsky, A. Yuryev,
|
||||
"Duplication-divergence model of protein interaction network",
|
||||
Phys. Rev. E, 71, 061911, 2005.
|
||||
|
||||
"""
|
||||
if p > 1 or p < 0:
|
||||
msg = f"NetworkXError p={p} is not in [0,1]."
|
||||
raise nx.NetworkXError(msg)
|
||||
if n < 2:
|
||||
msg = "n must be greater than or equal to 2"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
G = nx.Graph()
|
||||
|
||||
# Initialize the graph with two connected nodes.
|
||||
G.add_edge(0, 1)
|
||||
i = 2
|
||||
while i < n:
|
||||
# Choose a random node from current graph to duplicate.
|
||||
random_node = seed.choice(list(G))
|
||||
# Make the replica.
|
||||
G.add_node(i)
|
||||
# flag indicates whether at least one edge is connected on the replica.
|
||||
flag = False
|
||||
for nbr in G.neighbors(random_node):
|
||||
if seed.random() < p:
|
||||
# Link retention step.
|
||||
G.add_edge(i, nbr)
|
||||
flag = True
|
||||
if not flag:
|
||||
# Delete replica if no edges retained.
|
||||
G.remove_node(i)
|
||||
else:
|
||||
# Successful duplication.
|
||||
i += 1
|
||||
return G
|
||||
65
.CondaPkg/env/Lib/site-packages/networkx/generators/ego.py
vendored
Normal file
65
.CondaPkg/env/Lib/site-packages/networkx/generators/ego.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Ego graph.
|
||||
"""
|
||||
__all__ = ["ego_graph"]
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
@nx._dispatchable(preserve_all_attrs=True, returns_graph=True)
|
||||
def ego_graph(G, n, radius=1, center=True, undirected=False, distance=None):
|
||||
"""Returns induced subgraph of neighbors centered at node n within
|
||||
a given radius.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX Graph or DiGraph
|
||||
|
||||
n : node
|
||||
A single node
|
||||
|
||||
radius : number, optional
|
||||
Include all neighbors of distance<=radius from n.
|
||||
|
||||
center : bool, optional
|
||||
If False, do not include center node in graph
|
||||
|
||||
undirected : bool, optional
|
||||
If True use both in- and out-neighbors of directed graphs.
|
||||
|
||||
distance : key, optional
|
||||
Use specified edge data key as distance. For example, setting
|
||||
distance='weight' will use the edge weight to measure the
|
||||
distance from the node n.
|
||||
|
||||
Notes
|
||||
-----
|
||||
For directed graphs D this produces the "out" neighborhood
|
||||
or successors. If you want the neighborhood of predecessors
|
||||
first reverse the graph with D.reverse(). If you want both
|
||||
directions use the keyword argument undirected=True.
|
||||
|
||||
Node, edge, and graph attributes are copied to the returned subgraph.
|
||||
"""
|
||||
if undirected:
|
||||
if distance is not None:
|
||||
sp, _ = nx.single_source_dijkstra(
|
||||
G.to_undirected(), n, cutoff=radius, weight=distance
|
||||
)
|
||||
else:
|
||||
sp = dict(
|
||||
nx.single_source_shortest_path_length(
|
||||
G.to_undirected(), n, cutoff=radius
|
||||
)
|
||||
)
|
||||
else:
|
||||
if distance is not None:
|
||||
sp, _ = nx.single_source_dijkstra(G, n, cutoff=radius, weight=distance)
|
||||
else:
|
||||
sp = dict(nx.single_source_shortest_path_length(G, n, cutoff=radius))
|
||||
|
||||
H = G.subgraph(sp).copy()
|
||||
if not center:
|
||||
H.remove_node(n)
|
||||
return H
|
||||
475
.CondaPkg/env/Lib/site-packages/networkx/generators/expanders.py
vendored
Normal file
475
.CondaPkg/env/Lib/site-packages/networkx/generators/expanders.py
vendored
Normal file
@@ -0,0 +1,475 @@
|
||||
"""Provides explicit constructions of expander graphs.
|
||||
|
||||
"""
|
||||
import itertools
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = [
|
||||
"margulis_gabber_galil_graph",
|
||||
"chordal_cycle_graph",
|
||||
"paley_graph",
|
||||
"maybe_regular_expander",
|
||||
"is_regular_expander",
|
||||
"random_regular_expander_graph",
|
||||
]
|
||||
|
||||
|
||||
# Other discrete torus expanders can be constructed by using the following edge
|
||||
# sets. For more information, see Chapter 4, "Expander Graphs", in
|
||||
# "Pseudorandomness", by Salil Vadhan.
|
||||
#
|
||||
# For a directed expander, add edges from (x, y) to:
|
||||
#
|
||||
# (x, y),
|
||||
# ((x + 1) % n, y),
|
||||
# (x, (y + 1) % n),
|
||||
# (x, (x + y) % n),
|
||||
# (-y % n, x)
|
||||
#
|
||||
# For an undirected expander, add the reverse edges.
|
||||
#
|
||||
# Also appearing in the paper of Gabber and Galil:
|
||||
#
|
||||
# (x, y),
|
||||
# (x, (x + y) % n),
|
||||
# (x, (x + y + 1) % n),
|
||||
# ((x + y) % n, y),
|
||||
# ((x + y + 1) % n, y)
|
||||
#
|
||||
# and:
|
||||
#
|
||||
# (x, y),
|
||||
# ((x + 2*y) % n, y),
|
||||
# ((x + (2*y + 1)) % n, y),
|
||||
# ((x + (2*y + 2)) % n, y),
|
||||
# (x, (y + 2*x) % n),
|
||||
# (x, (y + (2*x + 1)) % n),
|
||||
# (x, (y + (2*x + 2)) % n),
|
||||
#
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def margulis_gabber_galil_graph(n, create_using=None):
|
||||
r"""Returns the Margulis-Gabber-Galil undirected MultiGraph on `n^2` nodes.
|
||||
|
||||
The undirected MultiGraph is regular with degree `8`. Nodes are integer
|
||||
pairs. The second-largest eigenvalue of the adjacency matrix of the graph
|
||||
is at most `5 \sqrt{2}`, regardless of `n`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
Determines the number of nodes in the graph: `n^2`.
|
||||
create_using : NetworkX graph constructor, optional (default MultiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
The constructed undirected multigraph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is directed or not a multigraph.
|
||||
|
||||
"""
|
||||
G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
|
||||
if G.is_directed() or not G.is_multigraph():
|
||||
msg = "`create_using` must be an undirected multigraph."
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
for x, y in itertools.product(range(n), repeat=2):
|
||||
for u, v in (
|
||||
((x + 2 * y) % n, y),
|
||||
((x + (2 * y + 1)) % n, y),
|
||||
(x, (y + 2 * x) % n),
|
||||
(x, (y + (2 * x + 1)) % n),
|
||||
):
|
||||
G.add_edge((x, y), (u, v))
|
||||
G.graph["name"] = f"margulis_gabber_galil_graph({n})"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def chordal_cycle_graph(p, create_using=None):
|
||||
"""Returns the chordal cycle graph on `p` nodes.
|
||||
|
||||
The returned graph is a cycle graph on `p` nodes with chords joining each
|
||||
vertex `x` to its inverse modulo `p`. This graph is a (mildly explicit)
|
||||
3-regular expander [1]_.
|
||||
|
||||
`p` *must* be a prime number.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : a prime number
|
||||
|
||||
The number of vertices in the graph. This also indicates where the
|
||||
chordal edges in the cycle will be created.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
The constructed undirected multigraph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
|
||||
If `create_using` indicates directed or not a multigraph.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
.. [1] Theorem 4.4.2 in A. Lubotzky. "Discrete groups, expanding graphs and
|
||||
invariant measures", volume 125 of Progress in Mathematics.
|
||||
Birkhäuser Verlag, Basel, 1994.
|
||||
|
||||
"""
|
||||
G = nx.empty_graph(0, create_using, default=nx.MultiGraph)
|
||||
if G.is_directed() or not G.is_multigraph():
|
||||
msg = "`create_using` must be an undirected multigraph."
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
for x in range(p):
|
||||
left = (x - 1) % p
|
||||
right = (x + 1) % p
|
||||
# Here we apply Fermat's Little Theorem to compute the multiplicative
|
||||
# inverse of x in Z/pZ. By Fermat's Little Theorem,
|
||||
#
|
||||
# x^p = x (mod p)
|
||||
#
|
||||
# Therefore,
|
||||
#
|
||||
# x * x^(p - 2) = 1 (mod p)
|
||||
#
|
||||
# The number 0 is a special case: we just let its inverse be itself.
|
||||
chord = pow(x, p - 2, p) if x > 0 else 0
|
||||
for y in (left, right, chord):
|
||||
G.add_edge(x, y)
|
||||
G.graph["name"] = f"chordal_cycle_graph({p})"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def paley_graph(p, create_using=None):
|
||||
r"""Returns the Paley $\frac{(p-1)}{2}$ -regular graph on $p$ nodes.
|
||||
|
||||
The returned graph is a graph on $\mathbb{Z}/p\mathbb{Z}$ with edges between $x$ and $y$
|
||||
if and only if $x-y$ is a nonzero square in $\mathbb{Z}/p\mathbb{Z}$.
|
||||
|
||||
If $p \equiv 1 \pmod 4$, $-1$ is a square in $\mathbb{Z}/p\mathbb{Z}$ and therefore $x-y$ is a square if and
|
||||
only if $y-x$ is also a square, i.e the edges in the Paley graph are symmetric.
|
||||
|
||||
If $p \equiv 3 \pmod 4$, $-1$ is not a square in $\mathbb{Z}/p\mathbb{Z}$ and therefore either $x-y$ or $y-x$
|
||||
is a square in $\mathbb{Z}/p\mathbb{Z}$ but not both.
|
||||
|
||||
Note that a more general definition of Paley graphs extends this construction
|
||||
to graphs over $q=p^n$ vertices, by using the finite field $F_q$ instead of $\mathbb{Z}/p\mathbb{Z}$.
|
||||
This construction requires to compute squares in general finite fields and is
|
||||
not what is implemented here (i.e `paley_graph(25)` does not return the true
|
||||
Paley graph associated with $5^2$).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : int, an odd prime number.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
The constructed directed graph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the graph is a multigraph.
|
||||
|
||||
References
|
||||
----------
|
||||
Chapter 13 in B. Bollobas, Random Graphs. Second edition.
|
||||
Cambridge Studies in Advanced Mathematics, 73.
|
||||
Cambridge University Press, Cambridge (2001).
|
||||
"""
|
||||
G = nx.empty_graph(0, create_using, default=nx.DiGraph)
|
||||
if G.is_multigraph():
|
||||
msg = "`create_using` cannot be a multigraph."
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
# Compute the squares in Z/pZ.
|
||||
# Make it a set to uniquify (there are exactly (p-1)/2 squares in Z/pZ
|
||||
# when is prime).
|
||||
square_set = {(x**2) % p for x in range(1, p) if (x**2) % p != 0}
|
||||
|
||||
for x in range(p):
|
||||
for x2 in square_set:
|
||||
G.add_edge(x, (x + x2) % p)
|
||||
G.graph["name"] = f"paley({p})"
|
||||
return G
|
||||
|
||||
|
||||
@nx.utils.decorators.np_random_state("seed")
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def maybe_regular_expander(n, d, *, create_using=None, max_tries=100, seed=None):
|
||||
r"""Utility for creating a random regular expander.
|
||||
|
||||
Returns a random $d$-regular graph on $n$ nodes which is an expander
|
||||
graph with very good probability.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes.
|
||||
d : int
|
||||
The degree of each node.
|
||||
create_using : Graph Instance or Constructor
|
||||
Indicator of type of graph to return.
|
||||
If a Graph-type instance, then clear and use it.
|
||||
If a constructor, call it to create an empty graph.
|
||||
Use the Graph constructor by default.
|
||||
max_tries : int. (default: 100)
|
||||
The number of allowed loops when generating each independent cycle
|
||||
seed : (default: None)
|
||||
Seed used to set random number generation state. See :ref`Randomness<randomness>`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The nodes are numbered from $0$ to $n - 1$.
|
||||
|
||||
The graph is generated by taking $d / 2$ random independent cycles.
|
||||
|
||||
Joel Friedman proved that in this model the resulting
|
||||
graph is an expander with probability
|
||||
$1 - O(n^{-\tau})$ where $\tau = \lceil (\sqrt{d - 1}) / 2 \rceil - 1$. [1]_
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.maybe_regular_expander(n=200, d=6, seed=8020)
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : graph
|
||||
The constructed undirected graph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If $d % 2 != 0$ as the degree must be even.
|
||||
If $n - 1$ is less than $ 2d $ as the graph is complete at most.
|
||||
If max_tries is reached
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_regular_expander
|
||||
random_regular_expander_graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Joel Friedman,
|
||||
A Proof of Alon’s Second Eigenvalue Conjecture and Related Problems, 2004
|
||||
https://arxiv.org/abs/cs/0405020
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
if n < 1:
|
||||
raise nx.NetworkXError("n must be a positive integer")
|
||||
|
||||
if not (d >= 2):
|
||||
raise nx.NetworkXError("d must be greater than or equal to 2")
|
||||
|
||||
if not (d % 2 == 0):
|
||||
raise nx.NetworkXError("d must be even")
|
||||
|
||||
if not (n - 1 >= d):
|
||||
raise nx.NetworkXError(
|
||||
f"Need n-1>= d to have room for {d//2} independent cycles with {n} nodes"
|
||||
)
|
||||
|
||||
G = nx.empty_graph(n, create_using)
|
||||
|
||||
if n < 2:
|
||||
return G
|
||||
|
||||
cycles = []
|
||||
edges = set()
|
||||
|
||||
# Create d / 2 cycles
|
||||
for i in range(d // 2):
|
||||
iterations = max_tries
|
||||
# Make sure the cycles are independent to have a regular graph
|
||||
while len(edges) != (i + 1) * n:
|
||||
iterations -= 1
|
||||
# Faster than random.permutation(n) since there are only
|
||||
# (n-1)! distinct cycles against n! permutations of size n
|
||||
cycle = seed.permutation(n - 1).tolist()
|
||||
cycle.append(n - 1)
|
||||
|
||||
new_edges = {
|
||||
(u, v)
|
||||
for u, v in nx.utils.pairwise(cycle, cyclic=True)
|
||||
if (u, v) not in edges and (v, u) not in edges
|
||||
}
|
||||
# If the new cycle has no edges in common with previous cycles
|
||||
# then add it to the list otherwise try again
|
||||
if len(new_edges) == n:
|
||||
cycles.append(cycle)
|
||||
edges.update(new_edges)
|
||||
|
||||
if iterations == 0:
|
||||
raise nx.NetworkXError("Too many iterations in maybe_regular_expander")
|
||||
|
||||
G.add_edges_from(edges)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
@nx.utils.not_implemented_for("directed")
|
||||
@nx.utils.not_implemented_for("multigraph")
|
||||
@nx._dispatchable(preserve_edge_attrs={"G": {"weight": 1}})
|
||||
def is_regular_expander(G, *, epsilon=0):
|
||||
r"""Determines whether the graph G is a regular expander. [1]_
|
||||
|
||||
An expander graph is a sparse graph with strong connectivity properties.
|
||||
|
||||
More precisely, this helper checks whether the graph is a
|
||||
regular $(n, d, \lambda)$-expander with $\lambda$ close to
|
||||
the Alon-Boppana bound and given by
|
||||
$\lambda = 2 \sqrt{d - 1} + \epsilon$. [2]_
|
||||
|
||||
In the case where $\epsilon = 0$ then if the graph successfully passes the test
|
||||
it is a Ramanujan graph. [3]_
|
||||
|
||||
A Ramanujan graph has spectral gap almost as large as possible, which makes them
|
||||
excellent expanders.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
epsilon : int, float, default=0
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the given graph is a regular $(n, d, \lambda)$-expander
|
||||
where $\lambda = 2 \sqrt{d - 1} + \epsilon$.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.random_regular_expander_graph(20, 4)
|
||||
>>> nx.is_regular_expander(G)
|
||||
True
|
||||
|
||||
See Also
|
||||
--------
|
||||
maybe_regular_expander
|
||||
random_regular_expander_graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Expander graph, https://en.wikipedia.org/wiki/Expander_graph
|
||||
.. [2] Alon-Boppana bound, https://en.wikipedia.org/wiki/Alon%E2%80%93Boppana_bound
|
||||
.. [3] Ramanujan graphs, https://en.wikipedia.org/wiki/Ramanujan_graph
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse.linalg import eigsh
|
||||
|
||||
if epsilon < 0:
|
||||
raise nx.NetworkXError("epsilon must be non negative")
|
||||
|
||||
if not nx.is_regular(G):
|
||||
return False
|
||||
|
||||
_, d = nx.utils.arbitrary_element(G.degree)
|
||||
|
||||
A = nx.adjacency_matrix(G, dtype=float)
|
||||
lams = eigsh(A, which="LM", k=2, return_eigenvectors=False)
|
||||
|
||||
# lambda2 is the second biggest eigenvalue
|
||||
lambda2 = min(lams)
|
||||
|
||||
# Use bool() to convert numpy scalar to Python Boolean
|
||||
return bool(abs(lambda2) < 2 ** np.sqrt(d - 1) + epsilon)
|
||||
|
||||
|
||||
@nx.utils.decorators.np_random_state("seed")
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_regular_expander_graph(
|
||||
n, d, *, epsilon=0, create_using=None, max_tries=100, seed=None
|
||||
):
|
||||
r"""Returns a random regular expander graph on $n$ nodes with degree $d$.
|
||||
|
||||
An expander graph is a sparse graph with strong connectivity properties. [1]_
|
||||
|
||||
More precisely the returned graph is a $(n, d, \lambda)$-expander with
|
||||
$\lambda = 2 \sqrt{d - 1} + \epsilon$, close to the Alon-Boppana bound. [2]_
|
||||
|
||||
In the case where $\epsilon = 0$ it returns a Ramanujan graph.
|
||||
A Ramanujan graph has spectral gap almost as large as possible,
|
||||
which makes them excellent expanders. [3]_
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes.
|
||||
d : int
|
||||
The degree of each node.
|
||||
epsilon : int, float, default=0
|
||||
max_tries : int, (default: 100)
|
||||
The number of allowed loops, also used in the maybe_regular_expander utility
|
||||
seed : (default: None)
|
||||
Seed used to set random number generation state. See :ref`Randomness<randomness>`.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If max_tries is reached
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.random_regular_expander_graph(20, 4)
|
||||
>>> nx.is_regular_expander(G)
|
||||
True
|
||||
|
||||
Notes
|
||||
-----
|
||||
This loops over `maybe_regular_expander` and can be slow when
|
||||
$n$ is too big or $\epsilon$ too small.
|
||||
|
||||
See Also
|
||||
--------
|
||||
maybe_regular_expander
|
||||
is_regular_expander
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Expander graph, https://en.wikipedia.org/wiki/Expander_graph
|
||||
.. [2] Alon-Boppana bound, https://en.wikipedia.org/wiki/Alon%E2%80%93Boppana_bound
|
||||
.. [3] Ramanujan graphs, https://en.wikipedia.org/wiki/Ramanujan_graph
|
||||
|
||||
"""
|
||||
G = maybe_regular_expander(
|
||||
n, d, create_using=create_using, max_tries=max_tries, seed=seed
|
||||
)
|
||||
iterations = max_tries
|
||||
|
||||
while not is_regular_expander(G, epsilon=epsilon):
|
||||
iterations -= 1
|
||||
G = maybe_regular_expander(
|
||||
n=n, d=d, create_using=create_using, max_tries=max_tries, seed=seed
|
||||
)
|
||||
|
||||
if iterations == 0:
|
||||
raise nx.NetworkXError(
|
||||
"Too many iterations in random_regular_expander_graph"
|
||||
)
|
||||
|
||||
return G
|
||||
1047
.CondaPkg/env/Lib/site-packages/networkx/generators/geometric.py
vendored
Normal file
1047
.CondaPkg/env/Lib/site-packages/networkx/generators/geometric.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
199
.CondaPkg/env/Lib/site-packages/networkx/generators/harary_graph.py
vendored
Normal file
199
.CondaPkg/env/Lib/site-packages/networkx/generators/harary_graph.py
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
"""Generators for Harary graphs
|
||||
|
||||
This module gives two generators for the Harary graph, which was
|
||||
introduced by the famous mathematician Frank Harary in his 1962 work [H]_.
|
||||
The first generator gives the Harary graph that maximizes the node
|
||||
connectivity with given number of nodes and given number of edges.
|
||||
The second generator gives the Harary graph that minimizes
|
||||
the number of edges in the graph with given node connectivity and
|
||||
number of nodes.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [H] Harary, F. "The Maximum Connectivity of a Graph."
|
||||
Proc. Nat. Acad. Sci. USA 48, 1142-1146, 1962.
|
||||
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
|
||||
__all__ = ["hnm_harary_graph", "hkn_harary_graph"]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def hnm_harary_graph(n, m, create_using=None):
|
||||
"""Returns the Harary graph with given numbers of nodes and edges.
|
||||
|
||||
The Harary graph $H_{n,m}$ is the graph that maximizes node connectivity
|
||||
with $n$ nodes and $m$ edges.
|
||||
|
||||
This maximum node connectivity is known to be floor($2m/n$). [1]_
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n: integer
|
||||
The number of nodes the generated graph is to contain
|
||||
|
||||
m: integer
|
||||
The number of edges the generated graph is to contain
|
||||
|
||||
create_using : NetworkX graph constructor, optional Graph type
|
||||
to create (default=nx.Graph). If graph instance, then cleared
|
||||
before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The Harary graph $H_{n,m}$.
|
||||
|
||||
See Also
|
||||
--------
|
||||
hkn_harary_graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
This algorithm runs in $O(m)$ time.
|
||||
It is implemented by following the Reference [2]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] F. T. Boesch, A. Satyanarayana, and C. L. Suffel,
|
||||
"A Survey of Some Network Reliability Analysis and Synthesis Results,"
|
||||
Networks, pp. 99-107, 2009.
|
||||
|
||||
.. [2] Harary, F. "The Maximum Connectivity of a Graph."
|
||||
Proc. Nat. Acad. Sci. USA 48, 1142-1146, 1962.
|
||||
"""
|
||||
|
||||
if n < 1:
|
||||
raise NetworkXError("The number of nodes must be >= 1!")
|
||||
if m < n - 1:
|
||||
raise NetworkXError("The number of edges must be >= n - 1 !")
|
||||
if m > n * (n - 1) // 2:
|
||||
raise NetworkXError("The number of edges must be <= n(n-1)/2")
|
||||
|
||||
# Construct an empty graph with n nodes first
|
||||
H = nx.empty_graph(n, create_using)
|
||||
# Get the floor of average node degree
|
||||
d = 2 * m // n
|
||||
|
||||
# Test the parity of n and d
|
||||
if (n % 2 == 0) or (d % 2 == 0):
|
||||
# Start with a regular graph of d degrees
|
||||
offset = d // 2
|
||||
for i in range(n):
|
||||
for j in range(1, offset + 1):
|
||||
H.add_edge(i, (i - j) % n)
|
||||
H.add_edge(i, (i + j) % n)
|
||||
if d & 1:
|
||||
# in case d is odd; n must be even in this case
|
||||
half = n // 2
|
||||
for i in range(half):
|
||||
# add edges diagonally
|
||||
H.add_edge(i, i + half)
|
||||
# Get the remainder of 2*m modulo n
|
||||
r = 2 * m % n
|
||||
if r > 0:
|
||||
# add remaining edges at offset+1
|
||||
for i in range(r // 2):
|
||||
H.add_edge(i, i + offset + 1)
|
||||
else:
|
||||
# Start with a regular graph of (d - 1) degrees
|
||||
offset = (d - 1) // 2
|
||||
for i in range(n):
|
||||
for j in range(1, offset + 1):
|
||||
H.add_edge(i, (i - j) % n)
|
||||
H.add_edge(i, (i + j) % n)
|
||||
half = n // 2
|
||||
for i in range(m - n * offset):
|
||||
# add the remaining m - n*offset edges between i and i+half
|
||||
H.add_edge(i, (i + half) % n)
|
||||
|
||||
return H
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def hkn_harary_graph(k, n, create_using=None):
|
||||
"""Returns the Harary graph with given node connectivity and node number.
|
||||
|
||||
The Harary graph $H_{k,n}$ is the graph that minimizes the number of
|
||||
edges needed with given node connectivity $k$ and node number $n$.
|
||||
|
||||
This smallest number of edges is known to be ceil($kn/2$) [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
k: integer
|
||||
The node connectivity of the generated graph
|
||||
|
||||
n: integer
|
||||
The number of nodes the generated graph is to contain
|
||||
|
||||
create_using : NetworkX graph constructor, optional Graph type
|
||||
to create (default=nx.Graph). If graph instance, then cleared
|
||||
before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The Harary graph $H_{k,n}$.
|
||||
|
||||
See Also
|
||||
--------
|
||||
hnm_harary_graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
This algorithm runs in $O(kn)$ time.
|
||||
It is implemented by following the Reference [2]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Weisstein, Eric W. "Harary Graph." From MathWorld--A Wolfram Web
|
||||
Resource. http://mathworld.wolfram.com/HararyGraph.html.
|
||||
|
||||
.. [2] Harary, F. "The Maximum Connectivity of a Graph."
|
||||
Proc. Nat. Acad. Sci. USA 48, 1142-1146, 1962.
|
||||
"""
|
||||
|
||||
if k < 1:
|
||||
raise NetworkXError("The node connectivity must be >= 1!")
|
||||
if n < k + 1:
|
||||
raise NetworkXError("The number of nodes must be >= k+1 !")
|
||||
|
||||
# in case of connectivity 1, simply return the path graph
|
||||
if k == 1:
|
||||
H = nx.path_graph(n, create_using)
|
||||
return H
|
||||
|
||||
# Construct an empty graph with n nodes first
|
||||
H = nx.empty_graph(n, create_using)
|
||||
|
||||
# Test the parity of k and n
|
||||
if (k % 2 == 0) or (n % 2 == 0):
|
||||
# Construct a regular graph with k degrees
|
||||
offset = k // 2
|
||||
for i in range(n):
|
||||
for j in range(1, offset + 1):
|
||||
H.add_edge(i, (i - j) % n)
|
||||
H.add_edge(i, (i + j) % n)
|
||||
if k & 1:
|
||||
# odd degree; n must be even in this case
|
||||
half = n // 2
|
||||
for i in range(half):
|
||||
# add edges diagonally
|
||||
H.add_edge(i, i + half)
|
||||
else:
|
||||
# Construct a regular graph with (k - 1) degrees
|
||||
offset = (k - 1) // 2
|
||||
for i in range(n):
|
||||
for j in range(1, offset + 1):
|
||||
H.add_edge(i, (i - j) % n)
|
||||
H.add_edge(i, (i + j) % n)
|
||||
half = n // 2
|
||||
for i in range(half + 1):
|
||||
# add half+1 edges between i and i+half
|
||||
H.add_edge(i, (i + half) % n)
|
||||
|
||||
return H
|
||||
441
.CondaPkg/env/Lib/site-packages/networkx/generators/internet_as_graphs.py
vendored
Normal file
441
.CondaPkg/env/Lib/site-packages/networkx/generators/internet_as_graphs.py
vendored
Normal file
@@ -0,0 +1,441 @@
|
||||
"""Generates graphs resembling the Internet Autonomous System network"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = ["random_internet_as_graph"]
|
||||
|
||||
|
||||
def uniform_int_from_avg(a, m, seed):
|
||||
"""Pick a random integer with uniform probability.
|
||||
|
||||
Returns a random integer uniformly taken from a distribution with
|
||||
minimum value 'a' and average value 'm', X~U(a,b), E[X]=m, X in N where
|
||||
b = 2*m - a.
|
||||
|
||||
Notes
|
||||
-----
|
||||
p = (b-floor(b))/2
|
||||
X = X1 + X2; X1~U(a,floor(b)), X2~B(p)
|
||||
E[X] = E[X1] + E[X2] = (floor(b)+a)/2 + (b-floor(b))/2 = (b+a)/2 = m
|
||||
"""
|
||||
|
||||
from math import floor
|
||||
|
||||
assert m >= a
|
||||
b = 2 * m - a
|
||||
p = (b - floor(b)) / 2
|
||||
X1 = round(seed.random() * (floor(b) - a) + a)
|
||||
if seed.random() < p:
|
||||
X2 = 1
|
||||
else:
|
||||
X2 = 0
|
||||
return X1 + X2
|
||||
|
||||
|
||||
def choose_pref_attach(degs, seed):
|
||||
"""Pick a random value, with a probability given by its weight.
|
||||
|
||||
Returns a random choice among degs keys, each of which has a
|
||||
probability proportional to the corresponding dictionary value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
degs: dictionary
|
||||
It contains the possible values (keys) and the corresponding
|
||||
probabilities (values)
|
||||
seed: random state
|
||||
|
||||
Returns
|
||||
-------
|
||||
v: object
|
||||
A key of degs or None if degs is empty
|
||||
"""
|
||||
|
||||
if len(degs) == 0:
|
||||
return None
|
||||
s = sum(degs.values())
|
||||
if s == 0:
|
||||
return seed.choice(list(degs.keys()))
|
||||
v = seed.random() * s
|
||||
|
||||
nodes = list(degs.keys())
|
||||
i = 0
|
||||
acc = degs[nodes[i]]
|
||||
while v > acc:
|
||||
i += 1
|
||||
acc += degs[nodes[i]]
|
||||
return nodes[i]
|
||||
|
||||
|
||||
class AS_graph_generator:
|
||||
"""Generates random internet AS graphs."""
|
||||
|
||||
def __init__(self, n, seed):
|
||||
"""Initializes variables. Immediate numbers are taken from [1].
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n: integer
|
||||
Number of graph nodes
|
||||
seed: random state
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
GG: AS_graph_generator object
|
||||
|
||||
References
|
||||
----------
|
||||
[1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
|
||||
BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
|
||||
in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
|
||||
"""
|
||||
|
||||
self.seed = seed
|
||||
self.n_t = min(n, round(self.seed.random() * 2 + 4)) # num of T nodes
|
||||
self.n_m = round(0.15 * n) # number of M nodes
|
||||
self.n_cp = round(0.05 * n) # number of CP nodes
|
||||
self.n_c = max(0, n - self.n_t - self.n_m - self.n_cp) # number of C nodes
|
||||
|
||||
self.d_m = 2 + (2.5 * n) / 10000 # average multihoming degree for M nodes
|
||||
self.d_cp = 2 + (1.5 * n) / 10000 # avg multihoming degree for CP nodes
|
||||
self.d_c = 1 + (5 * n) / 100000 # average multihoming degree for C nodes
|
||||
|
||||
self.p_m_m = 1 + (2 * n) / 10000 # avg num of peer edges between M and M
|
||||
self.p_cp_m = 0.2 + (2 * n) / 10000 # avg num of peer edges between CP, M
|
||||
self.p_cp_cp = 0.05 + (2 * n) / 100000 # avg num of peer edges btwn CP, CP
|
||||
|
||||
self.t_m = 0.375 # probability M's provider is T
|
||||
self.t_cp = 0.375 # probability CP's provider is T
|
||||
self.t_c = 0.125 # probability C's provider is T
|
||||
|
||||
def t_graph(self):
|
||||
"""Generates the core mesh network of tier one nodes of a AS graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: Networkx Graph
|
||||
Core network
|
||||
"""
|
||||
|
||||
self.G = nx.Graph()
|
||||
for i in range(self.n_t):
|
||||
self.G.add_node(i, type="T")
|
||||
for r in self.regions:
|
||||
self.regions[r].add(i)
|
||||
for j in self.G.nodes():
|
||||
if i != j:
|
||||
self.add_edge(i, j, "peer")
|
||||
self.customers[i] = set()
|
||||
self.providers[i] = set()
|
||||
return self.G
|
||||
|
||||
def add_edge(self, i, j, kind):
|
||||
if kind == "transit":
|
||||
customer = str(i)
|
||||
else:
|
||||
customer = "none"
|
||||
self.G.add_edge(i, j, type=kind, customer=customer)
|
||||
|
||||
def choose_peer_pref_attach(self, node_list):
|
||||
"""Pick a node with a probability weighted by its peer degree.
|
||||
|
||||
Pick a node from node_list with preferential attachment
|
||||
computed only on their peer degree
|
||||
"""
|
||||
|
||||
d = {}
|
||||
for n in node_list:
|
||||
d[n] = self.G.nodes[n]["peers"]
|
||||
return choose_pref_attach(d, self.seed)
|
||||
|
||||
def choose_node_pref_attach(self, node_list):
|
||||
"""Pick a node with a probability weighted by its degree.
|
||||
|
||||
Pick a node from node_list with preferential attachment
|
||||
computed on their degree
|
||||
"""
|
||||
|
||||
degs = dict(self.G.degree(node_list))
|
||||
return choose_pref_attach(degs, self.seed)
|
||||
|
||||
def add_customer(self, i, j):
|
||||
"""Keep the dictionaries 'customers' and 'providers' consistent."""
|
||||
|
||||
self.customers[j].add(i)
|
||||
self.providers[i].add(j)
|
||||
for z in self.providers[j]:
|
||||
self.customers[z].add(i)
|
||||
self.providers[i].add(z)
|
||||
|
||||
def add_node(self, i, kind, reg2prob, avg_deg, t_edge_prob):
|
||||
"""Add a node and its customer transit edges to the graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i: object
|
||||
Identifier of the new node
|
||||
kind: string
|
||||
Type of the new node. Options are: 'M' for middle node, 'CP' for
|
||||
content provider and 'C' for customer.
|
||||
reg2prob: float
|
||||
Probability the new node can be in two different regions.
|
||||
avg_deg: float
|
||||
Average number of transit nodes of which node i is customer.
|
||||
t_edge_prob: float
|
||||
Probability node i establish a customer transit edge with a tier
|
||||
one (T) node
|
||||
|
||||
Returns
|
||||
-------
|
||||
i: object
|
||||
Identifier of the new node
|
||||
"""
|
||||
|
||||
regs = 1 # regions in which node resides
|
||||
if self.seed.random() < reg2prob: # node is in two regions
|
||||
regs = 2
|
||||
node_options = set()
|
||||
|
||||
self.G.add_node(i, type=kind, peers=0)
|
||||
self.customers[i] = set()
|
||||
self.providers[i] = set()
|
||||
self.nodes[kind].add(i)
|
||||
for r in self.seed.sample(list(self.regions), regs):
|
||||
node_options = node_options.union(self.regions[r])
|
||||
self.regions[r].add(i)
|
||||
|
||||
edge_num = uniform_int_from_avg(1, avg_deg, self.seed)
|
||||
|
||||
t_options = node_options.intersection(self.nodes["T"])
|
||||
m_options = node_options.intersection(self.nodes["M"])
|
||||
if i in m_options:
|
||||
m_options.remove(i)
|
||||
d = 0
|
||||
while d < edge_num and (len(t_options) > 0 or len(m_options) > 0):
|
||||
if len(m_options) == 0 or (
|
||||
len(t_options) > 0 and self.seed.random() < t_edge_prob
|
||||
): # add edge to a T node
|
||||
j = self.choose_node_pref_attach(t_options)
|
||||
t_options.remove(j)
|
||||
else:
|
||||
j = self.choose_node_pref_attach(m_options)
|
||||
m_options.remove(j)
|
||||
self.add_edge(i, j, "transit")
|
||||
self.add_customer(i, j)
|
||||
d += 1
|
||||
|
||||
return i
|
||||
|
||||
def add_m_peering_link(self, m, to_kind):
|
||||
"""Add a peering link between two middle tier (M) nodes.
|
||||
|
||||
Target node j is drawn considering a preferential attachment based on
|
||||
other M node peering degree.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
m: object
|
||||
Node identifier
|
||||
to_kind: string
|
||||
type for target node j (must be always M)
|
||||
|
||||
Returns
|
||||
-------
|
||||
success: boolean
|
||||
"""
|
||||
|
||||
# candidates are of type 'M' and are not customers of m
|
||||
node_options = self.nodes["M"].difference(self.customers[m])
|
||||
# candidates are not providers of m
|
||||
node_options = node_options.difference(self.providers[m])
|
||||
# remove self
|
||||
if m in node_options:
|
||||
node_options.remove(m)
|
||||
|
||||
# remove candidates we are already connected to
|
||||
for j in self.G.neighbors(m):
|
||||
if j in node_options:
|
||||
node_options.remove(j)
|
||||
|
||||
if len(node_options) > 0:
|
||||
j = self.choose_peer_pref_attach(node_options)
|
||||
self.add_edge(m, j, "peer")
|
||||
self.G.nodes[m]["peers"] += 1
|
||||
self.G.nodes[j]["peers"] += 1
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def add_cp_peering_link(self, cp, to_kind):
|
||||
"""Add a peering link to a content provider (CP) node.
|
||||
|
||||
Target node j can be CP or M and it is drawn uniformly among the nodes
|
||||
belonging to the same region as cp.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cp: object
|
||||
Node identifier
|
||||
to_kind: string
|
||||
type for target node j (must be M or CP)
|
||||
|
||||
Returns
|
||||
-------
|
||||
success: boolean
|
||||
"""
|
||||
|
||||
node_options = set()
|
||||
for r in self.regions: # options include nodes in the same region(s)
|
||||
if cp in self.regions[r]:
|
||||
node_options = node_options.union(self.regions[r])
|
||||
|
||||
# options are restricted to the indicated kind ('M' or 'CP')
|
||||
node_options = self.nodes[to_kind].intersection(node_options)
|
||||
|
||||
# remove self
|
||||
if cp in node_options:
|
||||
node_options.remove(cp)
|
||||
|
||||
# remove nodes that are cp's providers
|
||||
node_options = node_options.difference(self.providers[cp])
|
||||
|
||||
# remove nodes we are already connected to
|
||||
for j in self.G.neighbors(cp):
|
||||
if j in node_options:
|
||||
node_options.remove(j)
|
||||
|
||||
if len(node_options) > 0:
|
||||
j = self.seed.sample(list(node_options), 1)[0]
|
||||
self.add_edge(cp, j, "peer")
|
||||
self.G.nodes[cp]["peers"] += 1
|
||||
self.G.nodes[j]["peers"] += 1
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def graph_regions(self, rn):
|
||||
"""Initializes AS network regions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rn: integer
|
||||
Number of regions
|
||||
"""
|
||||
|
||||
self.regions = {}
|
||||
for i in range(rn):
|
||||
self.regions["REG" + str(i)] = set()
|
||||
|
||||
def add_peering_links(self, from_kind, to_kind):
|
||||
"""Utility function to add peering links among node groups."""
|
||||
peer_link_method = None
|
||||
if from_kind == "M":
|
||||
peer_link_method = self.add_m_peering_link
|
||||
m = self.p_m_m
|
||||
if from_kind == "CP":
|
||||
peer_link_method = self.add_cp_peering_link
|
||||
if to_kind == "M":
|
||||
m = self.p_cp_m
|
||||
else:
|
||||
m = self.p_cp_cp
|
||||
|
||||
for i in self.nodes[from_kind]:
|
||||
num = uniform_int_from_avg(0, m, self.seed)
|
||||
for _ in range(num):
|
||||
peer_link_method(i, to_kind)
|
||||
|
||||
def generate(self):
|
||||
"""Generates a random AS network graph as described in [1].
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: Graph object
|
||||
|
||||
Notes
|
||||
-----
|
||||
The process steps are the following: first we create the core network
|
||||
of tier one nodes, then we add the middle tier (M), the content
|
||||
provider (CP) and the customer (C) nodes along with their transit edges
|
||||
(link i,j means i is customer of j). Finally we add peering links
|
||||
between M nodes, between M and CP nodes and between CP node couples.
|
||||
For a detailed description of the algorithm, please refer to [1].
|
||||
|
||||
References
|
||||
----------
|
||||
[1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
|
||||
BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
|
||||
in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
|
||||
"""
|
||||
|
||||
self.graph_regions(5)
|
||||
self.customers = {}
|
||||
self.providers = {}
|
||||
self.nodes = {"T": set(), "M": set(), "CP": set(), "C": set()}
|
||||
|
||||
self.t_graph()
|
||||
self.nodes["T"] = set(self.G.nodes())
|
||||
|
||||
i = len(self.nodes["T"])
|
||||
for _ in range(self.n_m):
|
||||
self.nodes["M"].add(self.add_node(i, "M", 0.2, self.d_m, self.t_m))
|
||||
i += 1
|
||||
for _ in range(self.n_cp):
|
||||
self.nodes["CP"].add(self.add_node(i, "CP", 0.05, self.d_cp, self.t_cp))
|
||||
i += 1
|
||||
for _ in range(self.n_c):
|
||||
self.nodes["C"].add(self.add_node(i, "C", 0, self.d_c, self.t_c))
|
||||
i += 1
|
||||
|
||||
self.add_peering_links("M", "M")
|
||||
self.add_peering_links("CP", "M")
|
||||
self.add_peering_links("CP", "CP")
|
||||
|
||||
return self.G
|
||||
|
||||
|
||||
@py_random_state(1)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_internet_as_graph(n, seed=None):
|
||||
"""Generates a random undirected graph resembling the Internet AS network
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n: integer in [1000, 10000]
|
||||
Number of graph nodes
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G: Networkx Graph object
|
||||
A randomly generated undirected graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
This algorithm returns an undirected graph resembling the Internet
|
||||
Autonomous System (AS) network, it uses the approach by Elmokashfi et al.
|
||||
[1]_ and it grants the properties described in the related paper [1]_.
|
||||
|
||||
Each node models an autonomous system, with an attribute 'type' specifying
|
||||
its kind; tier-1 (T), mid-level (M), customer (C) or content-provider (CP).
|
||||
Each edge models an ADV communication link (hence, bidirectional) with
|
||||
attributes:
|
||||
|
||||
- type: transit|peer, the kind of commercial agreement between nodes;
|
||||
- customer: <node id>, the identifier of the node acting as customer
|
||||
('none' if type is peer).
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] A. Elmokashfi, A. Kvalbein and C. Dovrolis, "On the Scalability of
|
||||
BGP: The Role of Topology Growth," in IEEE Journal on Selected Areas
|
||||
in Communications, vol. 28, no. 8, pp. 1250-1261, October 2010.
|
||||
"""
|
||||
|
||||
GG = AS_graph_generator(n, seed)
|
||||
G = GG.generate()
|
||||
return G
|
||||
124
.CondaPkg/env/Lib/site-packages/networkx/generators/intersection.py
vendored
Normal file
124
.CondaPkg/env/Lib/site-packages/networkx/generators/intersection.py
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
Generators for random intersection graphs.
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = [
|
||||
"uniform_random_intersection_graph",
|
||||
"k_random_intersection_graph",
|
||||
"general_random_intersection_graph",
|
||||
]
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def uniform_random_intersection_graph(n, m, p, seed=None):
|
||||
"""Returns a uniform random intersection graph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes in the first bipartite set (nodes)
|
||||
m : int
|
||||
The number of nodes in the second bipartite set (attributes)
|
||||
p : float
|
||||
Probability of connecting nodes between bipartite sets
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
gnp_random_graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] K.B. Singer-Cohen, Random Intersection Graphs, 1995,
|
||||
PhD thesis, Johns Hopkins University
|
||||
.. [2] Fill, J. A., Scheinerman, E. R., and Singer-Cohen, K. B.,
|
||||
Random intersection graphs when m = !(n):
|
||||
An equivalence theorem relating the evolution of the g(n, m, p)
|
||||
and g(n, p) models. Random Struct. Algorithms 16, 2 (2000), 156–176.
|
||||
"""
|
||||
from networkx.algorithms import bipartite
|
||||
|
||||
G = bipartite.random_graph(n, m, p, seed)
|
||||
return nx.projected_graph(G, range(n))
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def k_random_intersection_graph(n, m, k, seed=None):
|
||||
"""Returns a intersection graph with randomly chosen attribute sets for
|
||||
each node that are of equal size (k).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes in the first bipartite set (nodes)
|
||||
m : int
|
||||
The number of nodes in the second bipartite set (attributes)
|
||||
k : float
|
||||
Size of attribute set to assign to each node.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
gnp_random_graph, uniform_random_intersection_graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Godehardt, E., and Jaworski, J.
|
||||
Two models of random intersection graphs and their applications.
|
||||
Electronic Notes in Discrete Mathematics 10 (2001), 129--132.
|
||||
"""
|
||||
G = nx.empty_graph(n + m)
|
||||
mset = range(n, n + m)
|
||||
for v in range(n):
|
||||
targets = seed.sample(mset, k)
|
||||
G.add_edges_from(zip([v] * len(targets), targets))
|
||||
return nx.projected_graph(G, range(n))
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def general_random_intersection_graph(n, m, p, seed=None):
|
||||
"""Returns a random intersection graph with independent probabilities
|
||||
for connections between node and attribute sets.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The number of nodes in the first bipartite set (nodes)
|
||||
m : int
|
||||
The number of nodes in the second bipartite set (attributes)
|
||||
p : list of floats of length m
|
||||
Probabilities for connecting nodes to each attribute
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
gnp_random_graph, uniform_random_intersection_graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Nikoletseas, S. E., Raptopoulos, C., and Spirakis, P. G.
|
||||
The existence and efficient construction of large independent sets
|
||||
in general random intersection graphs. In ICALP (2004), J. D´ıaz,
|
||||
J. Karhum¨aki, A. Lepist¨o, and D. Sannella, Eds., vol. 3142
|
||||
of Lecture Notes in Computer Science, Springer, pp. 1029–1040.
|
||||
"""
|
||||
if len(p) != m:
|
||||
raise ValueError("Probability list p must have m elements.")
|
||||
G = nx.empty_graph(n + m)
|
||||
mset = range(n, n + m)
|
||||
for u in range(n):
|
||||
for v, q in zip(mset, p):
|
||||
if seed.random() < q:
|
||||
G.add_edge(u, v)
|
||||
return nx.projected_graph(G, range(n))
|
||||
69
.CondaPkg/env/Lib/site-packages/networkx/generators/interval_graph.py
vendored
Normal file
69
.CondaPkg/env/Lib/site-packages/networkx/generators/interval_graph.py
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Generators for interval graph.
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["interval_graph"]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def interval_graph(intervals):
|
||||
"""Generates an interval graph for a list of intervals given.
|
||||
|
||||
In graph theory, an interval graph is an undirected graph formed from a set
|
||||
of closed intervals on the real line, with a vertex for each interval
|
||||
and an edge between vertices whose intervals intersect.
|
||||
It is the intersection graph of the intervals.
|
||||
|
||||
More information can be found at:
|
||||
https://en.wikipedia.org/wiki/Interval_graph
|
||||
|
||||
Parameters
|
||||
----------
|
||||
intervals : a sequence of intervals, say (l, r) where l is the left end,
|
||||
and r is the right end of the closed interval.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx graph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> intervals = [(-2, 3), [1, 4], (2, 3), (4, 6)]
|
||||
>>> G = nx.interval_graph(intervals)
|
||||
>>> sorted(G.edges)
|
||||
[((-2, 3), (1, 4)), ((-2, 3), (2, 3)), ((1, 4), (2, 3)), ((1, 4), (4, 6))]
|
||||
|
||||
Raises
|
||||
------
|
||||
:exc:`TypeError`
|
||||
if `intervals` contains None or an element which is not
|
||||
collections.abc.Sequence or not a length of 2.
|
||||
:exc:`ValueError`
|
||||
if `intervals` contains an interval such that min1 > max1
|
||||
where min1,max1 = interval
|
||||
"""
|
||||
intervals = list(intervals)
|
||||
for interval in intervals:
|
||||
if not (isinstance(interval, Sequence) and len(interval) == 2):
|
||||
raise TypeError(
|
||||
"Each interval must have length 2, and be a "
|
||||
"collections.abc.Sequence such as tuple or list."
|
||||
)
|
||||
if interval[0] > interval[1]:
|
||||
raise ValueError(f"Interval must have lower value first. Got {interval}")
|
||||
|
||||
graph = nx.Graph()
|
||||
|
||||
tupled_intervals = [tuple(interval) for interval in intervals]
|
||||
graph.add_nodes_from(tupled_intervals)
|
||||
|
||||
while tupled_intervals:
|
||||
min1, max1 = interval1 = tupled_intervals.pop()
|
||||
for interval2 in tupled_intervals:
|
||||
min2, max2 = interval2
|
||||
if max1 >= min2 and max2 >= min1:
|
||||
graph.add_edge(interval1, interval2)
|
||||
return graph
|
||||
664
.CondaPkg/env/Lib/site-packages/networkx/generators/joint_degree_seq.py
vendored
Normal file
664
.CondaPkg/env/Lib/site-packages/networkx/generators/joint_degree_seq.py
vendored
Normal file
@@ -0,0 +1,664 @@
|
||||
"""Generate graphs with a given joint degree and directed joint degree"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = [
|
||||
"is_valid_joint_degree",
|
||||
"is_valid_directed_joint_degree",
|
||||
"joint_degree_graph",
|
||||
"directed_joint_degree_graph",
|
||||
]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None)
|
||||
def is_valid_joint_degree(joint_degrees):
|
||||
"""Checks whether the given joint degree dictionary is realizable.
|
||||
|
||||
A *joint degree dictionary* is a dictionary of dictionaries, in
|
||||
which entry ``joint_degrees[k][l]`` is an integer representing the
|
||||
number of edges joining nodes of degree *k* with nodes of degree
|
||||
*l*. Such a dictionary is realizable as a simple graph if and only
|
||||
if the following conditions are satisfied.
|
||||
|
||||
- each entry must be an integer,
|
||||
- the total number of nodes of degree *k*, computed by
|
||||
``sum(joint_degrees[k].values()) / k``, must be an integer,
|
||||
- the total number of edges joining nodes of degree *k* with
|
||||
nodes of degree *l* cannot exceed the total number of possible edges,
|
||||
- each diagonal entry ``joint_degrees[k][k]`` must be even (this is
|
||||
a convention assumed by the :func:`joint_degree_graph` function).
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
joint_degrees : dictionary of dictionary of integers
|
||||
A joint degree dictionary in which entry ``joint_degrees[k][l]``
|
||||
is the number of edges joining nodes of degree *k* with nodes of
|
||||
degree *l*.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the given joint degree dictionary is realizable as a
|
||||
simple graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M. Gjoka, M. Kurant, A. Markopoulou, "2.5K Graphs: from Sampling
|
||||
to Generation", IEEE Infocom, 2013.
|
||||
.. [2] I. Stanton, A. Pinar, "Constructing and sampling graphs with a
|
||||
prescribed joint degree distribution", Journal of Experimental
|
||||
Algorithmics, 2012.
|
||||
"""
|
||||
|
||||
degree_count = {}
|
||||
for k in joint_degrees:
|
||||
if k > 0:
|
||||
k_size = sum(joint_degrees[k].values()) / k
|
||||
if not k_size.is_integer():
|
||||
return False
|
||||
degree_count[k] = k_size
|
||||
|
||||
for k in joint_degrees:
|
||||
for l in joint_degrees[k]:
|
||||
if not float(joint_degrees[k][l]).is_integer():
|
||||
return False
|
||||
|
||||
if (k != l) and (joint_degrees[k][l] > degree_count[k] * degree_count[l]):
|
||||
return False
|
||||
elif k == l:
|
||||
if joint_degrees[k][k] > degree_count[k] * (degree_count[k] - 1):
|
||||
return False
|
||||
if joint_degrees[k][k] % 2 != 0:
|
||||
return False
|
||||
|
||||
# if all above conditions have been satisfied then the input
|
||||
# joint degree is realizable as a simple graph.
|
||||
return True
|
||||
|
||||
|
||||
def _neighbor_switch(G, w, unsat, h_node_residual, avoid_node_id=None):
|
||||
"""Releases one free stub for ``w``, while preserving joint degree in G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
Graph in which the neighbor switch will take place.
|
||||
w : integer
|
||||
Node id for which we will execute this neighbor switch.
|
||||
unsat : set of integers
|
||||
Set of unsaturated node ids that have the same degree as w.
|
||||
h_node_residual: dictionary of integers
|
||||
Keeps track of the remaining stubs for a given node.
|
||||
avoid_node_id: integer
|
||||
Node id to avoid when selecting w_prime.
|
||||
|
||||
Notes
|
||||
-----
|
||||
First, it selects *w_prime*, an unsaturated node that has the same degree
|
||||
as ``w``. Second, it selects *switch_node*, a neighbor node of ``w`` that
|
||||
is not connected to *w_prime*. Then it executes an edge swap i.e. removes
|
||||
(``w``,*switch_node*) and adds (*w_prime*,*switch_node*). Gjoka et. al. [1]
|
||||
prove that such an edge swap is always possible.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M. Gjoka, B. Tillman, A. Markopoulou, "Construction of Simple
|
||||
Graphs with a Target Joint Degree Matrix and Beyond", IEEE Infocom, '15
|
||||
"""
|
||||
|
||||
if (avoid_node_id is None) or (h_node_residual[avoid_node_id] > 1):
|
||||
# select unsaturated node w_prime that has the same degree as w
|
||||
w_prime = next(iter(unsat))
|
||||
else:
|
||||
# assume that the node pair (v,w) has been selected for connection. if
|
||||
# - neighbor_switch is called for node w,
|
||||
# - nodes v and w have the same degree,
|
||||
# - node v=avoid_node_id has only one stub left,
|
||||
# then prevent v=avoid_node_id from being selected as w_prime.
|
||||
|
||||
iter_var = iter(unsat)
|
||||
while True:
|
||||
w_prime = next(iter_var)
|
||||
if w_prime != avoid_node_id:
|
||||
break
|
||||
|
||||
# select switch_node, a neighbor of w, that is not connected to w_prime
|
||||
w_prime_neighbs = G[w_prime] # slightly faster declaring this variable
|
||||
for v in G[w]:
|
||||
if (v not in w_prime_neighbs) and (v != w_prime):
|
||||
switch_node = v
|
||||
break
|
||||
|
||||
# remove edge (w,switch_node), add edge (w_prime,switch_node) and update
|
||||
# data structures
|
||||
G.remove_edge(w, switch_node)
|
||||
G.add_edge(w_prime, switch_node)
|
||||
h_node_residual[w] += 1
|
||||
h_node_residual[w_prime] -= 1
|
||||
if h_node_residual[w_prime] == 0:
|
||||
unsat.remove(w_prime)
|
||||
|
||||
|
||||
@py_random_state(1)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def joint_degree_graph(joint_degrees, seed=None):
|
||||
"""Generates a random simple graph with the given joint degree dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
joint_degrees : dictionary of dictionary of integers
|
||||
A joint degree dictionary in which entry ``joint_degrees[k][l]`` is the
|
||||
number of edges joining nodes of degree *k* with nodes of degree *l*.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
A graph with the specified joint degree dictionary.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If *joint_degrees* dictionary is not realizable.
|
||||
|
||||
Notes
|
||||
-----
|
||||
In each iteration of the "while loop" the algorithm picks two disconnected
|
||||
nodes *v* and *w*, of degree *k* and *l* correspondingly, for which
|
||||
``joint_degrees[k][l]`` has not reached its target yet. It then adds
|
||||
edge (*v*, *w*) and increases the number of edges in graph G by one.
|
||||
|
||||
The intelligence of the algorithm lies in the fact that it is always
|
||||
possible to add an edge between such disconnected nodes *v* and *w*,
|
||||
even if one or both nodes do not have free stubs. That is made possible by
|
||||
executing a "neighbor switch", an edge rewiring move that releases
|
||||
a free stub while keeping the joint degree of G the same.
|
||||
|
||||
The algorithm continues for E (number of edges) iterations of
|
||||
the "while loop", at the which point all entries of the given
|
||||
``joint_degrees[k][l]`` have reached their target values and the
|
||||
construction is complete.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] M. Gjoka, B. Tillman, A. Markopoulou, "Construction of Simple
|
||||
Graphs with a Target Joint Degree Matrix and Beyond", IEEE Infocom, '15
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> joint_degrees = {
|
||||
... 1: {4: 1},
|
||||
... 2: {2: 2, 3: 2, 4: 2},
|
||||
... 3: {2: 2, 4: 1},
|
||||
... 4: {1: 1, 2: 2, 3: 1},
|
||||
... }
|
||||
>>> G = nx.joint_degree_graph(joint_degrees)
|
||||
>>>
|
||||
"""
|
||||
|
||||
if not is_valid_joint_degree(joint_degrees):
|
||||
msg = "Input joint degree dict not realizable as a simple graph"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
# compute degree count from joint_degrees
|
||||
degree_count = {k: sum(l.values()) // k for k, l in joint_degrees.items() if k > 0}
|
||||
|
||||
# start with empty N-node graph
|
||||
N = sum(degree_count.values())
|
||||
G = nx.empty_graph(N)
|
||||
|
||||
# for a given degree group, keep the list of all node ids
|
||||
h_degree_nodelist = {}
|
||||
|
||||
# for a given node, keep track of the remaining stubs
|
||||
h_node_residual = {}
|
||||
|
||||
# populate h_degree_nodelist and h_node_residual
|
||||
nodeid = 0
|
||||
for degree, num_nodes in degree_count.items():
|
||||
h_degree_nodelist[degree] = range(nodeid, nodeid + num_nodes)
|
||||
for v in h_degree_nodelist[degree]:
|
||||
h_node_residual[v] = degree
|
||||
nodeid += int(num_nodes)
|
||||
|
||||
# iterate over every degree pair (k,l) and add the number of edges given
|
||||
# for each pair
|
||||
for k in joint_degrees:
|
||||
for l in joint_degrees[k]:
|
||||
# n_edges_add is the number of edges to add for the
|
||||
# degree pair (k,l)
|
||||
n_edges_add = joint_degrees[k][l]
|
||||
|
||||
if (n_edges_add > 0) and (k >= l):
|
||||
# number of nodes with degree k and l
|
||||
k_size = degree_count[k]
|
||||
l_size = degree_count[l]
|
||||
|
||||
# k_nodes and l_nodes consist of all nodes of degree k and l
|
||||
k_nodes = h_degree_nodelist[k]
|
||||
l_nodes = h_degree_nodelist[l]
|
||||
|
||||
# k_unsat and l_unsat consist of nodes of degree k and l that
|
||||
# are unsaturated (nodes that have at least 1 available stub)
|
||||
k_unsat = {v for v in k_nodes if h_node_residual[v] > 0}
|
||||
|
||||
if k != l:
|
||||
l_unsat = {w for w in l_nodes if h_node_residual[w] > 0}
|
||||
else:
|
||||
l_unsat = k_unsat
|
||||
n_edges_add = joint_degrees[k][l] // 2
|
||||
|
||||
while n_edges_add > 0:
|
||||
# randomly pick nodes v and w that have degrees k and l
|
||||
v = k_nodes[seed.randrange(k_size)]
|
||||
w = l_nodes[seed.randrange(l_size)]
|
||||
|
||||
# if nodes v and w are disconnected then attempt to connect
|
||||
if not G.has_edge(v, w) and (v != w):
|
||||
# if node v has no free stubs then do neighbor switch
|
||||
if h_node_residual[v] == 0:
|
||||
_neighbor_switch(G, v, k_unsat, h_node_residual)
|
||||
|
||||
# if node w has no free stubs then do neighbor switch
|
||||
if h_node_residual[w] == 0:
|
||||
if k != l:
|
||||
_neighbor_switch(G, w, l_unsat, h_node_residual)
|
||||
else:
|
||||
_neighbor_switch(
|
||||
G, w, l_unsat, h_node_residual, avoid_node_id=v
|
||||
)
|
||||
|
||||
# add edge (v, w) and update data structures
|
||||
G.add_edge(v, w)
|
||||
h_node_residual[v] -= 1
|
||||
h_node_residual[w] -= 1
|
||||
n_edges_add -= 1
|
||||
|
||||
if h_node_residual[v] == 0:
|
||||
k_unsat.discard(v)
|
||||
if h_node_residual[w] == 0:
|
||||
l_unsat.discard(w)
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None)
|
||||
def is_valid_directed_joint_degree(in_degrees, out_degrees, nkk):
|
||||
"""Checks whether the given directed joint degree input is realizable
|
||||
|
||||
Parameters
|
||||
----------
|
||||
in_degrees : list of integers
|
||||
in degree sequence contains the in degrees of nodes.
|
||||
out_degrees : list of integers
|
||||
out degree sequence contains the out degrees of nodes.
|
||||
nkk : dictionary of dictionary of integers
|
||||
directed joint degree dictionary. for nodes of out degree k (first
|
||||
level of dict) and nodes of in degree l (second level of dict)
|
||||
describes the number of edges.
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean
|
||||
returns true if given input is realizable, else returns false.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Here is the list of conditions that the inputs (in/out degree sequences,
|
||||
nkk) need to satisfy for simple directed graph realizability:
|
||||
|
||||
- Condition 0: in_degrees and out_degrees have the same length
|
||||
- Condition 1: nkk[k][l] is integer for all k,l
|
||||
- Condition 2: sum(nkk[k])/k = number of nodes with partition id k, is an
|
||||
integer and matching degree sequence
|
||||
- Condition 3: number of edges and non-chords between k and l cannot exceed
|
||||
maximum possible number of edges
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
[1] B. Tillman, A. Markopoulou, C. T. Butts & M. Gjoka,
|
||||
"Construction of Directed 2K Graphs". In Proc. of KDD 2017.
|
||||
"""
|
||||
V = {} # number of nodes with in/out degree.
|
||||
forbidden = {}
|
||||
if len(in_degrees) != len(out_degrees):
|
||||
return False
|
||||
|
||||
for idx in range(len(in_degrees)):
|
||||
i = in_degrees[idx]
|
||||
o = out_degrees[idx]
|
||||
V[(i, 0)] = V.get((i, 0), 0) + 1
|
||||
V[(o, 1)] = V.get((o, 1), 0) + 1
|
||||
|
||||
forbidden[(o, i)] = forbidden.get((o, i), 0) + 1
|
||||
|
||||
S = {} # number of edges going from in/out degree nodes.
|
||||
for k in nkk:
|
||||
for l in nkk[k]:
|
||||
val = nkk[k][l]
|
||||
if not float(val).is_integer(): # condition 1
|
||||
return False
|
||||
|
||||
if val > 0:
|
||||
S[(k, 1)] = S.get((k, 1), 0) + val
|
||||
S[(l, 0)] = S.get((l, 0), 0) + val
|
||||
# condition 3
|
||||
if val + forbidden.get((k, l), 0) > V[(k, 1)] * V[(l, 0)]:
|
||||
return False
|
||||
|
||||
return all(S[s] / s[0] == V[s] for s in S)
|
||||
|
||||
|
||||
def _directed_neighbor_switch(
|
||||
G, w, unsat, h_node_residual_out, chords, h_partition_in, partition
|
||||
):
|
||||
"""Releases one free stub for node w, while preserving joint degree in G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : networkx directed graph
|
||||
graph within which the edge swap will take place.
|
||||
w : integer
|
||||
node id for which we need to perform a neighbor switch.
|
||||
unsat: set of integers
|
||||
set of node ids that have the same degree as w and are unsaturated.
|
||||
h_node_residual_out: dict of integers
|
||||
for a given node, keeps track of the remaining stubs to be added.
|
||||
chords: set of tuples
|
||||
keeps track of available positions to add edges.
|
||||
h_partition_in: dict of integers
|
||||
for a given node, keeps track of its partition id (in degree).
|
||||
partition: integer
|
||||
partition id to check if chords have to be updated.
|
||||
|
||||
Notes
|
||||
-----
|
||||
First, it selects node w_prime that (1) has the same degree as w and
|
||||
(2) is unsaturated. Then, it selects node v, a neighbor of w, that is
|
||||
not connected to w_prime and does an edge swap i.e. removes (w,v) and
|
||||
adds (w_prime,v). If neighbor switch is not possible for w using
|
||||
w_prime and v, then return w_prime; in [1] it's proven that
|
||||
such unsaturated nodes can be used.
|
||||
|
||||
References
|
||||
----------
|
||||
[1] B. Tillman, A. Markopoulou, C. T. Butts & M. Gjoka,
|
||||
"Construction of Directed 2K Graphs". In Proc. of KDD 2017.
|
||||
"""
|
||||
w_prime = unsat.pop()
|
||||
unsat.add(w_prime)
|
||||
# select node t, a neighbor of w, that is not connected to w_prime
|
||||
w_neighbs = list(G.successors(w))
|
||||
# slightly faster declaring this variable
|
||||
w_prime_neighbs = list(G.successors(w_prime))
|
||||
|
||||
for v in w_neighbs:
|
||||
if (v not in w_prime_neighbs) and w_prime != v:
|
||||
# removes (w,v), add (w_prime,v) and update data structures
|
||||
G.remove_edge(w, v)
|
||||
G.add_edge(w_prime, v)
|
||||
|
||||
if h_partition_in[v] == partition:
|
||||
chords.add((w, v))
|
||||
chords.discard((w_prime, v))
|
||||
|
||||
h_node_residual_out[w] += 1
|
||||
h_node_residual_out[w_prime] -= 1
|
||||
if h_node_residual_out[w_prime] == 0:
|
||||
unsat.remove(w_prime)
|
||||
return None
|
||||
|
||||
# If neighbor switch didn't work, use unsaturated node
|
||||
return w_prime
|
||||
|
||||
|
||||
def _directed_neighbor_switch_rev(
|
||||
G, w, unsat, h_node_residual_in, chords, h_partition_out, partition
|
||||
):
|
||||
"""The reverse of directed_neighbor_switch.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : networkx directed graph
|
||||
graph within which the edge swap will take place.
|
||||
w : integer
|
||||
node id for which we need to perform a neighbor switch.
|
||||
unsat: set of integers
|
||||
set of node ids that have the same degree as w and are unsaturated.
|
||||
h_node_residual_in: dict of integers
|
||||
for a given node, keeps track of the remaining stubs to be added.
|
||||
chords: set of tuples
|
||||
keeps track of available positions to add edges.
|
||||
h_partition_out: dict of integers
|
||||
for a given node, keeps track of its partition id (out degree).
|
||||
partition: integer
|
||||
partition id to check if chords have to be updated.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Same operation as directed_neighbor_switch except it handles this operation
|
||||
for incoming edges instead of outgoing.
|
||||
"""
|
||||
w_prime = unsat.pop()
|
||||
unsat.add(w_prime)
|
||||
# slightly faster declaring these as variables.
|
||||
w_neighbs = list(G.predecessors(w))
|
||||
w_prime_neighbs = list(G.predecessors(w_prime))
|
||||
# select node v, a neighbor of w, that is not connected to w_prime.
|
||||
for v in w_neighbs:
|
||||
if (v not in w_prime_neighbs) and w_prime != v:
|
||||
# removes (v,w), add (v,w_prime) and update data structures.
|
||||
G.remove_edge(v, w)
|
||||
G.add_edge(v, w_prime)
|
||||
if h_partition_out[v] == partition:
|
||||
chords.add((v, w))
|
||||
chords.discard((v, w_prime))
|
||||
|
||||
h_node_residual_in[w] += 1
|
||||
h_node_residual_in[w_prime] -= 1
|
||||
if h_node_residual_in[w_prime] == 0:
|
||||
unsat.remove(w_prime)
|
||||
return None
|
||||
|
||||
# If neighbor switch didn't work, use the unsaturated node.
|
||||
return w_prime
|
||||
|
||||
|
||||
@py_random_state(3)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def directed_joint_degree_graph(in_degrees, out_degrees, nkk, seed=None):
|
||||
"""Generates a random simple directed graph with the joint degree.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
degree_seq : list of tuples (of size 3)
|
||||
degree sequence contains tuples of nodes with node id, in degree and
|
||||
out degree.
|
||||
nkk : dictionary of dictionary of integers
|
||||
directed joint degree dictionary, for nodes of out degree k (first
|
||||
level of dict) and nodes of in degree l (second level of dict)
|
||||
describes the number of edges.
|
||||
seed : hashable object, optional
|
||||
Seed for random number generator.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
A directed graph with the specified inputs.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If degree_seq and nkk are not realizable as a simple directed graph.
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
Similarly to the undirected version:
|
||||
In each iteration of the "while loop" the algorithm picks two disconnected
|
||||
nodes v and w, of degree k and l correspondingly, for which nkk[k][l] has
|
||||
not reached its target yet i.e. (for given k,l): n_edges_add < nkk[k][l].
|
||||
It then adds edge (v,w) and always increases the number of edges in graph G
|
||||
by one.
|
||||
|
||||
The intelligence of the algorithm lies in the fact that it is always
|
||||
possible to add an edge between disconnected nodes v and w, for which
|
||||
nkk[degree(v)][degree(w)] has not reached its target, even if one or both
|
||||
nodes do not have free stubs. If either node v or w does not have a free
|
||||
stub, we perform a "neighbor switch", an edge rewiring move that releases a
|
||||
free stub while keeping nkk the same.
|
||||
|
||||
The difference for the directed version lies in the fact that neighbor
|
||||
switches might not be able to rewire, but in these cases unsaturated nodes
|
||||
can be reassigned to use instead, see [1] for detailed description and
|
||||
proofs.
|
||||
|
||||
The algorithm continues for E (number of edges in the graph) iterations of
|
||||
the "while loop", at which point all entries of the given nkk[k][l] have
|
||||
reached their target values and the construction is complete.
|
||||
|
||||
References
|
||||
----------
|
||||
[1] B. Tillman, A. Markopoulou, C. T. Butts & M. Gjoka,
|
||||
"Construction of Directed 2K Graphs". In Proc. of KDD 2017.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> in_degrees = [0, 1, 1, 2]
|
||||
>>> out_degrees = [1, 1, 1, 1]
|
||||
>>> nkk = {1: {1: 2, 2: 2}}
|
||||
>>> G = nx.directed_joint_degree_graph(in_degrees, out_degrees, nkk)
|
||||
>>>
|
||||
"""
|
||||
if not is_valid_directed_joint_degree(in_degrees, out_degrees, nkk):
|
||||
msg = "Input is not realizable as a simple graph"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
# start with an empty directed graph.
|
||||
G = nx.DiGraph()
|
||||
|
||||
# for a given group, keep the list of all node ids.
|
||||
h_degree_nodelist_in = {}
|
||||
h_degree_nodelist_out = {}
|
||||
# for a given group, keep the list of all unsaturated node ids.
|
||||
h_degree_nodelist_in_unsat = {}
|
||||
h_degree_nodelist_out_unsat = {}
|
||||
# for a given node, keep track of the remaining stubs to be added.
|
||||
h_node_residual_out = {}
|
||||
h_node_residual_in = {}
|
||||
# for a given node, keep track of the partition id.
|
||||
h_partition_out = {}
|
||||
h_partition_in = {}
|
||||
# keep track of non-chords between pairs of partition ids.
|
||||
non_chords = {}
|
||||
|
||||
# populate data structures
|
||||
for idx, i in enumerate(in_degrees):
|
||||
idx = int(idx)
|
||||
if i > 0:
|
||||
h_degree_nodelist_in.setdefault(i, [])
|
||||
h_degree_nodelist_in_unsat.setdefault(i, set())
|
||||
h_degree_nodelist_in[i].append(idx)
|
||||
h_degree_nodelist_in_unsat[i].add(idx)
|
||||
h_node_residual_in[idx] = i
|
||||
h_partition_in[idx] = i
|
||||
|
||||
for idx, o in enumerate(out_degrees):
|
||||
o = out_degrees[idx]
|
||||
non_chords[(o, in_degrees[idx])] = non_chords.get((o, in_degrees[idx]), 0) + 1
|
||||
idx = int(idx)
|
||||
if o > 0:
|
||||
h_degree_nodelist_out.setdefault(o, [])
|
||||
h_degree_nodelist_out_unsat.setdefault(o, set())
|
||||
h_degree_nodelist_out[o].append(idx)
|
||||
h_degree_nodelist_out_unsat[o].add(idx)
|
||||
h_node_residual_out[idx] = o
|
||||
h_partition_out[idx] = o
|
||||
|
||||
G.add_node(idx)
|
||||
|
||||
nk_in = {}
|
||||
nk_out = {}
|
||||
for p in h_degree_nodelist_in:
|
||||
nk_in[p] = len(h_degree_nodelist_in[p])
|
||||
for p in h_degree_nodelist_out:
|
||||
nk_out[p] = len(h_degree_nodelist_out[p])
|
||||
|
||||
# iterate over every degree pair (k,l) and add the number of edges given
|
||||
# for each pair.
|
||||
for k in nkk:
|
||||
for l in nkk[k]:
|
||||
n_edges_add = nkk[k][l]
|
||||
|
||||
if n_edges_add > 0:
|
||||
# chords contains a random set of potential edges.
|
||||
chords = set()
|
||||
|
||||
k_len = nk_out[k]
|
||||
l_len = nk_in[l]
|
||||
chords_sample = seed.sample(
|
||||
range(k_len * l_len), n_edges_add + non_chords.get((k, l), 0)
|
||||
)
|
||||
|
||||
num = 0
|
||||
while len(chords) < n_edges_add:
|
||||
i = h_degree_nodelist_out[k][chords_sample[num] % k_len]
|
||||
j = h_degree_nodelist_in[l][chords_sample[num] // k_len]
|
||||
num += 1
|
||||
if i != j:
|
||||
chords.add((i, j))
|
||||
|
||||
# k_unsat and l_unsat consist of nodes of in/out degree k and l
|
||||
# that are unsaturated i.e. those nodes that have at least one
|
||||
# available stub
|
||||
k_unsat = h_degree_nodelist_out_unsat[k]
|
||||
l_unsat = h_degree_nodelist_in_unsat[l]
|
||||
|
||||
while n_edges_add > 0:
|
||||
v, w = chords.pop()
|
||||
chords.add((v, w))
|
||||
|
||||
# if node v has no free stubs then do neighbor switch.
|
||||
if h_node_residual_out[v] == 0:
|
||||
_v = _directed_neighbor_switch(
|
||||
G,
|
||||
v,
|
||||
k_unsat,
|
||||
h_node_residual_out,
|
||||
chords,
|
||||
h_partition_in,
|
||||
l,
|
||||
)
|
||||
if _v is not None:
|
||||
v = _v
|
||||
|
||||
# if node w has no free stubs then do neighbor switch.
|
||||
if h_node_residual_in[w] == 0:
|
||||
_w = _directed_neighbor_switch_rev(
|
||||
G,
|
||||
w,
|
||||
l_unsat,
|
||||
h_node_residual_in,
|
||||
chords,
|
||||
h_partition_out,
|
||||
k,
|
||||
)
|
||||
if _w is not None:
|
||||
w = _w
|
||||
|
||||
# add edge (v,w) and update data structures.
|
||||
G.add_edge(v, w)
|
||||
h_node_residual_out[v] -= 1
|
||||
h_node_residual_in[w] -= 1
|
||||
n_edges_add -= 1
|
||||
chords.discard((v, w))
|
||||
|
||||
if h_node_residual_out[v] == 0:
|
||||
k_unsat.discard(v)
|
||||
if h_node_residual_in[w] == 0:
|
||||
l_unsat.discard(w)
|
||||
return G
|
||||
367
.CondaPkg/env/Lib/site-packages/networkx/generators/lattice.py
vendored
Normal file
367
.CondaPkg/env/Lib/site-packages/networkx/generators/lattice.py
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
"""Functions for generating grid graphs and lattices
|
||||
|
||||
The :func:`grid_2d_graph`, :func:`triangular_lattice_graph`, and
|
||||
:func:`hexagonal_lattice_graph` functions correspond to the three
|
||||
`regular tilings of the plane`_, the square, triangular, and hexagonal
|
||||
tilings, respectively. :func:`grid_graph` and :func:`hypercube_graph`
|
||||
are similar for arbitrary dimensions. Useful relevant discussion can
|
||||
be found about `Triangular Tiling`_, and `Square, Hex and Triangle Grids`_
|
||||
|
||||
.. _regular tilings of the plane: https://en.wikipedia.org/wiki/List_of_regular_polytopes_and_compounds#Euclidean_tilings
|
||||
.. _Square, Hex and Triangle Grids: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/
|
||||
.. _Triangular Tiling: https://en.wikipedia.org/wiki/Triangular_tiling
|
||||
|
||||
"""
|
||||
|
||||
from itertools import repeat
|
||||
from math import sqrt
|
||||
|
||||
import networkx as nx
|
||||
from networkx.classes import set_node_attributes
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.generators.classic import cycle_graph, empty_graph, path_graph
|
||||
from networkx.relabel import relabel_nodes
|
||||
from networkx.utils import flatten, nodes_or_number, pairwise
|
||||
|
||||
__all__ = [
|
||||
"grid_2d_graph",
|
||||
"grid_graph",
|
||||
"hypercube_graph",
|
||||
"triangular_lattice_graph",
|
||||
"hexagonal_lattice_graph",
|
||||
]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
@nodes_or_number([0, 1])
|
||||
def grid_2d_graph(m, n, periodic=False, create_using=None):
|
||||
"""Returns the two-dimensional grid graph.
|
||||
|
||||
The grid graph has each node connected to its four nearest neighbors.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
m, n : int or iterable container of nodes
|
||||
If an integer, nodes are from `range(n)`.
|
||||
If a container, elements become the coordinate of the nodes.
|
||||
|
||||
periodic : bool or iterable
|
||||
If `periodic` is True, both dimensions are periodic. If False, none
|
||||
are periodic. If `periodic` is iterable, it should yield 2 bool
|
||||
values indicating whether the 1st and 2nd axes, respectively, are
|
||||
periodic.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The (possibly periodic) grid graph of the specified dimensions.
|
||||
|
||||
"""
|
||||
G = empty_graph(0, create_using)
|
||||
row_name, rows = m
|
||||
col_name, cols = n
|
||||
G.add_nodes_from((i, j) for i in rows for j in cols)
|
||||
G.add_edges_from(((i, j), (pi, j)) for pi, i in pairwise(rows) for j in cols)
|
||||
G.add_edges_from(((i, j), (i, pj)) for i in rows for pj, j in pairwise(cols))
|
||||
|
||||
try:
|
||||
periodic_r, periodic_c = periodic
|
||||
except TypeError:
|
||||
periodic_r = periodic_c = periodic
|
||||
|
||||
if periodic_r and len(rows) > 2:
|
||||
first = rows[0]
|
||||
last = rows[-1]
|
||||
G.add_edges_from(((first, j), (last, j)) for j in cols)
|
||||
if periodic_c and len(cols) > 2:
|
||||
first = cols[0]
|
||||
last = cols[-1]
|
||||
G.add_edges_from(((i, first), (i, last)) for i in rows)
|
||||
# both directions for directed
|
||||
if G.is_directed():
|
||||
G.add_edges_from((v, u) for u, v in G.edges())
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def grid_graph(dim, periodic=False):
|
||||
"""Returns the *n*-dimensional grid graph.
|
||||
|
||||
The dimension *n* is the length of the list `dim` and the size in
|
||||
each dimension is the value of the corresponding list element.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dim : list or tuple of numbers or iterables of nodes
|
||||
'dim' is a tuple or list with, for each dimension, either a number
|
||||
that is the size of that dimension or an iterable of nodes for
|
||||
that dimension. The dimension of the grid_graph is the length
|
||||
of `dim`.
|
||||
|
||||
periodic : bool or iterable
|
||||
If `periodic` is True, all dimensions are periodic. If False all
|
||||
dimensions are not periodic. If `periodic` is iterable, it should
|
||||
yield `dim` bool values each of which indicates whether the
|
||||
corresponding axis is periodic.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The (possibly periodic) grid graph of the specified dimensions.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To produce a 2 by 3 by 4 grid graph, a graph on 24 nodes:
|
||||
|
||||
>>> from networkx import grid_graph
|
||||
>>> G = grid_graph(dim=(2, 3, 4))
|
||||
>>> len(G)
|
||||
24
|
||||
>>> G = grid_graph(dim=(range(7, 9), range(3, 6)))
|
||||
>>> len(G)
|
||||
6
|
||||
"""
|
||||
from networkx.algorithms.operators.product import cartesian_product
|
||||
|
||||
if not dim:
|
||||
return empty_graph(0)
|
||||
|
||||
try:
|
||||
func = (cycle_graph if p else path_graph for p in periodic)
|
||||
except TypeError:
|
||||
func = repeat(cycle_graph if periodic else path_graph)
|
||||
|
||||
G = next(func)(dim[0])
|
||||
for current_dim in dim[1:]:
|
||||
Gnew = next(func)(current_dim)
|
||||
G = cartesian_product(Gnew, G)
|
||||
# graph G is done but has labels of the form (1, (2, (3, 1))) so relabel
|
||||
H = relabel_nodes(G, flatten)
|
||||
return H
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def hypercube_graph(n):
|
||||
"""Returns the *n*-dimensional hypercube graph.
|
||||
|
||||
The nodes are the integers between 0 and ``2 ** n - 1``, inclusive.
|
||||
|
||||
For more information on the hypercube graph, see the Wikipedia
|
||||
article `Hypercube graph`_.
|
||||
|
||||
.. _Hypercube graph: https://en.wikipedia.org/wiki/Hypercube_graph
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The dimension of the hypercube.
|
||||
The number of nodes in the graph will be ``2 ** n``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The hypercube graph of dimension *n*.
|
||||
"""
|
||||
dim = n * [2]
|
||||
G = grid_graph(dim)
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def triangular_lattice_graph(
|
||||
m, n, periodic=False, with_positions=True, create_using=None
|
||||
):
|
||||
r"""Returns the $m$ by $n$ triangular lattice graph.
|
||||
|
||||
The `triangular lattice graph`_ is a two-dimensional `grid graph`_ in
|
||||
which each square unit has a diagonal edge (each grid unit has a chord).
|
||||
|
||||
The returned graph has $m$ rows and $n$ columns of triangles. Rows and
|
||||
columns include both triangles pointing up and down. Rows form a strip
|
||||
of constant height. Columns form a series of diamond shapes, staggered
|
||||
with the columns on either side. Another way to state the size is that
|
||||
the nodes form a grid of `m+1` rows and `(n + 1) // 2` columns.
|
||||
The odd row nodes are shifted horizontally relative to the even rows.
|
||||
|
||||
Directed graph types have edges pointed up or right.
|
||||
|
||||
Positions of nodes are computed by default or `with_positions is True`.
|
||||
The position of each node (embedded in a euclidean plane) is stored in
|
||||
the graph using equilateral triangles with sidelength 1.
|
||||
The height between rows of nodes is thus $\sqrt(3)/2$.
|
||||
Nodes lie in the first quadrant with the node $(0, 0)$ at the origin.
|
||||
|
||||
.. _triangular lattice graph: http://mathworld.wolfram.com/TriangularGrid.html
|
||||
.. _grid graph: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/
|
||||
.. _Triangular Tiling: https://en.wikipedia.org/wiki/Triangular_tiling
|
||||
|
||||
Parameters
|
||||
----------
|
||||
m : int
|
||||
The number of rows in the lattice.
|
||||
|
||||
n : int
|
||||
The number of columns in the lattice.
|
||||
|
||||
periodic : bool (default: False)
|
||||
If True, join the boundary vertices of the grid using periodic
|
||||
boundary conditions. The join between boundaries is the final row
|
||||
and column of triangles. This means there is one row and one column
|
||||
fewer nodes for the periodic lattice. Periodic lattices require
|
||||
`m >= 3`, `n >= 5` and are allowed but misaligned if `m` or `n` are odd
|
||||
|
||||
with_positions : bool (default: True)
|
||||
Store the coordinates of each node in the graph node attribute 'pos'.
|
||||
The coordinates provide a lattice with equilateral triangles.
|
||||
Periodic positions shift the nodes vertically in a nonlinear way so
|
||||
the edges don't overlap so much.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The *m* by *n* triangular lattice graph.
|
||||
"""
|
||||
H = empty_graph(0, create_using)
|
||||
if n == 0 or m == 0:
|
||||
return H
|
||||
if periodic:
|
||||
if n < 5 or m < 3:
|
||||
msg = f"m > 2 and n > 4 required for periodic. m={m}, n={n}"
|
||||
raise NetworkXError(msg)
|
||||
|
||||
N = (n + 1) // 2 # number of nodes in row
|
||||
rows = range(m + 1)
|
||||
cols = range(N + 1)
|
||||
# Make grid
|
||||
H.add_edges_from(((i, j), (i + 1, j)) for j in rows for i in cols[:N])
|
||||
H.add_edges_from(((i, j), (i, j + 1)) for j in rows[:m] for i in cols)
|
||||
# add diagonals
|
||||
H.add_edges_from(((i, j), (i + 1, j + 1)) for j in rows[1:m:2] for i in cols[:N])
|
||||
H.add_edges_from(((i + 1, j), (i, j + 1)) for j in rows[:m:2] for i in cols[:N])
|
||||
# identify boundary nodes if periodic
|
||||
from networkx.algorithms.minors import contracted_nodes
|
||||
|
||||
if periodic is True:
|
||||
for i in cols:
|
||||
H = contracted_nodes(H, (i, 0), (i, m))
|
||||
for j in rows[:m]:
|
||||
H = contracted_nodes(H, (0, j), (N, j))
|
||||
elif n % 2:
|
||||
# remove extra nodes
|
||||
H.remove_nodes_from((N, j) for j in rows[1::2])
|
||||
|
||||
# Add position node attributes
|
||||
if with_positions:
|
||||
ii = (i for i in cols for j in rows)
|
||||
jj = (j for i in cols for j in rows)
|
||||
xx = (0.5 * (j % 2) + i for i in cols for j in rows)
|
||||
h = sqrt(3) / 2
|
||||
if periodic:
|
||||
yy = (h * j + 0.01 * i * i for i in cols for j in rows)
|
||||
else:
|
||||
yy = (h * j for i in cols for j in rows)
|
||||
pos = {(i, j): (x, y) for i, j, x, y in zip(ii, jj, xx, yy) if (i, j) in H}
|
||||
set_node_attributes(H, pos, "pos")
|
||||
return H
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def hexagonal_lattice_graph(
|
||||
m, n, periodic=False, with_positions=True, create_using=None
|
||||
):
|
||||
"""Returns an `m` by `n` hexagonal lattice graph.
|
||||
|
||||
The *hexagonal lattice graph* is a graph whose nodes and edges are
|
||||
the `hexagonal tiling`_ of the plane.
|
||||
|
||||
The returned graph will have `m` rows and `n` columns of hexagons.
|
||||
`Odd numbered columns`_ are shifted up relative to even numbered columns.
|
||||
|
||||
Positions of nodes are computed by default or `with_positions is True`.
|
||||
Node positions creating the standard embedding in the plane
|
||||
with sidelength 1 and are stored in the node attribute 'pos'.
|
||||
`pos = nx.get_node_attributes(G, 'pos')` creates a dict ready for drawing.
|
||||
|
||||
.. _hexagonal tiling: https://en.wikipedia.org/wiki/Hexagonal_tiling
|
||||
.. _Odd numbered columns: http://www-cs-students.stanford.edu/~amitp/game-programming/grids/
|
||||
|
||||
Parameters
|
||||
----------
|
||||
m : int
|
||||
The number of rows of hexagons in the lattice.
|
||||
|
||||
n : int
|
||||
The number of columns of hexagons in the lattice.
|
||||
|
||||
periodic : bool
|
||||
Whether to make a periodic grid by joining the boundary vertices.
|
||||
For this to work `n` must be even and both `n > 1` and `m > 1`.
|
||||
The periodic connections create another row and column of hexagons
|
||||
so these graphs have fewer nodes as boundary nodes are identified.
|
||||
|
||||
with_positions : bool (default: True)
|
||||
Store the coordinates of each node in the graph node attribute 'pos'.
|
||||
The coordinates provide a lattice with vertical columns of hexagons
|
||||
offset to interleave and cover the plane.
|
||||
Periodic positions shift the nodes vertically in a nonlinear way so
|
||||
the edges don't overlap so much.
|
||||
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
If graph is directed, edges will point up or right.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The *m* by *n* hexagonal lattice graph.
|
||||
"""
|
||||
G = empty_graph(0, create_using)
|
||||
if m == 0 or n == 0:
|
||||
return G
|
||||
if periodic and (n % 2 == 1 or m < 2 or n < 2):
|
||||
msg = "periodic hexagonal lattice needs m > 1, n > 1 and even n"
|
||||
raise NetworkXError(msg)
|
||||
|
||||
M = 2 * m # twice as many nodes as hexagons vertically
|
||||
rows = range(M + 2)
|
||||
cols = range(n + 1)
|
||||
# make lattice
|
||||
col_edges = (((i, j), (i, j + 1)) for i in cols for j in rows[: M + 1])
|
||||
row_edges = (((i, j), (i + 1, j)) for i in cols[:n] for j in rows if i % 2 == j % 2)
|
||||
G.add_edges_from(col_edges)
|
||||
G.add_edges_from(row_edges)
|
||||
# Remove corner nodes with one edge
|
||||
G.remove_node((0, M + 1))
|
||||
G.remove_node((n, (M + 1) * (n % 2)))
|
||||
|
||||
# identify boundary nodes if periodic
|
||||
from networkx.algorithms.minors import contracted_nodes
|
||||
|
||||
if periodic:
|
||||
for i in cols[:n]:
|
||||
G = contracted_nodes(G, (i, 0), (i, M))
|
||||
for i in cols[1:]:
|
||||
G = contracted_nodes(G, (i, 1), (i, M + 1))
|
||||
for j in rows[1:M]:
|
||||
G = contracted_nodes(G, (0, j), (n, j))
|
||||
G.remove_node((n, M))
|
||||
|
||||
# calc position in embedded space
|
||||
ii = (i for i in cols for j in rows)
|
||||
jj = (j for i in cols for j in rows)
|
||||
xx = (0.5 + i + i // 2 + (j % 2) * ((i % 2) - 0.5) for i in cols for j in rows)
|
||||
h = sqrt(3) / 2
|
||||
if periodic:
|
||||
yy = (h * j + 0.01 * i * i for i in cols for j in rows)
|
||||
else:
|
||||
yy = (h * j for i in cols for j in rows)
|
||||
# exclude nodes not in G
|
||||
pos = {(i, j): (x, y) for i, j, x, y in zip(ii, jj, xx, yy) if (i, j) in G}
|
||||
set_node_attributes(G, pos, "pos")
|
||||
return G
|
||||
499
.CondaPkg/env/Lib/site-packages/networkx/generators/line.py
vendored
Normal file
499
.CondaPkg/env/Lib/site-packages/networkx/generators/line.py
vendored
Normal file
@@ -0,0 +1,499 @@
|
||||
"""Functions for generating line graphs."""
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
from itertools import combinations
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import arbitrary_element
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = ["line_graph", "inverse_line_graph"]
|
||||
|
||||
|
||||
@nx._dispatchable(returns_graph=True)
|
||||
def line_graph(G, create_using=None):
|
||||
r"""Returns the line graph of the graph or digraph `G`.
|
||||
|
||||
The line graph of a graph `G` has a node for each edge in `G` and an
|
||||
edge joining those nodes if the two edges in `G` share a common node. For
|
||||
directed graphs, nodes are adjacent exactly when the edges they represent
|
||||
form a directed path of length two.
|
||||
|
||||
The nodes of the line graph are 2-tuples of nodes in the original graph (or
|
||||
3-tuples for multigraphs, with the key of the edge as the third element).
|
||||
|
||||
For information about self-loops and more discussion, see the **Notes**
|
||||
section below.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX Graph, DiGraph, MultiGraph, or MultiDigraph.
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
L : graph
|
||||
The line graph of G.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.star_graph(3)
|
||||
>>> L = nx.line_graph(G)
|
||||
>>> print(sorted(map(sorted, L.edges()))) # makes a 3-clique, K3
|
||||
[[(0, 1), (0, 2)], [(0, 1), (0, 3)], [(0, 2), (0, 3)]]
|
||||
|
||||
Edge attributes from `G` are not copied over as node attributes in `L`, but
|
||||
attributes can be copied manually:
|
||||
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> G.add_edges_from((u, v, {"tot": u + v}) for u, v in G.edges)
|
||||
>>> G.edges(data=True)
|
||||
EdgeDataView([(0, 1, {'tot': 1}), (1, 2, {'tot': 3}), (2, 3, {'tot': 5})])
|
||||
>>> H = nx.line_graph(G)
|
||||
>>> H.add_nodes_from((node, G.edges[node]) for node in H)
|
||||
>>> H.nodes(data=True)
|
||||
NodeDataView({(0, 1): {'tot': 1}, (2, 3): {'tot': 5}, (1, 2): {'tot': 3}})
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph, node, and edge data are not propagated to the new graph. For
|
||||
undirected graphs, the nodes in G must be sortable, otherwise the
|
||||
constructed line graph may not be correct.
|
||||
|
||||
*Self-loops in undirected graphs*
|
||||
|
||||
For an undirected graph `G` without multiple edges, each edge can be
|
||||
written as a set `\{u, v\}`. Its line graph `L` has the edges of `G` as
|
||||
its nodes. If `x` and `y` are two nodes in `L`, then `\{x, y\}` is an edge
|
||||
in `L` if and only if the intersection of `x` and `y` is nonempty. Thus,
|
||||
the set of all edges is determined by the set of all pairwise intersections
|
||||
of edges in `G`.
|
||||
|
||||
Trivially, every edge in G would have a nonzero intersection with itself,
|
||||
and so every node in `L` should have a self-loop. This is not so
|
||||
interesting, and the original context of line graphs was with simple
|
||||
graphs, which had no self-loops or multiple edges. The line graph was also
|
||||
meant to be a simple graph and thus, self-loops in `L` are not part of the
|
||||
standard definition of a line graph. In a pairwise intersection matrix,
|
||||
this is analogous to excluding the diagonal entries from the line graph
|
||||
definition.
|
||||
|
||||
Self-loops and multiple edges in `G` add nodes to `L` in a natural way, and
|
||||
do not require any fundamental changes to the definition. It might be
|
||||
argued that the self-loops we excluded before should now be included.
|
||||
However, the self-loops are still "trivial" in some sense and thus, are
|
||||
usually excluded.
|
||||
|
||||
*Self-loops in directed graphs*
|
||||
|
||||
For a directed graph `G` without multiple edges, each edge can be written
|
||||
as a tuple `(u, v)`. Its line graph `L` has the edges of `G` as its
|
||||
nodes. If `x` and `y` are two nodes in `L`, then `(x, y)` is an edge in `L`
|
||||
if and only if the tail of `x` matches the head of `y`, for example, if `x
|
||||
= (a, b)` and `y = (b, c)` for some vertices `a`, `b`, and `c` in `G`.
|
||||
|
||||
Due to the directed nature of the edges, it is no longer the case that
|
||||
every edge in `G` should have a self-loop in `L`. Now, the only time
|
||||
self-loops arise is if a node in `G` itself has a self-loop. So such
|
||||
self-loops are no longer "trivial" but instead, represent essential
|
||||
features of the topology of `G`. For this reason, the historical
|
||||
development of line digraphs is such that self-loops are included. When the
|
||||
graph `G` has multiple edges, once again only superficial changes are
|
||||
required to the definition.
|
||||
|
||||
References
|
||||
----------
|
||||
* Harary, Frank, and Norman, Robert Z., "Some properties of line digraphs",
|
||||
Rend. Circ. Mat. Palermo, II. Ser. 9 (1960), 161--168.
|
||||
* Hemminger, R. L.; Beineke, L. W. (1978), "Line graphs and line digraphs",
|
||||
in Beineke, L. W.; Wilson, R. J., Selected Topics in Graph Theory,
|
||||
Academic Press Inc., pp. 271--305.
|
||||
|
||||
"""
|
||||
if G.is_directed():
|
||||
L = _lg_directed(G, create_using=create_using)
|
||||
else:
|
||||
L = _lg_undirected(G, selfloops=False, create_using=create_using)
|
||||
return L
|
||||
|
||||
|
||||
def _lg_directed(G, create_using=None):
|
||||
"""Returns the line graph L of the (multi)digraph G.
|
||||
|
||||
Edges in G appear as nodes in L, represented as tuples of the form (u,v)
|
||||
or (u,v,key) if G is a multidigraph. A node in L corresponding to the edge
|
||||
(u,v) is connected to every node corresponding to an edge (v,w).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : digraph
|
||||
A directed graph or directed multigraph.
|
||||
create_using : NetworkX graph constructor, optional
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
Default is to use the same graph class as `G`.
|
||||
|
||||
"""
|
||||
L = nx.empty_graph(0, create_using, default=G.__class__)
|
||||
|
||||
# Create a graph specific edge function.
|
||||
get_edges = partial(G.edges, keys=True) if G.is_multigraph() else G.edges
|
||||
|
||||
for from_node in get_edges():
|
||||
# from_node is: (u,v) or (u,v,key)
|
||||
L.add_node(from_node)
|
||||
for to_node in get_edges(from_node[1]):
|
||||
L.add_edge(from_node, to_node)
|
||||
|
||||
return L
|
||||
|
||||
|
||||
def _lg_undirected(G, selfloops=False, create_using=None):
|
||||
"""Returns the line graph L of the (multi)graph G.
|
||||
|
||||
Edges in G appear as nodes in L, represented as sorted tuples of the form
|
||||
(u,v), or (u,v,key) if G is a multigraph. A node in L corresponding to
|
||||
the edge {u,v} is connected to every node corresponding to an edge that
|
||||
involves u or v.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
An undirected graph or multigraph.
|
||||
selfloops : bool
|
||||
If `True`, then self-loops are included in the line graph. If `False`,
|
||||
they are excluded.
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The standard algorithm for line graphs of undirected graphs does not
|
||||
produce self-loops.
|
||||
|
||||
"""
|
||||
L = nx.empty_graph(0, create_using, default=G.__class__)
|
||||
|
||||
# Graph specific functions for edges.
|
||||
get_edges = partial(G.edges, keys=True) if G.is_multigraph() else G.edges
|
||||
|
||||
# Determine if we include self-loops or not.
|
||||
shift = 0 if selfloops else 1
|
||||
|
||||
# Introduce numbering of nodes
|
||||
node_index = {n: i for i, n in enumerate(G)}
|
||||
|
||||
# Lift canonical representation of nodes to edges in line graph
|
||||
edge_key_function = lambda edge: (node_index[edge[0]], node_index[edge[1]])
|
||||
|
||||
edges = set()
|
||||
for u in G:
|
||||
# Label nodes as a sorted tuple of nodes in original graph.
|
||||
# Decide on representation of {u, v} as (u, v) or (v, u) depending on node_index.
|
||||
# -> This ensures a canonical representation and avoids comparing values of different types.
|
||||
nodes = [tuple(sorted(x[:2], key=node_index.get)) + x[2:] for x in get_edges(u)]
|
||||
|
||||
if len(nodes) == 1:
|
||||
# Then the edge will be an isolated node in L.
|
||||
L.add_node(nodes[0])
|
||||
|
||||
# Add a clique of `nodes` to graph. To prevent double adding edges,
|
||||
# especially important for multigraphs, we store the edges in
|
||||
# canonical form in a set.
|
||||
for i, a in enumerate(nodes):
|
||||
edges.update(
|
||||
[
|
||||
tuple(sorted((a, b), key=edge_key_function))
|
||||
for b in nodes[i + shift :]
|
||||
]
|
||||
)
|
||||
|
||||
L.add_edges_from(edges)
|
||||
return L
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable(returns_graph=True)
|
||||
def inverse_line_graph(G):
|
||||
"""Returns the inverse line graph of graph G.
|
||||
|
||||
If H is a graph, and G is the line graph of H, such that G = L(H).
|
||||
Then H is the inverse line graph of G.
|
||||
|
||||
Not all graphs are line graphs and these do not have an inverse line graph.
|
||||
In these cases this function raises a NetworkXError.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX Graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
H : graph
|
||||
The inverse line graph of G.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is directed or a multigraph
|
||||
|
||||
NetworkXError
|
||||
If G is not a line graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
This is an implementation of the Roussopoulos algorithm[1]_.
|
||||
|
||||
If G consists of multiple components, then the algorithm doesn't work.
|
||||
You should invert every component separately:
|
||||
|
||||
>>> K5 = nx.complete_graph(5)
|
||||
>>> P4 = nx.Graph([("a", "b"), ("b", "c"), ("c", "d")])
|
||||
>>> G = nx.union(K5, P4)
|
||||
>>> root_graphs = []
|
||||
>>> for comp in nx.connected_components(G):
|
||||
... root_graphs.append(nx.inverse_line_graph(G.subgraph(comp)))
|
||||
>>> len(root_graphs)
|
||||
2
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Roussopoulos, N.D. , "A max {m, n} algorithm for determining the graph H from
|
||||
its line graph G", Information Processing Letters 2, (1973), 108--112, ISSN 0020-0190,
|
||||
`DOI link <https://doi.org/10.1016/0020-0190(73)90029-X>`_
|
||||
|
||||
"""
|
||||
if G.number_of_nodes() == 0:
|
||||
return nx.empty_graph(1)
|
||||
elif G.number_of_nodes() == 1:
|
||||
v = arbitrary_element(G)
|
||||
a = (v, 0)
|
||||
b = (v, 1)
|
||||
H = nx.Graph([(a, b)])
|
||||
return H
|
||||
elif G.number_of_nodes() > 1 and G.number_of_edges() == 0:
|
||||
msg = (
|
||||
"inverse_line_graph() doesn't work on an edgeless graph. "
|
||||
"Please use this function on each component separately."
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
if nx.number_of_selfloops(G) != 0:
|
||||
msg = (
|
||||
"A line graph as generated by NetworkX has no selfloops, so G has no "
|
||||
"inverse line graph. Please remove the selfloops from G and try again."
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
starting_cell = _select_starting_cell(G)
|
||||
P = _find_partition(G, starting_cell)
|
||||
# count how many times each vertex appears in the partition set
|
||||
P_count = {u: 0 for u in G.nodes}
|
||||
for p in P:
|
||||
for u in p:
|
||||
P_count[u] += 1
|
||||
|
||||
if max(P_count.values()) > 2:
|
||||
msg = "G is not a line graph (vertex found in more than two partition cells)"
|
||||
raise nx.NetworkXError(msg)
|
||||
W = tuple((u,) for u in P_count if P_count[u] == 1)
|
||||
H = nx.Graph()
|
||||
H.add_nodes_from(P)
|
||||
H.add_nodes_from(W)
|
||||
for a, b in combinations(H.nodes, 2):
|
||||
if any(a_bit in b for a_bit in a):
|
||||
H.add_edge(a, b)
|
||||
return H
|
||||
|
||||
|
||||
def _triangles(G, e):
|
||||
"""Return list of all triangles containing edge e"""
|
||||
u, v = e
|
||||
if u not in G:
|
||||
raise nx.NetworkXError(f"Vertex {u} not in graph")
|
||||
if v not in G[u]:
|
||||
raise nx.NetworkXError(f"Edge ({u}, {v}) not in graph")
|
||||
triangle_list = []
|
||||
for x in G[u]:
|
||||
if x in G[v]:
|
||||
triangle_list.append((u, v, x))
|
||||
return triangle_list
|
||||
|
||||
|
||||
def _odd_triangle(G, T):
|
||||
"""Test whether T is an odd triangle in G
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
T : 3-tuple of vertices forming triangle in G
|
||||
|
||||
Returns
|
||||
-------
|
||||
True is T is an odd triangle
|
||||
False otherwise
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
T is not a triangle in G
|
||||
|
||||
Notes
|
||||
-----
|
||||
An odd triangle is one in which there exists another vertex in G which is
|
||||
adjacent to either exactly one or exactly all three of the vertices in the
|
||||
triangle.
|
||||
|
||||
"""
|
||||
for u in T:
|
||||
if u not in G.nodes():
|
||||
raise nx.NetworkXError(f"Vertex {u} not in graph")
|
||||
for e in list(combinations(T, 2)):
|
||||
if e[0] not in G[e[1]]:
|
||||
raise nx.NetworkXError(f"Edge ({e[0]}, {e[1]}) not in graph")
|
||||
|
||||
T_nbrs = defaultdict(int)
|
||||
for t in T:
|
||||
for v in G[t]:
|
||||
if v not in T:
|
||||
T_nbrs[v] += 1
|
||||
return any(T_nbrs[v] in [1, 3] for v in T_nbrs)
|
||||
|
||||
|
||||
def _find_partition(G, starting_cell):
|
||||
"""Find a partition of the vertices of G into cells of complete graphs
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
starting_cell : tuple of vertices in G which form a cell
|
||||
|
||||
Returns
|
||||
-------
|
||||
List of tuples of vertices of G
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If a cell is not a complete subgraph then G is not a line graph
|
||||
"""
|
||||
G_partition = G.copy()
|
||||
P = [starting_cell] # partition set
|
||||
G_partition.remove_edges_from(list(combinations(starting_cell, 2)))
|
||||
# keep list of partitioned nodes which might have an edge in G_partition
|
||||
partitioned_vertices = list(starting_cell)
|
||||
while G_partition.number_of_edges() > 0:
|
||||
# there are still edges left and so more cells to be made
|
||||
u = partitioned_vertices.pop()
|
||||
deg_u = len(G_partition[u])
|
||||
if deg_u != 0:
|
||||
# if u still has edges then we need to find its other cell
|
||||
# this other cell must be a complete subgraph or else G is
|
||||
# not a line graph
|
||||
new_cell = [u] + list(G_partition[u])
|
||||
for u in new_cell:
|
||||
for v in new_cell:
|
||||
if (u != v) and (v not in G_partition[u]):
|
||||
msg = (
|
||||
"G is not a line graph "
|
||||
"(partition cell not a complete subgraph)"
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
P.append(tuple(new_cell))
|
||||
G_partition.remove_edges_from(list(combinations(new_cell, 2)))
|
||||
partitioned_vertices += new_cell
|
||||
return P
|
||||
|
||||
|
||||
def _select_starting_cell(G, starting_edge=None):
|
||||
"""Select a cell to initiate _find_partition
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
starting_edge: an edge to build the starting cell from
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple of vertices in G
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If it is determined that G is not a line graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
If starting edge not specified then pick an arbitrary edge - doesn't
|
||||
matter which. However, this function may call itself requiring a
|
||||
specific starting edge. Note that the r, s notation for counting
|
||||
triangles is the same as in the Roussopoulos paper cited above.
|
||||
"""
|
||||
if starting_edge is None:
|
||||
e = arbitrary_element(G.edges())
|
||||
else:
|
||||
e = starting_edge
|
||||
if e[0] not in G.nodes():
|
||||
raise nx.NetworkXError(f"Vertex {e[0]} not in graph")
|
||||
if e[1] not in G[e[0]]:
|
||||
msg = f"starting_edge ({e[0]}, {e[1]}) is not in the Graph"
|
||||
raise nx.NetworkXError(msg)
|
||||
e_triangles = _triangles(G, e)
|
||||
r = len(e_triangles)
|
||||
if r == 0:
|
||||
# there are no triangles containing e, so the starting cell is just e
|
||||
starting_cell = e
|
||||
elif r == 1:
|
||||
# there is exactly one triangle, T, containing e. If other 2 edges
|
||||
# of T belong only to this triangle then T is starting cell
|
||||
T = e_triangles[0]
|
||||
a, b, c = T
|
||||
# ab was original edge so check the other 2 edges
|
||||
ac_edges = len(_triangles(G, (a, c)))
|
||||
bc_edges = len(_triangles(G, (b, c)))
|
||||
if ac_edges == 1:
|
||||
if bc_edges == 1:
|
||||
starting_cell = T
|
||||
else:
|
||||
return _select_starting_cell(G, starting_edge=(b, c))
|
||||
else:
|
||||
return _select_starting_cell(G, starting_edge=(a, c))
|
||||
else:
|
||||
# r >= 2 so we need to count the number of odd triangles, s
|
||||
s = 0
|
||||
odd_triangles = []
|
||||
for T in e_triangles:
|
||||
if _odd_triangle(G, T):
|
||||
s += 1
|
||||
odd_triangles.append(T)
|
||||
if r == 2 and s == 0:
|
||||
# in this case either triangle works, so just use T
|
||||
starting_cell = T
|
||||
elif r - 1 <= s <= r:
|
||||
# check if odd triangles containing e form complete subgraph
|
||||
triangle_nodes = set()
|
||||
for T in odd_triangles:
|
||||
for x in T:
|
||||
triangle_nodes.add(x)
|
||||
|
||||
for u in triangle_nodes:
|
||||
for v in triangle_nodes:
|
||||
if u != v and (v not in G[u]):
|
||||
msg = (
|
||||
"G is not a line graph (odd triangles "
|
||||
"do not form complete subgraph)"
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
# otherwise then we can use this as the starting cell
|
||||
starting_cell = tuple(triangle_nodes)
|
||||
|
||||
else:
|
||||
msg = (
|
||||
"G is not a line graph (incorrect number of "
|
||||
"odd triangles around starting edge)"
|
||||
)
|
||||
raise nx.NetworkXError(msg)
|
||||
return starting_cell
|
||||
110
.CondaPkg/env/Lib/site-packages/networkx/generators/mycielski.py
vendored
Normal file
110
.CondaPkg/env/Lib/site-packages/networkx/generators/mycielski.py
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Functions related to the Mycielski Operation and the Mycielskian family
|
||||
of graphs.
|
||||
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = ["mycielskian", "mycielski_graph"]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
@nx._dispatchable(returns_graph=True)
|
||||
def mycielskian(G, iterations=1):
|
||||
r"""Returns the Mycielskian of a simple, undirected graph G
|
||||
|
||||
The Mycielskian of graph preserves a graph's triangle free
|
||||
property while increasing the chromatic number by 1.
|
||||
|
||||
The Mycielski Operation on a graph, :math:`G=(V, E)`, constructs a new
|
||||
graph with :math:`2|V| + 1` nodes and :math:`3|E| + |V|` edges.
|
||||
|
||||
The construction is as follows:
|
||||
|
||||
Let :math:`V = {0, ..., n-1}`. Construct another vertex set
|
||||
:math:`U = {n, ..., 2n}` and a vertex, `w`.
|
||||
Construct a new graph, `M`, with vertices :math:`U \bigcup V \bigcup w`.
|
||||
For edges, :math:`(u, v) \in E` add edges :math:`(u, v), (u, v + n)`, and
|
||||
:math:`(u + n, v)` to M. Finally, for all vertices :math:`u \in U`, add
|
||||
edge :math:`(u, w)` to M.
|
||||
|
||||
The Mycielski Operation can be done multiple times by repeating the above
|
||||
process iteratively.
|
||||
|
||||
More information can be found at https://en.wikipedia.org/wiki/Mycielskian
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A simple, undirected NetworkX graph
|
||||
iterations : int
|
||||
The number of iterations of the Mycielski operation to
|
||||
perform on G. Defaults to 1. Must be a non-negative integer.
|
||||
|
||||
Returns
|
||||
-------
|
||||
M : graph
|
||||
The Mycielskian of G after the specified number of iterations.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Graph, node, and edge data are not necessarily propagated to the new graph.
|
||||
|
||||
"""
|
||||
|
||||
M = nx.convert_node_labels_to_integers(G)
|
||||
|
||||
for i in range(iterations):
|
||||
n = M.number_of_nodes()
|
||||
M.add_nodes_from(range(n, 2 * n))
|
||||
old_edges = list(M.edges())
|
||||
M.add_edges_from((u, v + n) for u, v in old_edges)
|
||||
M.add_edges_from((u + n, v) for u, v in old_edges)
|
||||
M.add_node(2 * n)
|
||||
M.add_edges_from((u + n, 2 * n) for u in range(n))
|
||||
|
||||
return M
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def mycielski_graph(n):
|
||||
"""Generator for the n_th Mycielski Graph.
|
||||
|
||||
The Mycielski family of graphs is an infinite set of graphs.
|
||||
:math:`M_1` is the singleton graph, :math:`M_2` is two vertices with an
|
||||
edge, and, for :math:`i > 2`, :math:`M_i` is the Mycielskian of
|
||||
:math:`M_{i-1}`.
|
||||
|
||||
More information can be found at
|
||||
http://mathworld.wolfram.com/MycielskiGraph.html
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The desired Mycielski Graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
M : graph
|
||||
The n_th Mycielski Graph
|
||||
|
||||
Notes
|
||||
-----
|
||||
The first graph in the Mycielski sequence is the singleton graph.
|
||||
The Mycielskian of this graph is not the :math:`P_2` graph, but rather the
|
||||
:math:`P_2` graph with an extra, isolated vertex. The second Mycielski
|
||||
graph is the :math:`P_2` graph, so the first two are hard coded.
|
||||
The remaining graphs are generated using the Mycielski operation.
|
||||
|
||||
"""
|
||||
|
||||
if n < 1:
|
||||
raise nx.NetworkXError("must satisfy n >= 1")
|
||||
|
||||
if n == 1:
|
||||
return nx.empty_graph(1)
|
||||
|
||||
else:
|
||||
return mycielskian(nx.path_graph(2), n - 2)
|
||||
212
.CondaPkg/env/Lib/site-packages/networkx/generators/nonisomorphic_trees.py
vendored
Normal file
212
.CondaPkg/env/Lib/site-packages/networkx/generators/nonisomorphic_trees.py
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
Implementation of the Wright, Richmond, Odlyzko and McKay (WROM)
|
||||
algorithm for the enumeration of all non-isomorphic free trees of a
|
||||
given order. Rooted trees are represented by level sequences, i.e.,
|
||||
lists in which the i-th element specifies the distance of vertex i to
|
||||
the root.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ["nonisomorphic_trees", "number_of_nonisomorphic_trees"]
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def nonisomorphic_trees(order, create="graph"):
|
||||
"""Generates lists of nonisomorphic trees
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
order of the desired tree(s)
|
||||
|
||||
create : one of {"graph", "matrix"} (default="graph")
|
||||
If ``"graph"`` is selected a list of ``Graph`` instances will be returned,
|
||||
if matrix is selected a list of adjacency matrices will be returned.
|
||||
|
||||
.. deprecated:: 3.3
|
||||
|
||||
The `create` argument is deprecated and will be removed in NetworkX
|
||||
version 3.5. In the future, `nonisomorphic_trees` will yield graph
|
||||
instances by default. To generate adjacency matrices, call
|
||||
``nx.to_numpy_array`` on the output, e.g.::
|
||||
|
||||
[nx.to_numpy_array(G) for G in nx.nonisomorphic_trees(N)]
|
||||
|
||||
Yields
|
||||
------
|
||||
list
|
||||
A list of nonisomorphic trees, in one of two formats depending on the
|
||||
value of the `create` parameter:
|
||||
- ``create="graph"``: yields a list of `networkx.Graph` instances
|
||||
- ``create="matrix"``: yields a list of list-of-lists representing adjacency matrices
|
||||
"""
|
||||
|
||||
if order < 2:
|
||||
raise ValueError
|
||||
# start at the path graph rooted at its center
|
||||
layout = list(range(order // 2 + 1)) + list(range(1, (order + 1) // 2))
|
||||
|
||||
while layout is not None:
|
||||
layout = _next_tree(layout)
|
||||
if layout is not None:
|
||||
if create == "graph":
|
||||
yield _layout_to_graph(layout)
|
||||
elif create == "matrix":
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
(
|
||||
"\n\nThe 'create=matrix' argument of nonisomorphic_trees\n"
|
||||
"is deprecated and will be removed in version 3.5.\n"
|
||||
"Use ``nx.to_numpy_array`` to convert graphs to adjacency "
|
||||
"matrices, e.g.::\n\n"
|
||||
" [nx.to_numpy_array(G) for G in nx.nonisomorphic_trees(N)]"
|
||||
),
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
yield _layout_to_matrix(layout)
|
||||
layout = _next_rooted_tree(layout)
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None)
|
||||
def number_of_nonisomorphic_trees(order):
|
||||
"""Returns the number of nonisomorphic trees
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
order of the desired tree(s)
|
||||
|
||||
Returns
|
||||
-------
|
||||
length : Number of nonisomorphic graphs for the given order
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
"""
|
||||
return sum(1 for _ in nonisomorphic_trees(order))
|
||||
|
||||
|
||||
def _next_rooted_tree(predecessor, p=None):
|
||||
"""One iteration of the Beyer-Hedetniemi algorithm."""
|
||||
|
||||
if p is None:
|
||||
p = len(predecessor) - 1
|
||||
while predecessor[p] == 1:
|
||||
p -= 1
|
||||
if p == 0:
|
||||
return None
|
||||
|
||||
q = p - 1
|
||||
while predecessor[q] != predecessor[p] - 1:
|
||||
q -= 1
|
||||
result = list(predecessor)
|
||||
for i in range(p, len(result)):
|
||||
result[i] = result[i - p + q]
|
||||
return result
|
||||
|
||||
|
||||
def _next_tree(candidate):
|
||||
"""One iteration of the Wright, Richmond, Odlyzko and McKay
|
||||
algorithm."""
|
||||
|
||||
# valid representation of a free tree if:
|
||||
# there are at least two vertices at layer 1
|
||||
# (this is always the case because we start at the path graph)
|
||||
left, rest = _split_tree(candidate)
|
||||
|
||||
# and the left subtree of the root
|
||||
# is less high than the tree with the left subtree removed
|
||||
left_height = max(left)
|
||||
rest_height = max(rest)
|
||||
valid = rest_height >= left_height
|
||||
|
||||
if valid and rest_height == left_height:
|
||||
# and, if left and rest are of the same height,
|
||||
# if left does not encompass more vertices
|
||||
if len(left) > len(rest):
|
||||
valid = False
|
||||
# and, if they have the same number or vertices,
|
||||
# if left does not come after rest lexicographically
|
||||
elif len(left) == len(rest) and left > rest:
|
||||
valid = False
|
||||
|
||||
if valid:
|
||||
return candidate
|
||||
else:
|
||||
# jump to the next valid free tree
|
||||
p = len(left)
|
||||
new_candidate = _next_rooted_tree(candidate, p)
|
||||
if candidate[p] > 2:
|
||||
new_left, new_rest = _split_tree(new_candidate)
|
||||
new_left_height = max(new_left)
|
||||
suffix = range(1, new_left_height + 2)
|
||||
new_candidate[-len(suffix) :] = suffix
|
||||
return new_candidate
|
||||
|
||||
|
||||
def _split_tree(layout):
|
||||
"""Returns a tuple of two layouts, one containing the left
|
||||
subtree of the root vertex, and one containing the original tree
|
||||
with the left subtree removed."""
|
||||
|
||||
one_found = False
|
||||
m = None
|
||||
for i in range(len(layout)):
|
||||
if layout[i] == 1:
|
||||
if one_found:
|
||||
m = i
|
||||
break
|
||||
else:
|
||||
one_found = True
|
||||
|
||||
if m is None:
|
||||
m = len(layout)
|
||||
|
||||
left = [layout[i] - 1 for i in range(1, m)]
|
||||
rest = [0] + [layout[i] for i in range(m, len(layout))]
|
||||
return (left, rest)
|
||||
|
||||
|
||||
def _layout_to_matrix(layout):
|
||||
"""Create the adjacency matrix for the tree specified by the
|
||||
given layout (level sequence)."""
|
||||
|
||||
result = [[0] * len(layout) for i in range(len(layout))]
|
||||
stack = []
|
||||
for i in range(len(layout)):
|
||||
i_level = layout[i]
|
||||
if stack:
|
||||
j = stack[-1]
|
||||
j_level = layout[j]
|
||||
while j_level >= i_level:
|
||||
stack.pop()
|
||||
j = stack[-1]
|
||||
j_level = layout[j]
|
||||
result[i][j] = result[j][i] = 1
|
||||
stack.append(i)
|
||||
return result
|
||||
|
||||
|
||||
def _layout_to_graph(layout):
|
||||
"""Create a NetworkX Graph for the tree specified by the
|
||||
given layout(level sequence)"""
|
||||
G = nx.Graph()
|
||||
stack = []
|
||||
for i in range(len(layout)):
|
||||
i_level = layout[i]
|
||||
if stack:
|
||||
j = stack[-1]
|
||||
j_level = layout[j]
|
||||
while j_level >= i_level:
|
||||
stack.pop()
|
||||
j = stack[-1]
|
||||
j_level = layout[j]
|
||||
G.add_edge(i, j)
|
||||
stack.append(i)
|
||||
return G
|
||||
117
.CondaPkg/env/Lib/site-packages/networkx/generators/random_clustered.py
vendored
Normal file
117
.CondaPkg/env/Lib/site-packages/networkx/generators/random_clustered.py
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
"""Generate graphs with given degree and triangle sequence.
|
||||
"""
|
||||
import networkx as nx
|
||||
from networkx.utils import py_random_state
|
||||
|
||||
__all__ = ["random_clustered_graph"]
|
||||
|
||||
|
||||
@py_random_state(2)
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def random_clustered_graph(joint_degree_sequence, create_using=None, seed=None):
|
||||
r"""Generate a random graph with the given joint independent edge degree and
|
||||
triangle degree sequence.
|
||||
|
||||
This uses a configuration model-like approach to generate a random graph
|
||||
(with parallel edges and self-loops) by randomly assigning edges to match
|
||||
the given joint degree sequence.
|
||||
|
||||
The joint degree sequence is a list of pairs of integers of the form
|
||||
$[(d_{1,i}, d_{1,t}), \dotsc, (d_{n,i}, d_{n,t})]$. According to this list,
|
||||
vertex $u$ is a member of $d_{u,t}$ triangles and has $d_{u, i}$ other
|
||||
edges. The number $d_{u,t}$ is the *triangle degree* of $u$ and the number
|
||||
$d_{u,i}$ is the *independent edge degree*.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
joint_degree_sequence : list of integer pairs
|
||||
Each list entry corresponds to the independent edge degree and
|
||||
triangle degree of a node.
|
||||
create_using : NetworkX graph constructor, optional (default MultiGraph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : MultiGraph
|
||||
A graph with the specified degree sequence. Nodes are labeled
|
||||
starting at 0 with an index corresponding to the position in
|
||||
deg_sequence.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If the independent edge degree sequence sum is not even
|
||||
or the triangle degree sequence sum is not divisible by 3.
|
||||
|
||||
Notes
|
||||
-----
|
||||
As described by Miller [1]_ (see also Newman [2]_ for an equivalent
|
||||
description).
|
||||
|
||||
A non-graphical degree sequence (not realizable by some simple
|
||||
graph) is allowed since this function returns graphs with self
|
||||
loops and parallel edges. An exception is raised if the
|
||||
independent degree sequence does not have an even sum or the
|
||||
triangle degree sequence sum is not divisible by 3.
|
||||
|
||||
This configuration model-like construction process can lead to
|
||||
duplicate edges and loops. You can remove the self-loops and
|
||||
parallel edges (see below) which will likely result in a graph
|
||||
that doesn't have the exact degree sequence specified. This
|
||||
"finite-size effect" decreases as the size of the graph increases.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Joel C. Miller. "Percolation and epidemics in random clustered
|
||||
networks". In: Physical review. E, Statistical, nonlinear, and soft
|
||||
matter physics 80 (2 Part 1 August 2009).
|
||||
.. [2] M. E. J. Newman. "Random Graphs with Clustering".
|
||||
In: Physical Review Letters 103 (5 July 2009)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> deg = [(1, 0), (1, 0), (1, 0), (2, 0), (1, 0), (2, 1), (0, 1), (0, 1)]
|
||||
>>> G = nx.random_clustered_graph(deg)
|
||||
|
||||
To remove parallel edges:
|
||||
|
||||
>>> G = nx.Graph(G)
|
||||
|
||||
To remove self loops:
|
||||
|
||||
>>> G.remove_edges_from(nx.selfloop_edges(G))
|
||||
|
||||
"""
|
||||
# In Python 3, zip() returns an iterator. Make this into a list.
|
||||
joint_degree_sequence = list(joint_degree_sequence)
|
||||
|
||||
N = len(joint_degree_sequence)
|
||||
G = nx.empty_graph(N, create_using, default=nx.MultiGraph)
|
||||
if G.is_directed():
|
||||
raise nx.NetworkXError("Directed Graph not supported")
|
||||
|
||||
ilist = []
|
||||
tlist = []
|
||||
for n in G:
|
||||
degrees = joint_degree_sequence[n]
|
||||
for icount in range(degrees[0]):
|
||||
ilist.append(n)
|
||||
for tcount in range(degrees[1]):
|
||||
tlist.append(n)
|
||||
|
||||
if len(ilist) % 2 != 0 or len(tlist) % 3 != 0:
|
||||
raise nx.NetworkXError("Invalid degree sequence")
|
||||
|
||||
seed.shuffle(ilist)
|
||||
seed.shuffle(tlist)
|
||||
while ilist:
|
||||
G.add_edge(ilist.pop(), ilist.pop())
|
||||
while tlist:
|
||||
n1 = tlist.pop()
|
||||
n2 = tlist.pop()
|
||||
n3 = tlist.pop()
|
||||
G.add_edges_from([(n1, n2), (n1, n3), (n2, n3)])
|
||||
return G
|
||||
1331
.CondaPkg/env/Lib/site-packages/networkx/generators/random_graphs.py
vendored
Normal file
1331
.CondaPkg/env/Lib/site-packages/networkx/generators/random_graphs.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
993
.CondaPkg/env/Lib/site-packages/networkx/generators/small.py
vendored
Normal file
993
.CondaPkg/env/Lib/site-packages/networkx/generators/small.py
vendored
Normal file
@@ -0,0 +1,993 @@
|
||||
"""
|
||||
Various small and named graphs, together with some compact generators.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"LCF_graph",
|
||||
"bull_graph",
|
||||
"chvatal_graph",
|
||||
"cubical_graph",
|
||||
"desargues_graph",
|
||||
"diamond_graph",
|
||||
"dodecahedral_graph",
|
||||
"frucht_graph",
|
||||
"heawood_graph",
|
||||
"hoffman_singleton_graph",
|
||||
"house_graph",
|
||||
"house_x_graph",
|
||||
"icosahedral_graph",
|
||||
"krackhardt_kite_graph",
|
||||
"moebius_kantor_graph",
|
||||
"octahedral_graph",
|
||||
"pappus_graph",
|
||||
"petersen_graph",
|
||||
"sedgewick_maze_graph",
|
||||
"tetrahedral_graph",
|
||||
"truncated_cube_graph",
|
||||
"truncated_tetrahedron_graph",
|
||||
"tutte_graph",
|
||||
]
|
||||
|
||||
from functools import wraps
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.generators.classic import (
|
||||
complete_graph,
|
||||
cycle_graph,
|
||||
empty_graph,
|
||||
path_graph,
|
||||
)
|
||||
|
||||
|
||||
def _raise_on_directed(func):
|
||||
"""
|
||||
A decorator which inspects the `create_using` argument and raises a
|
||||
NetworkX exception when `create_using` is a DiGraph (class or instance) for
|
||||
graph generators that do not support directed outputs.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if kwargs.get("create_using") is not None:
|
||||
G = nx.empty_graph(create_using=kwargs["create_using"])
|
||||
if G.is_directed():
|
||||
raise NetworkXError("Directed Graph not supported")
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def LCF_graph(n, shift_list, repeats, create_using=None):
|
||||
"""
|
||||
Return the cubic graph specified in LCF notation.
|
||||
|
||||
LCF (Lederberg-Coxeter-Fruchte) notation[1]_ is a compressed
|
||||
notation used in the generation of various cubic Hamiltonian
|
||||
graphs of high symmetry. See, for example, `dodecahedral_graph`,
|
||||
`desargues_graph`, `heawood_graph` and `pappus_graph`.
|
||||
|
||||
Nodes are drawn from ``range(n)``. Each node ``n_i`` is connected with
|
||||
node ``n_i + shift % n`` where ``shift`` is given by cycling through
|
||||
the input `shift_list` `repeat` s times.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The starting graph is the `n`-cycle with nodes ``0, ..., n-1``.
|
||||
The null graph is returned if `n` < 1.
|
||||
|
||||
shift_list : list
|
||||
A list of integer shifts mod `n`, ``[s1, s2, .., sk]``
|
||||
|
||||
repeats : int
|
||||
Integer specifying the number of times that shifts in `shift_list`
|
||||
are successively applied to each current node in the n-cycle
|
||||
to generate an edge between ``n_current`` and ``n_current + shift mod n``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : Graph
|
||||
A graph instance created from the specified LCF notation.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The utility graph $K_{3,3}$
|
||||
|
||||
>>> G = nx.LCF_graph(6, [3, -3], 3)
|
||||
>>> G.edges()
|
||||
EdgeView([(0, 1), (0, 5), (0, 3), (1, 2), (1, 4), (2, 3), (2, 5), (3, 4), (4, 5)])
|
||||
|
||||
The Heawood graph:
|
||||
|
||||
>>> G = nx.LCF_graph(14, [5, -5], 7)
|
||||
>>> nx.is_isomorphic(G, nx.heawood_graph())
|
||||
True
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/LCF_notation
|
||||
|
||||
"""
|
||||
if n <= 0:
|
||||
return empty_graph(0, create_using)
|
||||
|
||||
# start with the n-cycle
|
||||
G = cycle_graph(n, create_using)
|
||||
if G.is_directed():
|
||||
raise NetworkXError("Directed Graph not supported")
|
||||
G.name = "LCF_graph"
|
||||
nodes = sorted(G)
|
||||
|
||||
n_extra_edges = repeats * len(shift_list)
|
||||
# edges are added n_extra_edges times
|
||||
# (not all of these need be new)
|
||||
if n_extra_edges < 1:
|
||||
return G
|
||||
|
||||
for i in range(n_extra_edges):
|
||||
shift = shift_list[i % len(shift_list)] # cycle through shift_list
|
||||
v1 = nodes[i % n] # cycle repeatedly through nodes
|
||||
v2 = nodes[(i + shift) % n]
|
||||
G.add_edge(v1, v2)
|
||||
return G
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Various small and named graphs
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def bull_graph(create_using=None):
|
||||
"""
|
||||
Returns the Bull Graph
|
||||
|
||||
The Bull Graph has 5 nodes and 5 edges. It is a planar undirected
|
||||
graph in the form of a triangle with two disjoint pendant edges [1]_
|
||||
The name comes from the triangle and pendant edges representing
|
||||
respectively the body and legs of a bull.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
A bull graph with 5 nodes
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Bull_graph.
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{0: [1, 2], 1: [0, 2, 3], 2: [0, 1, 4], 3: [1], 4: [2]},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Bull Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def chvatal_graph(create_using=None):
|
||||
"""
|
||||
Returns the Chvátal Graph
|
||||
|
||||
The Chvátal Graph is an undirected graph with 12 nodes and 24 edges [1]_.
|
||||
It has 370 distinct (directed) Hamiltonian cycles, giving a unique generalized
|
||||
LCF notation of order 4, two of order 6 , and 43 of order 1 [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
The Chvátal graph with 12 nodes and 24 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Chv%C3%A1tal_graph
|
||||
.. [2] https://mathworld.wolfram.com/ChvatalGraph.html
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 4, 6, 9],
|
||||
1: [2, 5, 7],
|
||||
2: [3, 6, 8],
|
||||
3: [4, 7, 9],
|
||||
4: [5, 8],
|
||||
5: [10, 11],
|
||||
6: [10, 11],
|
||||
7: [8, 11],
|
||||
8: [10],
|
||||
9: [10, 11],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Chvatal Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def cubical_graph(create_using=None):
|
||||
"""
|
||||
Returns the 3-regular Platonic Cubical Graph
|
||||
|
||||
The skeleton of the cube (the nodes and edges) form a graph, with 8
|
||||
nodes, and 12 edges. It is a special case of the hypercube graph.
|
||||
It is one of 5 Platonic graphs, each a skeleton of its
|
||||
Platonic solid [1]_.
|
||||
Such graphs arise in parallel processing in computers.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
A cubical graph with 8 nodes and 12 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Cube#Cubical_graph
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 3, 4],
|
||||
1: [0, 2, 7],
|
||||
2: [1, 3, 6],
|
||||
3: [0, 2, 5],
|
||||
4: [0, 5, 7],
|
||||
5: [3, 4, 6],
|
||||
6: [2, 5, 7],
|
||||
7: [1, 4, 6],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Platonic Cubical Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def desargues_graph(create_using=None):
|
||||
"""
|
||||
Returns the Desargues Graph
|
||||
|
||||
The Desargues Graph is a non-planar, distance-transitive cubic graph
|
||||
with 20 nodes and 30 edges [1]_.
|
||||
It is a symmetric graph. It can be represented in LCF notation
|
||||
as [5,-5,9,-9]^5 [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Desargues Graph with 20 nodes and 30 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Desargues_graph
|
||||
.. [2] https://mathworld.wolfram.com/DesarguesGraph.html
|
||||
"""
|
||||
G = LCF_graph(20, [5, -5, 9, -9], 5, create_using)
|
||||
G.name = "Desargues Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def diamond_graph(create_using=None):
|
||||
"""
|
||||
Returns the Diamond graph
|
||||
|
||||
The Diamond Graph is planar undirected graph with 4 nodes and 5 edges.
|
||||
It is also sometimes known as the double triangle graph or kite graph [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Diamond Graph with 4 nodes and 5 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://mathworld.wolfram.com/DiamondGraph.html
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{0: [1, 2], 1: [0, 2, 3], 2: [0, 1, 3], 3: [1, 2]}, create_using=create_using
|
||||
)
|
||||
G.name = "Diamond Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def dodecahedral_graph(create_using=None):
|
||||
"""
|
||||
Returns the Platonic Dodecahedral graph.
|
||||
|
||||
The dodecahedral graph has 20 nodes and 30 edges. The skeleton of the
|
||||
dodecahedron forms a graph. It is one of 5 Platonic graphs [1]_.
|
||||
It can be described in LCF notation as:
|
||||
``[10, 7, 4, -4, -7, 10, -4, 7, -7, 4]^2`` [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Dodecahedral Graph with 20 nodes and 30 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Regular_dodecahedron#Dodecahedral_graph
|
||||
.. [2] https://mathworld.wolfram.com/DodecahedralGraph.html
|
||||
|
||||
"""
|
||||
G = LCF_graph(20, [10, 7, 4, -4, -7, 10, -4, 7, -7, 4], 2, create_using)
|
||||
G.name = "Dodecahedral Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def frucht_graph(create_using=None):
|
||||
"""
|
||||
Returns the Frucht Graph.
|
||||
|
||||
The Frucht Graph is the smallest cubical graph whose
|
||||
automorphism group consists only of the identity element [1]_.
|
||||
It has 12 nodes and 18 edges and no nontrivial symmetries.
|
||||
It is planar and Hamiltonian [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Frucht Graph with 12 nodes and 18 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Frucht_graph
|
||||
.. [2] https://mathworld.wolfram.com/FruchtGraph.html
|
||||
|
||||
"""
|
||||
G = cycle_graph(7, create_using)
|
||||
G.add_edges_from(
|
||||
[
|
||||
[0, 7],
|
||||
[1, 7],
|
||||
[2, 8],
|
||||
[3, 9],
|
||||
[4, 9],
|
||||
[5, 10],
|
||||
[6, 10],
|
||||
[7, 11],
|
||||
[8, 11],
|
||||
[8, 9],
|
||||
[10, 11],
|
||||
]
|
||||
)
|
||||
|
||||
G.name = "Frucht Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def heawood_graph(create_using=None):
|
||||
"""
|
||||
Returns the Heawood Graph, a (3,6) cage.
|
||||
|
||||
The Heawood Graph is an undirected graph with 14 nodes and 21 edges,
|
||||
named after Percy John Heawood [1]_.
|
||||
It is cubic symmetric, nonplanar, Hamiltonian, and can be represented
|
||||
in LCF notation as ``[5,-5]^7`` [2]_.
|
||||
It is the unique (3,6)-cage: the regular cubic graph of girth 6 with
|
||||
minimal number of vertices [3]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Heawood Graph with 14 nodes and 21 edges
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Heawood_graph
|
||||
.. [2] https://mathworld.wolfram.com/HeawoodGraph.html
|
||||
.. [3] https://www.win.tue.nl/~aeb/graphs/Heawood.html
|
||||
|
||||
"""
|
||||
G = LCF_graph(14, [5, -5], 7, create_using)
|
||||
G.name = "Heawood Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def hoffman_singleton_graph():
|
||||
"""
|
||||
Returns the Hoffman-Singleton Graph.
|
||||
|
||||
The Hoffman–Singleton graph is a symmetrical undirected graph
|
||||
with 50 nodes and 175 edges.
|
||||
All indices lie in ``Z % 5``: that is, the integers mod 5 [1]_.
|
||||
It is the only regular graph of vertex degree 7, diameter 2, and girth 5.
|
||||
It is the unique (7,5)-cage graph and Moore graph, and contains many
|
||||
copies of the Petersen graph [2]_.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Hoffman–Singleton Graph with 50 nodes and 175 edges
|
||||
|
||||
Notes
|
||||
-----
|
||||
Constructed from pentagon and pentagram as follows: Take five pentagons $P_h$
|
||||
and five pentagrams $Q_i$ . Join vertex $j$ of $P_h$ to vertex $h·i+j$ of $Q_i$ [3]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://blogs.ams.org/visualinsight/2016/02/01/hoffman-singleton-graph/
|
||||
.. [2] https://mathworld.wolfram.com/Hoffman-SingletonGraph.html
|
||||
.. [3] https://en.wikipedia.org/wiki/Hoffman%E2%80%93Singleton_graph
|
||||
|
||||
"""
|
||||
G = nx.Graph()
|
||||
for i in range(5):
|
||||
for j in range(5):
|
||||
G.add_edge(("pentagon", i, j), ("pentagon", i, (j - 1) % 5))
|
||||
G.add_edge(("pentagon", i, j), ("pentagon", i, (j + 1) % 5))
|
||||
G.add_edge(("pentagram", i, j), ("pentagram", i, (j - 2) % 5))
|
||||
G.add_edge(("pentagram", i, j), ("pentagram", i, (j + 2) % 5))
|
||||
for k in range(5):
|
||||
G.add_edge(("pentagon", i, j), ("pentagram", k, (i * k + j) % 5))
|
||||
G = nx.convert_node_labels_to_integers(G)
|
||||
G.name = "Hoffman-Singleton Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def house_graph(create_using=None):
|
||||
"""
|
||||
Returns the House graph (square with triangle on top)
|
||||
|
||||
The house graph is a simple undirected graph with
|
||||
5 nodes and 6 edges [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
House graph in the form of a square with a triangle on top
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://mathworld.wolfram.com/HouseGraph.html
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{0: [1, 2], 1: [0, 3], 2: [0, 3, 4], 3: [1, 2, 4], 4: [2, 3]},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "House Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def house_x_graph(create_using=None):
|
||||
"""
|
||||
Returns the House graph with a cross inside the house square.
|
||||
|
||||
The House X-graph is the House graph plus the two edges connecting diagonally
|
||||
opposite vertices of the square base. It is also one of the two graphs
|
||||
obtained by removing two edges from the pentatope graph [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
House graph with diagonal vertices connected
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://mathworld.wolfram.com/HouseGraph.html
|
||||
"""
|
||||
G = house_graph(create_using)
|
||||
G.add_edges_from([(0, 3), (1, 2)])
|
||||
G.name = "House-with-X-inside Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def icosahedral_graph(create_using=None):
|
||||
"""
|
||||
Returns the Platonic Icosahedral graph.
|
||||
|
||||
The icosahedral graph has 12 nodes and 30 edges. It is a Platonic graph
|
||||
whose nodes have the connectivity of the icosahedron. It is undirected,
|
||||
regular and Hamiltonian [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Icosahedral graph with 12 nodes and 30 edges.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://mathworld.wolfram.com/IcosahedralGraph.html
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 5, 7, 8, 11],
|
||||
1: [2, 5, 6, 8],
|
||||
2: [3, 6, 8, 9],
|
||||
3: [4, 6, 9, 10],
|
||||
4: [5, 6, 10, 11],
|
||||
5: [6, 11],
|
||||
7: [8, 9, 10, 11],
|
||||
8: [9],
|
||||
9: [10],
|
||||
10: [11],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Platonic Icosahedral Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def krackhardt_kite_graph(create_using=None):
|
||||
"""
|
||||
Returns the Krackhardt Kite Social Network.
|
||||
|
||||
A 10 actor social network introduced by David Krackhardt
|
||||
to illustrate different centrality measures [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Krackhardt Kite graph with 10 nodes and 18 edges
|
||||
|
||||
Notes
|
||||
-----
|
||||
The traditional labeling is:
|
||||
Andre=1, Beverley=2, Carol=3, Diane=4,
|
||||
Ed=5, Fernando=6, Garth=7, Heather=8, Ike=9, Jane=10.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Krackhardt, David. "Assessing the Political Landscape: Structure,
|
||||
Cognition, and Power in Organizations". Administrative Science Quarterly.
|
||||
35 (2): 342–369. doi:10.2307/2393394. JSTOR 2393394. June 1990.
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 2, 3, 5],
|
||||
1: [0, 3, 4, 6],
|
||||
2: [0, 3, 5],
|
||||
3: [0, 1, 2, 4, 5, 6],
|
||||
4: [1, 3, 6],
|
||||
5: [0, 2, 3, 6, 7],
|
||||
6: [1, 3, 4, 5, 7],
|
||||
7: [5, 6, 8],
|
||||
8: [7, 9],
|
||||
9: [8],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Krackhardt Kite Social Network"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def moebius_kantor_graph(create_using=None):
|
||||
"""
|
||||
Returns the Moebius-Kantor graph.
|
||||
|
||||
The Möbius-Kantor graph is the cubic symmetric graph on 16 nodes.
|
||||
Its LCF notation is [5,-5]^8, and it is isomorphic to the generalized
|
||||
Petersen graph [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Moebius-Kantor graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/M%C3%B6bius%E2%80%93Kantor_graph
|
||||
|
||||
"""
|
||||
G = LCF_graph(16, [5, -5], 8, create_using)
|
||||
G.name = "Moebius-Kantor Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def octahedral_graph(create_using=None):
|
||||
"""
|
||||
Returns the Platonic Octahedral graph.
|
||||
|
||||
The octahedral graph is the 6-node 12-edge Platonic graph having the
|
||||
connectivity of the octahedron [1]_. If 6 couples go to a party,
|
||||
and each person shakes hands with every person except his or her partner,
|
||||
then this graph describes the set of handshakes that take place;
|
||||
for this reason it is also called the cocktail party graph [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Octahedral graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://mathworld.wolfram.com/OctahedralGraph.html
|
||||
.. [2] https://en.wikipedia.org/wiki/Tur%C3%A1n_graph#Special_cases
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{0: [1, 2, 3, 4], 1: [2, 3, 5], 2: [4, 5], 3: [4, 5], 4: [5]},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Platonic Octahedral Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def pappus_graph():
|
||||
"""
|
||||
Returns the Pappus graph.
|
||||
|
||||
The Pappus graph is a cubic symmetric distance-regular graph with 18 nodes
|
||||
and 27 edges. It is Hamiltonian and can be represented in LCF notation as
|
||||
[5,7,-7,7,-7,-5]^3 [1]_.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Pappus graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Pappus_graph
|
||||
"""
|
||||
G = LCF_graph(18, [5, 7, -7, 7, -7, -5], 3)
|
||||
G.name = "Pappus Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def petersen_graph(create_using=None):
|
||||
"""
|
||||
Returns the Petersen graph.
|
||||
|
||||
The Peterson graph is a cubic, undirected graph with 10 nodes and 15 edges [1]_.
|
||||
Julius Petersen constructed the graph as the smallest counterexample
|
||||
against the claim that a connected bridgeless cubic graph
|
||||
has an edge colouring with three colours [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Petersen graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Petersen_graph
|
||||
.. [2] https://www.win.tue.nl/~aeb/drg/graphs/Petersen.html
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 4, 5],
|
||||
1: [0, 2, 6],
|
||||
2: [1, 3, 7],
|
||||
3: [2, 4, 8],
|
||||
4: [3, 0, 9],
|
||||
5: [0, 7, 8],
|
||||
6: [1, 8, 9],
|
||||
7: [2, 5, 9],
|
||||
8: [3, 5, 6],
|
||||
9: [4, 6, 7],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Petersen Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def sedgewick_maze_graph(create_using=None):
|
||||
"""
|
||||
Return a small maze with a cycle.
|
||||
|
||||
This is the maze used in Sedgewick, 3rd Edition, Part 5, Graph
|
||||
Algorithms, Chapter 18, e.g. Figure 18.2 and following [1]_.
|
||||
Nodes are numbered 0,..,7
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Small maze with a cycle
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Figure 18.2, Chapter 18, Graph Algorithms (3rd Ed), Sedgewick
|
||||
"""
|
||||
G = empty_graph(0, create_using)
|
||||
G.add_nodes_from(range(8))
|
||||
G.add_edges_from([[0, 2], [0, 7], [0, 5]])
|
||||
G.add_edges_from([[1, 7], [2, 6]])
|
||||
G.add_edges_from([[3, 4], [3, 5]])
|
||||
G.add_edges_from([[4, 5], [4, 7], [4, 6]])
|
||||
G.name = "Sedgewick Maze"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def tetrahedral_graph(create_using=None):
|
||||
"""
|
||||
Returns the 3-regular Platonic Tetrahedral graph.
|
||||
|
||||
Tetrahedral graph has 4 nodes and 6 edges. It is a
|
||||
special case of the complete graph, K4, and wheel graph, W4.
|
||||
It is one of the 5 platonic graphs [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Tetrahedral Graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Tetrahedron#Tetrahedral_graph
|
||||
|
||||
"""
|
||||
G = complete_graph(4, create_using)
|
||||
G.name = "Platonic Tetrahedral Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def truncated_cube_graph(create_using=None):
|
||||
"""
|
||||
Returns the skeleton of the truncated cube.
|
||||
|
||||
The truncated cube is an Archimedean solid with 14 regular
|
||||
faces (6 octagonal and 8 triangular), 36 edges and 24 nodes [1]_.
|
||||
The truncated cube is created by truncating (cutting off) the tips
|
||||
of the cube one third of the way into each edge [2]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Skeleton of the truncated cube
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Truncated_cube
|
||||
.. [2] https://www.coolmath.com/reference/polyhedra-truncated-cube
|
||||
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 2, 4],
|
||||
1: [11, 14],
|
||||
2: [3, 4],
|
||||
3: [6, 8],
|
||||
4: [5],
|
||||
5: [16, 18],
|
||||
6: [7, 8],
|
||||
7: [10, 12],
|
||||
8: [9],
|
||||
9: [17, 20],
|
||||
10: [11, 12],
|
||||
11: [14],
|
||||
12: [13],
|
||||
13: [21, 22],
|
||||
14: [15],
|
||||
15: [19, 23],
|
||||
16: [17, 18],
|
||||
17: [20],
|
||||
18: [19],
|
||||
19: [23],
|
||||
20: [21],
|
||||
21: [22],
|
||||
22: [23],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Truncated Cube Graph"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def truncated_tetrahedron_graph(create_using=None):
|
||||
"""
|
||||
Returns the skeleton of the truncated Platonic tetrahedron.
|
||||
|
||||
The truncated tetrahedron is an Archimedean solid with 4 regular hexagonal faces,
|
||||
4 equilateral triangle faces, 12 nodes and 18 edges. It can be constructed by truncating
|
||||
all 4 vertices of a regular tetrahedron at one third of the original edge length [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Skeleton of the truncated tetrahedron
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Truncated_tetrahedron
|
||||
|
||||
"""
|
||||
G = path_graph(12, create_using)
|
||||
G.add_edges_from([(0, 2), (0, 9), (1, 6), (3, 11), (4, 11), (5, 7), (8, 10)])
|
||||
G.name = "Truncated Tetrahedron Graph"
|
||||
return G
|
||||
|
||||
|
||||
@_raise_on_directed
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def tutte_graph(create_using=None):
|
||||
"""
|
||||
Returns the Tutte graph.
|
||||
|
||||
The Tutte graph is a cubic polyhedral, non-Hamiltonian graph. It has
|
||||
46 nodes and 69 edges.
|
||||
It is a counterexample to Tait's conjecture that every 3-regular polyhedron
|
||||
has a Hamiltonian cycle.
|
||||
It can be realized geometrically from a tetrahedron by multiply truncating
|
||||
three of its vertices [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
create_using : NetworkX graph constructor, optional (default=nx.Graph)
|
||||
Graph type to create. If graph instance, then cleared before populated.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : networkx Graph
|
||||
Tutte graph
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Tutte_graph
|
||||
"""
|
||||
G = nx.from_dict_of_lists(
|
||||
{
|
||||
0: [1, 2, 3],
|
||||
1: [4, 26],
|
||||
2: [10, 11],
|
||||
3: [18, 19],
|
||||
4: [5, 33],
|
||||
5: [6, 29],
|
||||
6: [7, 27],
|
||||
7: [8, 14],
|
||||
8: [9, 38],
|
||||
9: [10, 37],
|
||||
10: [39],
|
||||
11: [12, 39],
|
||||
12: [13, 35],
|
||||
13: [14, 15],
|
||||
14: [34],
|
||||
15: [16, 22],
|
||||
16: [17, 44],
|
||||
17: [18, 43],
|
||||
18: [45],
|
||||
19: [20, 45],
|
||||
20: [21, 41],
|
||||
21: [22, 23],
|
||||
22: [40],
|
||||
23: [24, 27],
|
||||
24: [25, 32],
|
||||
25: [26, 31],
|
||||
26: [33],
|
||||
27: [28],
|
||||
28: [29, 32],
|
||||
29: [30],
|
||||
30: [31, 33],
|
||||
31: [32],
|
||||
34: [35, 38],
|
||||
35: [36],
|
||||
36: [37, 39],
|
||||
37: [38],
|
||||
40: [41, 44],
|
||||
41: [42],
|
||||
42: [43, 45],
|
||||
43: [44],
|
||||
},
|
||||
create_using=create_using,
|
||||
)
|
||||
G.name = "Tutte's Graph"
|
||||
return G
|
||||
546
.CondaPkg/env/Lib/site-packages/networkx/generators/social.py
vendored
Normal file
546
.CondaPkg/env/Lib/site-packages/networkx/generators/social.py
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
"""
|
||||
Famous social networks.
|
||||
"""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = [
|
||||
"karate_club_graph",
|
||||
"davis_southern_women_graph",
|
||||
"florentine_families_graph",
|
||||
"les_miserables_graph",
|
||||
]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def karate_club_graph():
|
||||
"""Returns Zachary's Karate Club graph.
|
||||
|
||||
Each node in the returned graph has a node attribute 'club' that
|
||||
indicates the name of the club to which the member represented by that node
|
||||
belongs, either 'Mr. Hi' or 'Officer'. Each edge has a weight based on the
|
||||
number of contexts in which that edge's incident node members interacted.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To get the name of the club to which a node belongs::
|
||||
|
||||
>>> G = nx.karate_club_graph()
|
||||
>>> G.nodes[5]["club"]
|
||||
'Mr. Hi'
|
||||
>>> G.nodes[9]["club"]
|
||||
'Officer'
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Zachary, Wayne W.
|
||||
"An Information Flow Model for Conflict and Fission in Small Groups."
|
||||
*Journal of Anthropological Research*, 33, 452--473, (1977).
|
||||
"""
|
||||
# Create the set of all members, and the members of each club.
|
||||
all_members = set(range(34))
|
||||
club1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 16, 17, 19, 21}
|
||||
# club2 = all_members - club1
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(all_members)
|
||||
G.name = "Zachary's Karate Club"
|
||||
|
||||
zacharydat = """\
|
||||
0 4 5 3 3 3 3 2 2 0 2 3 2 3 0 0 0 2 0 2 0 2 0 0 0 0 0 0 0 0 0 2 0 0
|
||||
4 0 6 3 0 0 0 4 0 0 0 0 0 5 0 0 0 1 0 2 0 2 0 0 0 0 0 0 0 0 2 0 0 0
|
||||
5 6 0 3 0 0 0 4 5 1 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 3 0
|
||||
3 3 3 0 0 0 0 3 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3 0 0 0 0 0 2 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3 0 0 0 0 0 5 0 0 0 3 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3 0 0 0 2 5 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
2 4 4 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
2 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 4 3
|
||||
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
|
||||
2 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
1 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3 5 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4
|
||||
0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2
|
||||
2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 1
|
||||
2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 4 0 2 0 0 5 4
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 0 2 0 0
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 2 0 0 0 0 0 0 7 0 0
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 2
|
||||
0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 3 0 0 0 0 0 0 0 0 4
|
||||
0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 2
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 4 0 0 0 0 0 3 2
|
||||
0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3
|
||||
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 7 0 0 2 0 0 0 4 4
|
||||
0 0 2 0 0 0 0 0 3 0 0 0 0 0 3 3 0 0 1 0 3 0 2 5 0 0 0 0 0 4 3 4 0 5
|
||||
0 0 0 0 0 0 0 0 4 2 0 0 0 3 2 4 0 0 2 1 1 0 3 4 0 0 2 4 2 2 3 4 5 0"""
|
||||
|
||||
for row, line in enumerate(zacharydat.split("\n")):
|
||||
thisrow = [int(b) for b in line.split()]
|
||||
for col, entry in enumerate(thisrow):
|
||||
if entry >= 1:
|
||||
G.add_edge(row, col, weight=entry)
|
||||
|
||||
# Add the name of each member's club as a node attribute.
|
||||
for v in G:
|
||||
G.nodes[v]["club"] = "Mr. Hi" if v in club1 else "Officer"
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def davis_southern_women_graph():
|
||||
"""Returns Davis Southern women social network.
|
||||
|
||||
This is a bipartite graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] A. Davis, Gardner, B. B., Gardner, M. R., 1941. Deep South.
|
||||
University of Chicago Press, Chicago, IL.
|
||||
"""
|
||||
G = nx.Graph()
|
||||
# Top nodes
|
||||
women = [
|
||||
"Evelyn Jefferson",
|
||||
"Laura Mandeville",
|
||||
"Theresa Anderson",
|
||||
"Brenda Rogers",
|
||||
"Charlotte McDowd",
|
||||
"Frances Anderson",
|
||||
"Eleanor Nye",
|
||||
"Pearl Oglethorpe",
|
||||
"Ruth DeSand",
|
||||
"Verne Sanderson",
|
||||
"Myra Liddel",
|
||||
"Katherina Rogers",
|
||||
"Sylvia Avondale",
|
||||
"Nora Fayette",
|
||||
"Helen Lloyd",
|
||||
"Dorothy Murchison",
|
||||
"Olivia Carleton",
|
||||
"Flora Price",
|
||||
]
|
||||
G.add_nodes_from(women, bipartite=0)
|
||||
# Bottom nodes
|
||||
events = [
|
||||
"E1",
|
||||
"E2",
|
||||
"E3",
|
||||
"E4",
|
||||
"E5",
|
||||
"E6",
|
||||
"E7",
|
||||
"E8",
|
||||
"E9",
|
||||
"E10",
|
||||
"E11",
|
||||
"E12",
|
||||
"E13",
|
||||
"E14",
|
||||
]
|
||||
G.add_nodes_from(events, bipartite=1)
|
||||
|
||||
G.add_edges_from(
|
||||
[
|
||||
("Evelyn Jefferson", "E1"),
|
||||
("Evelyn Jefferson", "E2"),
|
||||
("Evelyn Jefferson", "E3"),
|
||||
("Evelyn Jefferson", "E4"),
|
||||
("Evelyn Jefferson", "E5"),
|
||||
("Evelyn Jefferson", "E6"),
|
||||
("Evelyn Jefferson", "E8"),
|
||||
("Evelyn Jefferson", "E9"),
|
||||
("Laura Mandeville", "E1"),
|
||||
("Laura Mandeville", "E2"),
|
||||
("Laura Mandeville", "E3"),
|
||||
("Laura Mandeville", "E5"),
|
||||
("Laura Mandeville", "E6"),
|
||||
("Laura Mandeville", "E7"),
|
||||
("Laura Mandeville", "E8"),
|
||||
("Theresa Anderson", "E2"),
|
||||
("Theresa Anderson", "E3"),
|
||||
("Theresa Anderson", "E4"),
|
||||
("Theresa Anderson", "E5"),
|
||||
("Theresa Anderson", "E6"),
|
||||
("Theresa Anderson", "E7"),
|
||||
("Theresa Anderson", "E8"),
|
||||
("Theresa Anderson", "E9"),
|
||||
("Brenda Rogers", "E1"),
|
||||
("Brenda Rogers", "E3"),
|
||||
("Brenda Rogers", "E4"),
|
||||
("Brenda Rogers", "E5"),
|
||||
("Brenda Rogers", "E6"),
|
||||
("Brenda Rogers", "E7"),
|
||||
("Brenda Rogers", "E8"),
|
||||
("Charlotte McDowd", "E3"),
|
||||
("Charlotte McDowd", "E4"),
|
||||
("Charlotte McDowd", "E5"),
|
||||
("Charlotte McDowd", "E7"),
|
||||
("Frances Anderson", "E3"),
|
||||
("Frances Anderson", "E5"),
|
||||
("Frances Anderson", "E6"),
|
||||
("Frances Anderson", "E8"),
|
||||
("Eleanor Nye", "E5"),
|
||||
("Eleanor Nye", "E6"),
|
||||
("Eleanor Nye", "E7"),
|
||||
("Eleanor Nye", "E8"),
|
||||
("Pearl Oglethorpe", "E6"),
|
||||
("Pearl Oglethorpe", "E8"),
|
||||
("Pearl Oglethorpe", "E9"),
|
||||
("Ruth DeSand", "E5"),
|
||||
("Ruth DeSand", "E7"),
|
||||
("Ruth DeSand", "E8"),
|
||||
("Ruth DeSand", "E9"),
|
||||
("Verne Sanderson", "E7"),
|
||||
("Verne Sanderson", "E8"),
|
||||
("Verne Sanderson", "E9"),
|
||||
("Verne Sanderson", "E12"),
|
||||
("Myra Liddel", "E8"),
|
||||
("Myra Liddel", "E9"),
|
||||
("Myra Liddel", "E10"),
|
||||
("Myra Liddel", "E12"),
|
||||
("Katherina Rogers", "E8"),
|
||||
("Katherina Rogers", "E9"),
|
||||
("Katherina Rogers", "E10"),
|
||||
("Katherina Rogers", "E12"),
|
||||
("Katherina Rogers", "E13"),
|
||||
("Katherina Rogers", "E14"),
|
||||
("Sylvia Avondale", "E7"),
|
||||
("Sylvia Avondale", "E8"),
|
||||
("Sylvia Avondale", "E9"),
|
||||
("Sylvia Avondale", "E10"),
|
||||
("Sylvia Avondale", "E12"),
|
||||
("Sylvia Avondale", "E13"),
|
||||
("Sylvia Avondale", "E14"),
|
||||
("Nora Fayette", "E6"),
|
||||
("Nora Fayette", "E7"),
|
||||
("Nora Fayette", "E9"),
|
||||
("Nora Fayette", "E10"),
|
||||
("Nora Fayette", "E11"),
|
||||
("Nora Fayette", "E12"),
|
||||
("Nora Fayette", "E13"),
|
||||
("Nora Fayette", "E14"),
|
||||
("Helen Lloyd", "E7"),
|
||||
("Helen Lloyd", "E8"),
|
||||
("Helen Lloyd", "E10"),
|
||||
("Helen Lloyd", "E11"),
|
||||
("Helen Lloyd", "E12"),
|
||||
("Dorothy Murchison", "E8"),
|
||||
("Dorothy Murchison", "E9"),
|
||||
("Olivia Carleton", "E9"),
|
||||
("Olivia Carleton", "E11"),
|
||||
("Flora Price", "E9"),
|
||||
("Flora Price", "E11"),
|
||||
]
|
||||
)
|
||||
G.graph["top"] = women
|
||||
G.graph["bottom"] = events
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def florentine_families_graph():
|
||||
"""Returns Florentine families graph.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Ronald L. Breiger and Philippa E. Pattison
|
||||
Cumulated social roles: The duality of persons and their algebras,1
|
||||
Social Networks, Volume 8, Issue 3, September 1986, Pages 215-256
|
||||
"""
|
||||
G = nx.Graph()
|
||||
G.add_edge("Acciaiuoli", "Medici")
|
||||
G.add_edge("Castellani", "Peruzzi")
|
||||
G.add_edge("Castellani", "Strozzi")
|
||||
G.add_edge("Castellani", "Barbadori")
|
||||
G.add_edge("Medici", "Barbadori")
|
||||
G.add_edge("Medici", "Ridolfi")
|
||||
G.add_edge("Medici", "Tornabuoni")
|
||||
G.add_edge("Medici", "Albizzi")
|
||||
G.add_edge("Medici", "Salviati")
|
||||
G.add_edge("Salviati", "Pazzi")
|
||||
G.add_edge("Peruzzi", "Strozzi")
|
||||
G.add_edge("Peruzzi", "Bischeri")
|
||||
G.add_edge("Strozzi", "Ridolfi")
|
||||
G.add_edge("Strozzi", "Bischeri")
|
||||
G.add_edge("Ridolfi", "Tornabuoni")
|
||||
G.add_edge("Tornabuoni", "Guadagni")
|
||||
G.add_edge("Albizzi", "Ginori")
|
||||
G.add_edge("Albizzi", "Guadagni")
|
||||
G.add_edge("Bischeri", "Guadagni")
|
||||
G.add_edge("Guadagni", "Lamberteschi")
|
||||
return G
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def les_miserables_graph():
|
||||
"""Returns coappearance network of characters in the novel Les Miserables.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] D. E. Knuth, 1993.
|
||||
The Stanford GraphBase: a platform for combinatorial computing,
|
||||
pp. 74-87. New York: AcM Press.
|
||||
"""
|
||||
G = nx.Graph()
|
||||
G.add_edge("Napoleon", "Myriel", weight=1)
|
||||
G.add_edge("MlleBaptistine", "Myriel", weight=8)
|
||||
G.add_edge("MmeMagloire", "Myriel", weight=10)
|
||||
G.add_edge("MmeMagloire", "MlleBaptistine", weight=6)
|
||||
G.add_edge("CountessDeLo", "Myriel", weight=1)
|
||||
G.add_edge("Geborand", "Myriel", weight=1)
|
||||
G.add_edge("Champtercier", "Myriel", weight=1)
|
||||
G.add_edge("Cravatte", "Myriel", weight=1)
|
||||
G.add_edge("Count", "Myriel", weight=2)
|
||||
G.add_edge("OldMan", "Myriel", weight=1)
|
||||
G.add_edge("Valjean", "Labarre", weight=1)
|
||||
G.add_edge("Valjean", "MmeMagloire", weight=3)
|
||||
G.add_edge("Valjean", "MlleBaptistine", weight=3)
|
||||
G.add_edge("Valjean", "Myriel", weight=5)
|
||||
G.add_edge("Marguerite", "Valjean", weight=1)
|
||||
G.add_edge("MmeDeR", "Valjean", weight=1)
|
||||
G.add_edge("Isabeau", "Valjean", weight=1)
|
||||
G.add_edge("Gervais", "Valjean", weight=1)
|
||||
G.add_edge("Listolier", "Tholomyes", weight=4)
|
||||
G.add_edge("Fameuil", "Tholomyes", weight=4)
|
||||
G.add_edge("Fameuil", "Listolier", weight=4)
|
||||
G.add_edge("Blacheville", "Tholomyes", weight=4)
|
||||
G.add_edge("Blacheville", "Listolier", weight=4)
|
||||
G.add_edge("Blacheville", "Fameuil", weight=4)
|
||||
G.add_edge("Favourite", "Tholomyes", weight=3)
|
||||
G.add_edge("Favourite", "Listolier", weight=3)
|
||||
G.add_edge("Favourite", "Fameuil", weight=3)
|
||||
G.add_edge("Favourite", "Blacheville", weight=4)
|
||||
G.add_edge("Dahlia", "Tholomyes", weight=3)
|
||||
G.add_edge("Dahlia", "Listolier", weight=3)
|
||||
G.add_edge("Dahlia", "Fameuil", weight=3)
|
||||
G.add_edge("Dahlia", "Blacheville", weight=3)
|
||||
G.add_edge("Dahlia", "Favourite", weight=5)
|
||||
G.add_edge("Zephine", "Tholomyes", weight=3)
|
||||
G.add_edge("Zephine", "Listolier", weight=3)
|
||||
G.add_edge("Zephine", "Fameuil", weight=3)
|
||||
G.add_edge("Zephine", "Blacheville", weight=3)
|
||||
G.add_edge("Zephine", "Favourite", weight=4)
|
||||
G.add_edge("Zephine", "Dahlia", weight=4)
|
||||
G.add_edge("Fantine", "Tholomyes", weight=3)
|
||||
G.add_edge("Fantine", "Listolier", weight=3)
|
||||
G.add_edge("Fantine", "Fameuil", weight=3)
|
||||
G.add_edge("Fantine", "Blacheville", weight=3)
|
||||
G.add_edge("Fantine", "Favourite", weight=4)
|
||||
G.add_edge("Fantine", "Dahlia", weight=4)
|
||||
G.add_edge("Fantine", "Zephine", weight=4)
|
||||
G.add_edge("Fantine", "Marguerite", weight=2)
|
||||
G.add_edge("Fantine", "Valjean", weight=9)
|
||||
G.add_edge("MmeThenardier", "Fantine", weight=2)
|
||||
G.add_edge("MmeThenardier", "Valjean", weight=7)
|
||||
G.add_edge("Thenardier", "MmeThenardier", weight=13)
|
||||
G.add_edge("Thenardier", "Fantine", weight=1)
|
||||
G.add_edge("Thenardier", "Valjean", weight=12)
|
||||
G.add_edge("Cosette", "MmeThenardier", weight=4)
|
||||
G.add_edge("Cosette", "Valjean", weight=31)
|
||||
G.add_edge("Cosette", "Tholomyes", weight=1)
|
||||
G.add_edge("Cosette", "Thenardier", weight=1)
|
||||
G.add_edge("Javert", "Valjean", weight=17)
|
||||
G.add_edge("Javert", "Fantine", weight=5)
|
||||
G.add_edge("Javert", "Thenardier", weight=5)
|
||||
G.add_edge("Javert", "MmeThenardier", weight=1)
|
||||
G.add_edge("Javert", "Cosette", weight=1)
|
||||
G.add_edge("Fauchelevent", "Valjean", weight=8)
|
||||
G.add_edge("Fauchelevent", "Javert", weight=1)
|
||||
G.add_edge("Bamatabois", "Fantine", weight=1)
|
||||
G.add_edge("Bamatabois", "Javert", weight=1)
|
||||
G.add_edge("Bamatabois", "Valjean", weight=2)
|
||||
G.add_edge("Perpetue", "Fantine", weight=1)
|
||||
G.add_edge("Simplice", "Perpetue", weight=2)
|
||||
G.add_edge("Simplice", "Valjean", weight=3)
|
||||
G.add_edge("Simplice", "Fantine", weight=2)
|
||||
G.add_edge("Simplice", "Javert", weight=1)
|
||||
G.add_edge("Scaufflaire", "Valjean", weight=1)
|
||||
G.add_edge("Woman1", "Valjean", weight=2)
|
||||
G.add_edge("Woman1", "Javert", weight=1)
|
||||
G.add_edge("Judge", "Valjean", weight=3)
|
||||
G.add_edge("Judge", "Bamatabois", weight=2)
|
||||
G.add_edge("Champmathieu", "Valjean", weight=3)
|
||||
G.add_edge("Champmathieu", "Judge", weight=3)
|
||||
G.add_edge("Champmathieu", "Bamatabois", weight=2)
|
||||
G.add_edge("Brevet", "Judge", weight=2)
|
||||
G.add_edge("Brevet", "Champmathieu", weight=2)
|
||||
G.add_edge("Brevet", "Valjean", weight=2)
|
||||
G.add_edge("Brevet", "Bamatabois", weight=1)
|
||||
G.add_edge("Chenildieu", "Judge", weight=2)
|
||||
G.add_edge("Chenildieu", "Champmathieu", weight=2)
|
||||
G.add_edge("Chenildieu", "Brevet", weight=2)
|
||||
G.add_edge("Chenildieu", "Valjean", weight=2)
|
||||
G.add_edge("Chenildieu", "Bamatabois", weight=1)
|
||||
G.add_edge("Cochepaille", "Judge", weight=2)
|
||||
G.add_edge("Cochepaille", "Champmathieu", weight=2)
|
||||
G.add_edge("Cochepaille", "Brevet", weight=2)
|
||||
G.add_edge("Cochepaille", "Chenildieu", weight=2)
|
||||
G.add_edge("Cochepaille", "Valjean", weight=2)
|
||||
G.add_edge("Cochepaille", "Bamatabois", weight=1)
|
||||
G.add_edge("Pontmercy", "Thenardier", weight=1)
|
||||
G.add_edge("Boulatruelle", "Thenardier", weight=1)
|
||||
G.add_edge("Eponine", "MmeThenardier", weight=2)
|
||||
G.add_edge("Eponine", "Thenardier", weight=3)
|
||||
G.add_edge("Anzelma", "Eponine", weight=2)
|
||||
G.add_edge("Anzelma", "Thenardier", weight=2)
|
||||
G.add_edge("Anzelma", "MmeThenardier", weight=1)
|
||||
G.add_edge("Woman2", "Valjean", weight=3)
|
||||
G.add_edge("Woman2", "Cosette", weight=1)
|
||||
G.add_edge("Woman2", "Javert", weight=1)
|
||||
G.add_edge("MotherInnocent", "Fauchelevent", weight=3)
|
||||
G.add_edge("MotherInnocent", "Valjean", weight=1)
|
||||
G.add_edge("Gribier", "Fauchelevent", weight=2)
|
||||
G.add_edge("MmeBurgon", "Jondrette", weight=1)
|
||||
G.add_edge("Gavroche", "MmeBurgon", weight=2)
|
||||
G.add_edge("Gavroche", "Thenardier", weight=1)
|
||||
G.add_edge("Gavroche", "Javert", weight=1)
|
||||
G.add_edge("Gavroche", "Valjean", weight=1)
|
||||
G.add_edge("Gillenormand", "Cosette", weight=3)
|
||||
G.add_edge("Gillenormand", "Valjean", weight=2)
|
||||
G.add_edge("Magnon", "Gillenormand", weight=1)
|
||||
G.add_edge("Magnon", "MmeThenardier", weight=1)
|
||||
G.add_edge("MlleGillenormand", "Gillenormand", weight=9)
|
||||
G.add_edge("MlleGillenormand", "Cosette", weight=2)
|
||||
G.add_edge("MlleGillenormand", "Valjean", weight=2)
|
||||
G.add_edge("MmePontmercy", "MlleGillenormand", weight=1)
|
||||
G.add_edge("MmePontmercy", "Pontmercy", weight=1)
|
||||
G.add_edge("MlleVaubois", "MlleGillenormand", weight=1)
|
||||
G.add_edge("LtGillenormand", "MlleGillenormand", weight=2)
|
||||
G.add_edge("LtGillenormand", "Gillenormand", weight=1)
|
||||
G.add_edge("LtGillenormand", "Cosette", weight=1)
|
||||
G.add_edge("Marius", "MlleGillenormand", weight=6)
|
||||
G.add_edge("Marius", "Gillenormand", weight=12)
|
||||
G.add_edge("Marius", "Pontmercy", weight=1)
|
||||
G.add_edge("Marius", "LtGillenormand", weight=1)
|
||||
G.add_edge("Marius", "Cosette", weight=21)
|
||||
G.add_edge("Marius", "Valjean", weight=19)
|
||||
G.add_edge("Marius", "Tholomyes", weight=1)
|
||||
G.add_edge("Marius", "Thenardier", weight=2)
|
||||
G.add_edge("Marius", "Eponine", weight=5)
|
||||
G.add_edge("Marius", "Gavroche", weight=4)
|
||||
G.add_edge("BaronessT", "Gillenormand", weight=1)
|
||||
G.add_edge("BaronessT", "Marius", weight=1)
|
||||
G.add_edge("Mabeuf", "Marius", weight=1)
|
||||
G.add_edge("Mabeuf", "Eponine", weight=1)
|
||||
G.add_edge("Mabeuf", "Gavroche", weight=1)
|
||||
G.add_edge("Enjolras", "Marius", weight=7)
|
||||
G.add_edge("Enjolras", "Gavroche", weight=7)
|
||||
G.add_edge("Enjolras", "Javert", weight=6)
|
||||
G.add_edge("Enjolras", "Mabeuf", weight=1)
|
||||
G.add_edge("Enjolras", "Valjean", weight=4)
|
||||
G.add_edge("Combeferre", "Enjolras", weight=15)
|
||||
G.add_edge("Combeferre", "Marius", weight=5)
|
||||
G.add_edge("Combeferre", "Gavroche", weight=6)
|
||||
G.add_edge("Combeferre", "Mabeuf", weight=2)
|
||||
G.add_edge("Prouvaire", "Gavroche", weight=1)
|
||||
G.add_edge("Prouvaire", "Enjolras", weight=4)
|
||||
G.add_edge("Prouvaire", "Combeferre", weight=2)
|
||||
G.add_edge("Feuilly", "Gavroche", weight=2)
|
||||
G.add_edge("Feuilly", "Enjolras", weight=6)
|
||||
G.add_edge("Feuilly", "Prouvaire", weight=2)
|
||||
G.add_edge("Feuilly", "Combeferre", weight=5)
|
||||
G.add_edge("Feuilly", "Mabeuf", weight=1)
|
||||
G.add_edge("Feuilly", "Marius", weight=1)
|
||||
G.add_edge("Courfeyrac", "Marius", weight=9)
|
||||
G.add_edge("Courfeyrac", "Enjolras", weight=17)
|
||||
G.add_edge("Courfeyrac", "Combeferre", weight=13)
|
||||
G.add_edge("Courfeyrac", "Gavroche", weight=7)
|
||||
G.add_edge("Courfeyrac", "Mabeuf", weight=2)
|
||||
G.add_edge("Courfeyrac", "Eponine", weight=1)
|
||||
G.add_edge("Courfeyrac", "Feuilly", weight=6)
|
||||
G.add_edge("Courfeyrac", "Prouvaire", weight=3)
|
||||
G.add_edge("Bahorel", "Combeferre", weight=5)
|
||||
G.add_edge("Bahorel", "Gavroche", weight=5)
|
||||
G.add_edge("Bahorel", "Courfeyrac", weight=6)
|
||||
G.add_edge("Bahorel", "Mabeuf", weight=2)
|
||||
G.add_edge("Bahorel", "Enjolras", weight=4)
|
||||
G.add_edge("Bahorel", "Feuilly", weight=3)
|
||||
G.add_edge("Bahorel", "Prouvaire", weight=2)
|
||||
G.add_edge("Bahorel", "Marius", weight=1)
|
||||
G.add_edge("Bossuet", "Marius", weight=5)
|
||||
G.add_edge("Bossuet", "Courfeyrac", weight=12)
|
||||
G.add_edge("Bossuet", "Gavroche", weight=5)
|
||||
G.add_edge("Bossuet", "Bahorel", weight=4)
|
||||
G.add_edge("Bossuet", "Enjolras", weight=10)
|
||||
G.add_edge("Bossuet", "Feuilly", weight=6)
|
||||
G.add_edge("Bossuet", "Prouvaire", weight=2)
|
||||
G.add_edge("Bossuet", "Combeferre", weight=9)
|
||||
G.add_edge("Bossuet", "Mabeuf", weight=1)
|
||||
G.add_edge("Bossuet", "Valjean", weight=1)
|
||||
G.add_edge("Joly", "Bahorel", weight=5)
|
||||
G.add_edge("Joly", "Bossuet", weight=7)
|
||||
G.add_edge("Joly", "Gavroche", weight=3)
|
||||
G.add_edge("Joly", "Courfeyrac", weight=5)
|
||||
G.add_edge("Joly", "Enjolras", weight=5)
|
||||
G.add_edge("Joly", "Feuilly", weight=5)
|
||||
G.add_edge("Joly", "Prouvaire", weight=2)
|
||||
G.add_edge("Joly", "Combeferre", weight=5)
|
||||
G.add_edge("Joly", "Mabeuf", weight=1)
|
||||
G.add_edge("Joly", "Marius", weight=2)
|
||||
G.add_edge("Grantaire", "Bossuet", weight=3)
|
||||
G.add_edge("Grantaire", "Enjolras", weight=3)
|
||||
G.add_edge("Grantaire", "Combeferre", weight=1)
|
||||
G.add_edge("Grantaire", "Courfeyrac", weight=2)
|
||||
G.add_edge("Grantaire", "Joly", weight=2)
|
||||
G.add_edge("Grantaire", "Gavroche", weight=1)
|
||||
G.add_edge("Grantaire", "Bahorel", weight=1)
|
||||
G.add_edge("Grantaire", "Feuilly", weight=1)
|
||||
G.add_edge("Grantaire", "Prouvaire", weight=1)
|
||||
G.add_edge("MotherPlutarch", "Mabeuf", weight=3)
|
||||
G.add_edge("Gueulemer", "Thenardier", weight=5)
|
||||
G.add_edge("Gueulemer", "Valjean", weight=1)
|
||||
G.add_edge("Gueulemer", "MmeThenardier", weight=1)
|
||||
G.add_edge("Gueulemer", "Javert", weight=1)
|
||||
G.add_edge("Gueulemer", "Gavroche", weight=1)
|
||||
G.add_edge("Gueulemer", "Eponine", weight=1)
|
||||
G.add_edge("Babet", "Thenardier", weight=6)
|
||||
G.add_edge("Babet", "Gueulemer", weight=6)
|
||||
G.add_edge("Babet", "Valjean", weight=1)
|
||||
G.add_edge("Babet", "MmeThenardier", weight=1)
|
||||
G.add_edge("Babet", "Javert", weight=2)
|
||||
G.add_edge("Babet", "Gavroche", weight=1)
|
||||
G.add_edge("Babet", "Eponine", weight=1)
|
||||
G.add_edge("Claquesous", "Thenardier", weight=4)
|
||||
G.add_edge("Claquesous", "Babet", weight=4)
|
||||
G.add_edge("Claquesous", "Gueulemer", weight=4)
|
||||
G.add_edge("Claquesous", "Valjean", weight=1)
|
||||
G.add_edge("Claquesous", "MmeThenardier", weight=1)
|
||||
G.add_edge("Claquesous", "Javert", weight=1)
|
||||
G.add_edge("Claquesous", "Eponine", weight=1)
|
||||
G.add_edge("Claquesous", "Enjolras", weight=1)
|
||||
G.add_edge("Montparnasse", "Javert", weight=1)
|
||||
G.add_edge("Montparnasse", "Babet", weight=2)
|
||||
G.add_edge("Montparnasse", "Gueulemer", weight=2)
|
||||
G.add_edge("Montparnasse", "Claquesous", weight=2)
|
||||
G.add_edge("Montparnasse", "Valjean", weight=1)
|
||||
G.add_edge("Montparnasse", "Gavroche", weight=1)
|
||||
G.add_edge("Montparnasse", "Eponine", weight=1)
|
||||
G.add_edge("Montparnasse", "Thenardier", weight=1)
|
||||
G.add_edge("Toussaint", "Cosette", weight=2)
|
||||
G.add_edge("Toussaint", "Javert", weight=1)
|
||||
G.add_edge("Toussaint", "Valjean", weight=1)
|
||||
G.add_edge("Child1", "Gavroche", weight=2)
|
||||
G.add_edge("Child2", "Gavroche", weight=2)
|
||||
G.add_edge("Child2", "Child1", weight=3)
|
||||
G.add_edge("Brujon", "Babet", weight=3)
|
||||
G.add_edge("Brujon", "Gueulemer", weight=3)
|
||||
G.add_edge("Brujon", "Thenardier", weight=3)
|
||||
G.add_edge("Brujon", "Gavroche", weight=1)
|
||||
G.add_edge("Brujon", "Eponine", weight=1)
|
||||
G.add_edge("Brujon", "Claquesous", weight=1)
|
||||
G.add_edge("Brujon", "Montparnasse", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Bossuet", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Joly", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Grantaire", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Bahorel", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Courfeyrac", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Gavroche", weight=1)
|
||||
G.add_edge("MmeHucheloup", "Enjolras", weight=1)
|
||||
return G
|
||||
121
.CondaPkg/env/Lib/site-packages/networkx/generators/spectral_graph_forge.py
vendored
Normal file
121
.CondaPkg/env/Lib/site-packages/networkx/generators/spectral_graph_forge.py
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Generates graphs with a given eigenvector structure"""
|
||||
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import np_random_state
|
||||
|
||||
__all__ = ["spectral_graph_forge"]
|
||||
|
||||
|
||||
@np_random_state(3)
|
||||
@nx._dispatchable(returns_graph=True)
|
||||
def spectral_graph_forge(G, alpha, transformation="identity", seed=None):
|
||||
"""Returns a random simple graph with spectrum resembling that of `G`
|
||||
|
||||
This algorithm, called Spectral Graph Forge (SGF), computes the
|
||||
eigenvectors of a given graph adjacency matrix, filters them and
|
||||
builds a random graph with a similar eigenstructure.
|
||||
SGF has been proved to be particularly useful for synthesizing
|
||||
realistic social networks and it can also be used to anonymize
|
||||
graph sensitive data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : Graph
|
||||
alpha : float
|
||||
Ratio representing the percentage of eigenvectors of G to consider,
|
||||
values in [0,1].
|
||||
transformation : string, optional
|
||||
Represents the intended matrix linear transformation, possible values
|
||||
are 'identity' and 'modularity'
|
||||
seed : integer, random_state, or None (default)
|
||||
Indicator of numpy random number generation state.
|
||||
See :ref:`Randomness<randomness>`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
H : Graph
|
||||
A graph with a similar eigenvector structure of the input one.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If transformation has a value different from 'identity' or 'modularity'
|
||||
|
||||
Notes
|
||||
-----
|
||||
Spectral Graph Forge (SGF) generates a random simple graph resembling the
|
||||
global properties of the given one.
|
||||
It leverages the low-rank approximation of the associated adjacency matrix
|
||||
driven by the *alpha* precision parameter.
|
||||
SGF preserves the number of nodes of the input graph and their ordering.
|
||||
This way, nodes of output graphs resemble the properties of the input one
|
||||
and attributes can be directly mapped.
|
||||
|
||||
It considers the graph adjacency matrices which can optionally be
|
||||
transformed to other symmetric real matrices (currently transformation
|
||||
options include *identity* and *modularity*).
|
||||
The *modularity* transformation, in the sense of Newman's modularity matrix
|
||||
allows the focusing on community structure related properties of the graph.
|
||||
|
||||
SGF applies a low-rank approximation whose fixed rank is computed from the
|
||||
ratio *alpha* of the input graph adjacency matrix dimension.
|
||||
This step performs a filtering on the input eigenvectors similar to the low
|
||||
pass filtering common in telecommunications.
|
||||
|
||||
The filtered values (after truncation) are used as input to a Bernoulli
|
||||
sampling for constructing a random adjacency matrix.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] L. Baldesi, C. T. Butts, A. Markopoulou, "Spectral Graph Forge:
|
||||
Graph Generation Targeting Modularity", IEEE Infocom, '18.
|
||||
https://arxiv.org/abs/1801.01715
|
||||
.. [2] M. Newman, "Networks: an introduction", Oxford university press,
|
||||
2010
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.karate_club_graph()
|
||||
>>> H = nx.spectral_graph_forge(G, 0.3)
|
||||
>>>
|
||||
"""
|
||||
import numpy as np
|
||||
import scipy as sp
|
||||
|
||||
available_transformations = ["identity", "modularity"]
|
||||
alpha = np.clip(alpha, 0, 1)
|
||||
A = nx.to_numpy_array(G)
|
||||
n = A.shape[1]
|
||||
level = round(n * alpha)
|
||||
|
||||
if transformation not in available_transformations:
|
||||
msg = f"{transformation!r} is not a valid transformation. "
|
||||
msg += f"Transformations: {available_transformations}"
|
||||
raise nx.NetworkXError(msg)
|
||||
|
||||
K = np.ones((1, n)) @ A
|
||||
|
||||
B = A
|
||||
if transformation == "modularity":
|
||||
B -= K.T @ K / K.sum()
|
||||
|
||||
# Compute low-rank approximation of B
|
||||
evals, evecs = np.linalg.eigh(B)
|
||||
k = np.argsort(np.abs(evals))[::-1] # indices of evals in descending order
|
||||
evecs[:, k[np.arange(level, n)]] = 0 # set smallest eigenvectors to 0
|
||||
B = evecs @ np.diag(evals) @ evecs.T
|
||||
|
||||
if transformation == "modularity":
|
||||
B += K.T @ K / K.sum()
|
||||
|
||||
B = np.clip(B, 0, 1)
|
||||
np.fill_diagonal(B, 0)
|
||||
|
||||
for i in range(n - 1):
|
||||
B[i, i + 1 :] = sp.stats.bernoulli.rvs(B[i, i + 1 :], random_state=seed)
|
||||
B[i + 1 :, i] = np.transpose(B[i, i + 1 :])
|
||||
|
||||
H = nx.from_numpy_array(B)
|
||||
|
||||
return H
|
||||
54
.CondaPkg/env/Lib/site-packages/networkx/generators/stochastic.py
vendored
Normal file
54
.CondaPkg/env/Lib/site-packages/networkx/generators/stochastic.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Functions for generating stochastic graphs from a given weighted directed
|
||||
graph.
|
||||
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.classes import DiGraph, MultiDiGraph
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = ["stochastic_graph"]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
@nx._dispatchable(
|
||||
edge_attrs="weight", mutates_input={"not copy": 1}, returns_graph=True
|
||||
)
|
||||
def stochastic_graph(G, copy=True, weight="weight"):
|
||||
"""Returns a right-stochastic representation of directed graph `G`.
|
||||
|
||||
A right-stochastic graph is a weighted digraph in which for each
|
||||
node, the sum of the weights of all the out-edges of that node is
|
||||
1. If the graph is already weighted (for example, via a 'weight'
|
||||
edge attribute), the reweighting takes that into account.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : directed graph
|
||||
A :class:`~networkx.DiGraph` or :class:`~networkx.MultiDiGraph`.
|
||||
|
||||
copy : boolean, optional
|
||||
If this is True, then this function returns a new graph with
|
||||
the stochastic reweighting. Otherwise, the original graph is
|
||||
modified in-place (and also returned, for convenience).
|
||||
|
||||
weight : edge attribute key (optional, default='weight')
|
||||
Edge attribute key used for reading the existing weight and
|
||||
setting the new weight. If no attribute with this key is found
|
||||
for an edge, then the edge weight is assumed to be 1. If an edge
|
||||
has a weight, it must be a positive number.
|
||||
|
||||
"""
|
||||
if copy:
|
||||
G = MultiDiGraph(G) if G.is_multigraph() else DiGraph(G)
|
||||
# There is a tradeoff here: the dictionary of node degrees may
|
||||
# require a lot of memory, whereas making a call to `G.out_degree`
|
||||
# inside the loop may be costly in computation time.
|
||||
degree = dict(G.out_degree(weight=weight))
|
||||
for u, v, d in G.edges(data=True):
|
||||
if degree[u] == 0:
|
||||
d[weight] = 0
|
||||
else:
|
||||
d[weight] = d.get(weight, 1) / degree[u]
|
||||
nx._clear_cache(G)
|
||||
return G
|
||||
131
.CondaPkg/env/Lib/site-packages/networkx/generators/sudoku.py
vendored
Normal file
131
.CondaPkg/env/Lib/site-packages/networkx/generators/sudoku.py
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
"""Generator for Sudoku graphs
|
||||
|
||||
This module gives a generator for n-Sudoku graphs. It can be used to develop
|
||||
algorithms for solving or generating Sudoku puzzles.
|
||||
|
||||
A completed Sudoku grid is a 9x9 array of integers between 1 and 9, with no
|
||||
number appearing twice in the same row, column, or 3x3 box.
|
||||
|
||||
+---------+---------+---------+
|
||||
| | 8 6 4 | | 3 7 1 | | 2 5 9 |
|
||||
| | 3 2 5 | | 8 4 9 | | 7 6 1 |
|
||||
| | 9 7 1 | | 2 6 5 | | 8 4 3 |
|
||||
+---------+---------+---------+
|
||||
| | 4 3 6 | | 1 9 2 | | 5 8 7 |
|
||||
| | 1 9 8 | | 6 5 7 | | 4 3 2 |
|
||||
| | 2 5 7 | | 4 8 3 | | 9 1 6 |
|
||||
+---------+---------+---------+
|
||||
| | 6 8 9 | | 7 3 4 | | 1 2 5 |
|
||||
| | 7 1 3 | | 5 2 8 | | 6 9 4 |
|
||||
| | 5 4 2 | | 9 1 6 | | 3 7 8 |
|
||||
+---------+---------+---------+
|
||||
|
||||
|
||||
The Sudoku graph is an undirected graph with 81 vertices, corresponding to
|
||||
the cells of a Sudoku grid. It is a regular graph of degree 20. Two distinct
|
||||
vertices are adjacent if and only if the corresponding cells belong to the
|
||||
same row, column, or box. A completed Sudoku grid corresponds to a vertex
|
||||
coloring of the Sudoku graph with nine colors.
|
||||
|
||||
More generally, the n-Sudoku graph is a graph with n^4 vertices, corresponding
|
||||
to the cells of an n^2 by n^2 grid. Two distinct vertices are adjacent if and
|
||||
only if they belong to the same row, column, or n by n box.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Herzberg, A. M., & Murty, M. R. (2007). Sudoku squares and chromatic
|
||||
polynomials. Notices of the AMS, 54(6), 708-717.
|
||||
.. [2] Sander, Torsten (2009), "Sudoku graphs are integral",
|
||||
Electronic Journal of Combinatorics, 16 (1): Note 25, 7pp, MR 2529816
|
||||
.. [3] Wikipedia contributors. "Glossary of Sudoku." Wikipedia, The Free
|
||||
Encyclopedia, 3 Dec. 2019. Web. 22 Dec. 2019.
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError
|
||||
|
||||
__all__ = ["sudoku_graph"]
|
||||
|
||||
|
||||
@nx._dispatchable(graphs=None, returns_graph=True)
|
||||
def sudoku_graph(n=3):
|
||||
"""Returns the n-Sudoku graph. The default value of n is 3.
|
||||
|
||||
The n-Sudoku graph is a graph with n^4 vertices, corresponding to the
|
||||
cells of an n^2 by n^2 grid. Two distinct vertices are adjacent if and
|
||||
only if they belong to the same row, column, or n-by-n box.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n: integer
|
||||
The order of the Sudoku graph, equal to the square root of the
|
||||
number of rows. The default is 3.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX graph
|
||||
The n-Sudoku graph Sud(n).
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.sudoku_graph()
|
||||
>>> G.number_of_nodes()
|
||||
81
|
||||
>>> G.number_of_edges()
|
||||
810
|
||||
>>> sorted(G.neighbors(42))
|
||||
[6, 15, 24, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 51, 52, 53, 60, 69, 78]
|
||||
>>> G = nx.sudoku_graph(2)
|
||||
>>> G.number_of_nodes()
|
||||
16
|
||||
>>> G.number_of_edges()
|
||||
56
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Herzberg, A. M., & Murty, M. R. (2007). Sudoku squares and chromatic
|
||||
polynomials. Notices of the AMS, 54(6), 708-717.
|
||||
.. [2] Sander, Torsten (2009), "Sudoku graphs are integral",
|
||||
Electronic Journal of Combinatorics, 16 (1): Note 25, 7pp, MR 2529816
|
||||
.. [3] Wikipedia contributors. "Glossary of Sudoku." Wikipedia, The Free
|
||||
Encyclopedia, 3 Dec. 2019. Web. 22 Dec. 2019.
|
||||
"""
|
||||
|
||||
if n < 0:
|
||||
raise NetworkXError("The order must be greater than or equal to zero.")
|
||||
|
||||
n2 = n * n
|
||||
n3 = n2 * n
|
||||
n4 = n3 * n
|
||||
|
||||
# Construct an empty graph with n^4 nodes
|
||||
G = nx.empty_graph(n4)
|
||||
|
||||
# A Sudoku graph of order 0 or 1 has no edges
|
||||
if n < 2:
|
||||
return G
|
||||
|
||||
# Add edges for cells in the same row
|
||||
for row_no in range(n2):
|
||||
row_start = row_no * n2
|
||||
for j in range(1, n2):
|
||||
for i in range(j):
|
||||
G.add_edge(row_start + i, row_start + j)
|
||||
|
||||
# Add edges for cells in the same column
|
||||
for col_no in range(n2):
|
||||
for j in range(col_no, n4, n2):
|
||||
for i in range(col_no, j, n2):
|
||||
G.add_edge(i, j)
|
||||
|
||||
# Add edges for cells in the same box
|
||||
for band_no in range(n):
|
||||
for stack_no in range(n):
|
||||
box_start = n3 * band_no + n * stack_no
|
||||
for j in range(1, n2):
|
||||
for i in range(j):
|
||||
u = box_start + (i % n) + n2 * (i // n)
|
||||
v = box_start + (j % n) + n2 * (j // n)
|
||||
G.add_edge(u, v)
|
||||
|
||||
return G
|
||||
0
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__init__.py
vendored
Normal file
0
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__init__.py
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_atlas.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_atlas.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_classic.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_classic.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_cographs.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_cographs.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_community.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_community.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_directed.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_directed.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_ego.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_ego.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_expanders.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_expanders.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_geometric.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_geometric.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_lattice.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_lattice.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_line.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_line.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_mycielski.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_mycielski.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_small.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_small.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_sudoku.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_sudoku.cpython-312.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_trees.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_trees.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_triads.cpython-312.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/__pycache__/test_triads.cpython-312.pyc
vendored
Normal file
Binary file not shown.
75
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_atlas.py
vendored
Normal file
75
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_atlas.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
from itertools import groupby
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx import graph_atlas, graph_atlas_g
|
||||
from networkx.generators.atlas import NUM_GRAPHS
|
||||
from networkx.utils import edges_equal, nodes_equal, pairwise
|
||||
|
||||
|
||||
class TestAtlasGraph:
|
||||
"""Unit tests for the :func:`~networkx.graph_atlas` function."""
|
||||
|
||||
def test_index_too_small(self):
|
||||
with pytest.raises(ValueError):
|
||||
graph_atlas(-1)
|
||||
|
||||
def test_index_too_large(self):
|
||||
with pytest.raises(ValueError):
|
||||
graph_atlas(NUM_GRAPHS)
|
||||
|
||||
def test_graph(self):
|
||||
G = graph_atlas(6)
|
||||
assert nodes_equal(G.nodes(), range(3))
|
||||
assert edges_equal(G.edges(), [(0, 1), (0, 2)])
|
||||
|
||||
|
||||
class TestAtlasGraphG:
|
||||
"""Unit tests for the :func:`~networkx.graph_atlas_g` function."""
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.GAG = graph_atlas_g()
|
||||
|
||||
def test_sizes(self):
|
||||
G = self.GAG[0]
|
||||
assert G.number_of_nodes() == 0
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
G = self.GAG[7]
|
||||
assert G.number_of_nodes() == 3
|
||||
assert G.number_of_edges() == 3
|
||||
|
||||
def test_names(self):
|
||||
for i, G in enumerate(self.GAG):
|
||||
assert int(G.name[1:]) == i
|
||||
|
||||
def test_nondecreasing_nodes(self):
|
||||
# check for nondecreasing number of nodes
|
||||
for n1, n2 in pairwise(map(len, self.GAG)):
|
||||
assert n2 <= n1 + 1
|
||||
|
||||
def test_nondecreasing_edges(self):
|
||||
# check for nondecreasing number of edges (for fixed number of
|
||||
# nodes)
|
||||
for n, group in groupby(self.GAG, key=nx.number_of_nodes):
|
||||
for m1, m2 in pairwise(map(nx.number_of_edges, group)):
|
||||
assert m2 <= m1 + 1
|
||||
|
||||
def test_nondecreasing_degree_sequence(self):
|
||||
# Check for lexicographically nondecreasing degree sequences
|
||||
# (for fixed number of nodes and edges).
|
||||
#
|
||||
# There are three exceptions to this rule in the order given in
|
||||
# the "Atlas of Graphs" book, so we need to manually exclude
|
||||
# those.
|
||||
exceptions = [("G55", "G56"), ("G1007", "G1008"), ("G1012", "G1013")]
|
||||
for n, group in groupby(self.GAG, key=nx.number_of_nodes):
|
||||
for m, group in groupby(group, key=nx.number_of_edges):
|
||||
for G1, G2 in pairwise(group):
|
||||
if (G1.name, G2.name) in exceptions:
|
||||
continue
|
||||
d1 = sorted(d for v, d in G1.degree())
|
||||
d2 = sorted(d for v, d in G2.degree())
|
||||
assert d1 <= d2
|
||||
635
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_classic.py
vendored
Normal file
635
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_classic.py
vendored
Normal file
@@ -0,0 +1,635 @@
|
||||
"""
|
||||
====================
|
||||
Generators - Classic
|
||||
====================
|
||||
|
||||
Unit tests for various classic graph generators in generators/classic.py
|
||||
"""
|
||||
import itertools
|
||||
import typing
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic
|
||||
from networkx.utils import edges_equal, nodes_equal
|
||||
|
||||
is_isomorphic = graph_could_be_isomorphic
|
||||
|
||||
|
||||
class TestGeneratorClassic:
|
||||
def test_balanced_tree(self):
|
||||
# balanced_tree(r,h) is a tree with (r**(h+1)-1)/(r-1) edges
|
||||
for r, h in [(2, 2), (3, 3), (6, 2)]:
|
||||
t = nx.balanced_tree(r, h)
|
||||
order = t.order()
|
||||
assert order == (r ** (h + 1) - 1) / (r - 1)
|
||||
assert nx.is_connected(t)
|
||||
assert t.size() == order - 1
|
||||
dh = nx.degree_histogram(t)
|
||||
assert dh[0] == 0 # no nodes of 0
|
||||
assert dh[1] == r**h # nodes of degree 1 are leaves
|
||||
assert dh[r] == 1 # root is degree r
|
||||
assert dh[r + 1] == order - r**h - 1 # everyone else is degree r+1
|
||||
assert len(dh) == r + 2
|
||||
|
||||
def test_balanced_tree_star(self):
|
||||
# balanced_tree(r,1) is the r-star
|
||||
t = nx.balanced_tree(r=2, h=1)
|
||||
assert is_isomorphic(t, nx.star_graph(2))
|
||||
t = nx.balanced_tree(r=5, h=1)
|
||||
assert is_isomorphic(t, nx.star_graph(5))
|
||||
t = nx.balanced_tree(r=10, h=1)
|
||||
assert is_isomorphic(t, nx.star_graph(10))
|
||||
|
||||
def test_balanced_tree_path(self):
|
||||
"""Tests that the balanced tree with branching factor one is the
|
||||
path graph.
|
||||
|
||||
"""
|
||||
# A tree of height four has five levels.
|
||||
T = nx.balanced_tree(1, 4)
|
||||
P = nx.path_graph(5)
|
||||
assert is_isomorphic(T, P)
|
||||
|
||||
def test_full_rary_tree(self):
|
||||
r = 2
|
||||
n = 9
|
||||
t = nx.full_rary_tree(r, n)
|
||||
assert t.order() == n
|
||||
assert nx.is_connected(t)
|
||||
dh = nx.degree_histogram(t)
|
||||
assert dh[0] == 0 # no nodes of 0
|
||||
assert dh[1] == 5 # nodes of degree 1 are leaves
|
||||
assert dh[r] == 1 # root is degree r
|
||||
assert dh[r + 1] == 9 - 5 - 1 # everyone else is degree r+1
|
||||
assert len(dh) == r + 2
|
||||
|
||||
def test_full_rary_tree_balanced(self):
|
||||
t = nx.full_rary_tree(2, 15)
|
||||
th = nx.balanced_tree(2, 3)
|
||||
assert is_isomorphic(t, th)
|
||||
|
||||
def test_full_rary_tree_path(self):
|
||||
t = nx.full_rary_tree(1, 10)
|
||||
assert is_isomorphic(t, nx.path_graph(10))
|
||||
|
||||
def test_full_rary_tree_empty(self):
|
||||
t = nx.full_rary_tree(0, 10)
|
||||
assert is_isomorphic(t, nx.empty_graph(10))
|
||||
t = nx.full_rary_tree(3, 0)
|
||||
assert is_isomorphic(t, nx.empty_graph(0))
|
||||
|
||||
def test_full_rary_tree_3_20(self):
|
||||
t = nx.full_rary_tree(3, 20)
|
||||
assert t.order() == 20
|
||||
|
||||
def test_barbell_graph(self):
|
||||
# number of nodes = 2*m1 + m2 (2 m1-complete graphs + m2-path + 2 edges)
|
||||
# number of edges = 2*(nx.number_of_edges(m1-complete graph) + m2 + 1
|
||||
m1 = 3
|
||||
m2 = 5
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert nx.number_of_nodes(b) == 2 * m1 + m2
|
||||
assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
|
||||
|
||||
m1 = 4
|
||||
m2 = 10
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert nx.number_of_nodes(b) == 2 * m1 + m2
|
||||
assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
|
||||
|
||||
m1 = 3
|
||||
m2 = 20
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert nx.number_of_nodes(b) == 2 * m1 + m2
|
||||
assert nx.number_of_edges(b) == m1 * (m1 - 1) + m2 + 1
|
||||
|
||||
# Raise NetworkXError if m1<2
|
||||
m1 = 1
|
||||
m2 = 20
|
||||
pytest.raises(nx.NetworkXError, nx.barbell_graph, m1, m2)
|
||||
|
||||
# Raise NetworkXError if m2<0
|
||||
m1 = 5
|
||||
m2 = -2
|
||||
pytest.raises(nx.NetworkXError, nx.barbell_graph, m1, m2)
|
||||
|
||||
# nx.barbell_graph(2,m) = nx.path_graph(m+4)
|
||||
m1 = 2
|
||||
m2 = 5
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert is_isomorphic(b, nx.path_graph(m2 + 4))
|
||||
|
||||
m1 = 2
|
||||
m2 = 10
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert is_isomorphic(b, nx.path_graph(m2 + 4))
|
||||
|
||||
m1 = 2
|
||||
m2 = 20
|
||||
b = nx.barbell_graph(m1, m2)
|
||||
assert is_isomorphic(b, nx.path_graph(m2 + 4))
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError, nx.barbell_graph, m1, m2, create_using=nx.DiGraph()
|
||||
)
|
||||
|
||||
mb = nx.barbell_graph(m1, m2, create_using=nx.MultiGraph())
|
||||
assert edges_equal(mb.edges(), b.edges())
|
||||
|
||||
def test_binomial_tree(self):
|
||||
graphs = (None, nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
|
||||
for create_using in graphs:
|
||||
for n in range(4):
|
||||
b = nx.binomial_tree(n, create_using)
|
||||
assert nx.number_of_nodes(b) == 2**n
|
||||
assert nx.number_of_edges(b) == (2**n - 1)
|
||||
|
||||
def test_complete_graph(self):
|
||||
# complete_graph(m) is a connected graph with
|
||||
# m nodes and m*(m+1)/2 edges
|
||||
for m in [0, 1, 3, 5]:
|
||||
g = nx.complete_graph(m)
|
||||
assert nx.number_of_nodes(g) == m
|
||||
assert nx.number_of_edges(g) == m * (m - 1) // 2
|
||||
|
||||
mg = nx.complete_graph(m, create_using=nx.MultiGraph)
|
||||
assert edges_equal(mg.edges(), g.edges())
|
||||
|
||||
g = nx.complete_graph("abc")
|
||||
assert nodes_equal(g.nodes(), ["a", "b", "c"])
|
||||
assert g.size() == 3
|
||||
|
||||
# creates a self-loop... should it? <backward compatible says yes>
|
||||
g = nx.complete_graph("abcb")
|
||||
assert nodes_equal(g.nodes(), ["a", "b", "c"])
|
||||
assert g.size() == 4
|
||||
|
||||
g = nx.complete_graph("abcb", create_using=nx.MultiGraph)
|
||||
assert nodes_equal(g.nodes(), ["a", "b", "c"])
|
||||
assert g.size() == 6
|
||||
|
||||
def test_complete_digraph(self):
|
||||
# complete_graph(m) is a connected graph with
|
||||
# m nodes and m*(m+1)/2 edges
|
||||
for m in [0, 1, 3, 5]:
|
||||
g = nx.complete_graph(m, create_using=nx.DiGraph)
|
||||
assert nx.number_of_nodes(g) == m
|
||||
assert nx.number_of_edges(g) == m * (m - 1)
|
||||
|
||||
g = nx.complete_graph("abc", create_using=nx.DiGraph)
|
||||
assert len(g) == 3
|
||||
assert g.size() == 6
|
||||
assert g.is_directed()
|
||||
|
||||
def test_circular_ladder_graph(self):
|
||||
G = nx.circular_ladder_graph(5)
|
||||
pytest.raises(
|
||||
nx.NetworkXError, nx.circular_ladder_graph, 5, create_using=nx.DiGraph
|
||||
)
|
||||
mG = nx.circular_ladder_graph(5, create_using=nx.MultiGraph)
|
||||
assert edges_equal(mG.edges(), G.edges())
|
||||
|
||||
def test_circulant_graph(self):
|
||||
# Ci_n(1) is the cycle graph for all n
|
||||
Ci6_1 = nx.circulant_graph(6, [1])
|
||||
C6 = nx.cycle_graph(6)
|
||||
assert edges_equal(Ci6_1.edges(), C6.edges())
|
||||
|
||||
# Ci_n(1, 2, ..., n div 2) is the complete graph for all n
|
||||
Ci7 = nx.circulant_graph(7, [1, 2, 3])
|
||||
K7 = nx.complete_graph(7)
|
||||
assert edges_equal(Ci7.edges(), K7.edges())
|
||||
|
||||
# Ci_6(1, 3) is K_3,3 i.e. the utility graph
|
||||
Ci6_1_3 = nx.circulant_graph(6, [1, 3])
|
||||
K3_3 = nx.complete_bipartite_graph(3, 3)
|
||||
assert is_isomorphic(Ci6_1_3, K3_3)
|
||||
|
||||
def test_cycle_graph(self):
|
||||
G = nx.cycle_graph(4)
|
||||
assert edges_equal(G.edges(), [(0, 1), (0, 3), (1, 2), (2, 3)])
|
||||
mG = nx.cycle_graph(4, create_using=nx.MultiGraph)
|
||||
assert edges_equal(mG.edges(), [(0, 1), (0, 3), (1, 2), (2, 3)])
|
||||
G = nx.cycle_graph(4, create_using=nx.DiGraph)
|
||||
assert not G.has_edge(2, 1)
|
||||
assert G.has_edge(1, 2)
|
||||
assert G.is_directed()
|
||||
|
||||
G = nx.cycle_graph("abc")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 3
|
||||
G = nx.cycle_graph("abcb")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 2
|
||||
g = nx.cycle_graph("abc", nx.DiGraph)
|
||||
assert len(g) == 3
|
||||
assert g.size() == 3
|
||||
assert g.is_directed()
|
||||
g = nx.cycle_graph("abcb", nx.DiGraph)
|
||||
assert len(g) == 3
|
||||
assert g.size() == 4
|
||||
|
||||
def test_dorogovtsev_goltsev_mendes_graph(self):
|
||||
G = nx.dorogovtsev_goltsev_mendes_graph(0)
|
||||
assert edges_equal(G.edges(), [(0, 1)])
|
||||
assert nodes_equal(list(G), [0, 1])
|
||||
G = nx.dorogovtsev_goltsev_mendes_graph(1)
|
||||
assert edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
|
||||
assert nx.average_clustering(G) == 1.0
|
||||
assert sorted(nx.triangles(G).values()) == [1, 1, 1]
|
||||
G = nx.dorogovtsev_goltsev_mendes_graph(10)
|
||||
assert nx.number_of_nodes(G) == 29526
|
||||
assert nx.number_of_edges(G) == 59049
|
||||
assert G.degree(0) == 1024
|
||||
assert G.degree(1) == 1024
|
||||
assert G.degree(2) == 1024
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
nx.dorogovtsev_goltsev_mendes_graph,
|
||||
7,
|
||||
create_using=nx.DiGraph,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
nx.dorogovtsev_goltsev_mendes_graph,
|
||||
7,
|
||||
create_using=nx.MultiGraph,
|
||||
)
|
||||
|
||||
def test_create_using(self):
|
||||
G = nx.empty_graph()
|
||||
assert isinstance(G, nx.Graph)
|
||||
pytest.raises(TypeError, nx.empty_graph, create_using=0.0)
|
||||
pytest.raises(TypeError, nx.empty_graph, create_using="Graph")
|
||||
|
||||
G = nx.empty_graph(create_using=nx.MultiGraph)
|
||||
assert isinstance(G, nx.MultiGraph)
|
||||
G = nx.empty_graph(create_using=nx.DiGraph)
|
||||
assert isinstance(G, nx.DiGraph)
|
||||
|
||||
G = nx.empty_graph(create_using=nx.DiGraph, default=nx.MultiGraph)
|
||||
assert isinstance(G, nx.DiGraph)
|
||||
G = nx.empty_graph(create_using=None, default=nx.MultiGraph)
|
||||
assert isinstance(G, nx.MultiGraph)
|
||||
G = nx.empty_graph(default=nx.MultiGraph)
|
||||
assert isinstance(G, nx.MultiGraph)
|
||||
|
||||
G = nx.path_graph(5)
|
||||
H = nx.empty_graph(create_using=G)
|
||||
assert not H.is_multigraph()
|
||||
assert not H.is_directed()
|
||||
assert len(H) == 0
|
||||
assert G is H
|
||||
|
||||
H = nx.empty_graph(create_using=nx.MultiGraph())
|
||||
assert H.is_multigraph()
|
||||
assert not H.is_directed()
|
||||
assert G is not H
|
||||
|
||||
# test for subclasses that also use typing.Protocol. See gh-6243
|
||||
class Mixin(typing.Protocol):
|
||||
pass
|
||||
|
||||
class MyGraph(Mixin, nx.DiGraph):
|
||||
pass
|
||||
|
||||
G = nx.empty_graph(create_using=MyGraph)
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.empty_graph()
|
||||
assert nx.number_of_nodes(G) == 0
|
||||
G = nx.empty_graph(42)
|
||||
assert nx.number_of_nodes(G) == 42
|
||||
assert nx.number_of_edges(G) == 0
|
||||
|
||||
G = nx.empty_graph("abc")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 0
|
||||
|
||||
# create empty digraph
|
||||
G = nx.empty_graph(42, create_using=nx.DiGraph(name="duh"))
|
||||
assert nx.number_of_nodes(G) == 42
|
||||
assert nx.number_of_edges(G) == 0
|
||||
assert isinstance(G, nx.DiGraph)
|
||||
|
||||
# create empty multigraph
|
||||
G = nx.empty_graph(42, create_using=nx.MultiGraph(name="duh"))
|
||||
assert nx.number_of_nodes(G) == 42
|
||||
assert nx.number_of_edges(G) == 0
|
||||
assert isinstance(G, nx.MultiGraph)
|
||||
|
||||
# create empty graph from another
|
||||
pete = nx.petersen_graph()
|
||||
G = nx.empty_graph(42, create_using=pete)
|
||||
assert nx.number_of_nodes(G) == 42
|
||||
assert nx.number_of_edges(G) == 0
|
||||
assert isinstance(G, nx.Graph)
|
||||
|
||||
def test_ladder_graph(self):
|
||||
for i, G in [
|
||||
(0, nx.empty_graph(0)),
|
||||
(1, nx.path_graph(2)),
|
||||
(2, nx.hypercube_graph(2)),
|
||||
(10, nx.grid_graph([2, 10])),
|
||||
]:
|
||||
assert is_isomorphic(nx.ladder_graph(i), G)
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.ladder_graph, 2, create_using=nx.DiGraph)
|
||||
|
||||
g = nx.ladder_graph(2)
|
||||
mg = nx.ladder_graph(2, create_using=nx.MultiGraph)
|
||||
assert edges_equal(mg.edges(), g.edges())
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [(3, 5), (4, 10), (3, 20)])
|
||||
def test_lollipop_graph_right_sizes(self, m, n):
|
||||
G = nx.lollipop_graph(m, n)
|
||||
assert nx.number_of_nodes(G) == m + n
|
||||
assert nx.number_of_edges(G) == m * (m - 1) / 2 + n
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [("ab", ""), ("abc", "defg")])
|
||||
def test_lollipop_graph_size_node_sequence(self, m, n):
|
||||
G = nx.lollipop_graph(m, n)
|
||||
assert nx.number_of_nodes(G) == len(m) + len(n)
|
||||
assert nx.number_of_edges(G) == len(m) * (len(m) - 1) / 2 + len(n)
|
||||
|
||||
def test_lollipop_graph_exceptions(self):
|
||||
# Raise NetworkXError if m<2
|
||||
pytest.raises(nx.NetworkXError, nx.lollipop_graph, -1, 2)
|
||||
pytest.raises(nx.NetworkXError, nx.lollipop_graph, 1, 20)
|
||||
pytest.raises(nx.NetworkXError, nx.lollipop_graph, "", 20)
|
||||
pytest.raises(nx.NetworkXError, nx.lollipop_graph, "a", 20)
|
||||
|
||||
# Raise NetworkXError if n<0
|
||||
pytest.raises(nx.NetworkXError, nx.lollipop_graph, 5, -2)
|
||||
|
||||
# raise NetworkXError if create_using is directed
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.lollipop_graph(2, 20, create_using=nx.DiGraph)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.lollipop_graph(2, 20, create_using=nx.MultiDiGraph)
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [(2, 0), (2, 5), (2, 10), ("ab", 20)])
|
||||
def test_lollipop_graph_same_as_path_when_m1_is_2(self, m, n):
|
||||
G = nx.lollipop_graph(m, n)
|
||||
assert is_isomorphic(G, nx.path_graph(n + 2))
|
||||
|
||||
def test_lollipop_graph_for_multigraph(self):
|
||||
G = nx.lollipop_graph(5, 20)
|
||||
MG = nx.lollipop_graph(5, 20, create_using=nx.MultiGraph)
|
||||
assert edges_equal(MG.edges(), G.edges())
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("m", "n"),
|
||||
[(4, "abc"), ("abcd", 3), ([1, 2, 3, 4], "abc"), ("abcd", [1, 2, 3])],
|
||||
)
|
||||
def test_lollipop_graph_mixing_input_types(self, m, n):
|
||||
expected = nx.compose(nx.complete_graph(4), nx.path_graph(range(100, 103)))
|
||||
expected.add_edge(0, 100) # Connect complete graph and path graph
|
||||
assert is_isomorphic(nx.lollipop_graph(m, n), expected)
|
||||
|
||||
def test_lollipop_graph_non_builtin_ints(self):
|
||||
np = pytest.importorskip("numpy")
|
||||
G = nx.lollipop_graph(np.int32(4), np.int64(3))
|
||||
expected = nx.compose(nx.complete_graph(4), nx.path_graph(range(100, 103)))
|
||||
expected.add_edge(0, 100) # Connect complete graph and path graph
|
||||
assert is_isomorphic(G, expected)
|
||||
|
||||
def test_null_graph(self):
|
||||
assert nx.number_of_nodes(nx.null_graph()) == 0
|
||||
|
||||
def test_path_graph(self):
|
||||
p = nx.path_graph(0)
|
||||
assert is_isomorphic(p, nx.null_graph())
|
||||
|
||||
p = nx.path_graph(1)
|
||||
assert is_isomorphic(p, nx.empty_graph(1))
|
||||
|
||||
p = nx.path_graph(10)
|
||||
assert nx.is_connected(p)
|
||||
assert sorted(d for n, d in p.degree()) == [1, 1, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
assert p.order() - 1 == p.size()
|
||||
|
||||
dp = nx.path_graph(3, create_using=nx.DiGraph)
|
||||
assert dp.has_edge(0, 1)
|
||||
assert not dp.has_edge(1, 0)
|
||||
|
||||
mp = nx.path_graph(10, create_using=nx.MultiGraph)
|
||||
assert edges_equal(mp.edges(), p.edges())
|
||||
|
||||
G = nx.path_graph("abc")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 2
|
||||
G = nx.path_graph("abcb")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 2
|
||||
g = nx.path_graph("abc", nx.DiGraph)
|
||||
assert len(g) == 3
|
||||
assert g.size() == 2
|
||||
assert g.is_directed()
|
||||
g = nx.path_graph("abcb", nx.DiGraph)
|
||||
assert len(g) == 3
|
||||
assert g.size() == 3
|
||||
|
||||
G = nx.path_graph((1, 2, 3, 2, 4))
|
||||
assert G.has_edge(2, 4)
|
||||
|
||||
def test_star_graph(self):
|
||||
assert is_isomorphic(nx.star_graph(""), nx.empty_graph(0))
|
||||
assert is_isomorphic(nx.star_graph([]), nx.empty_graph(0))
|
||||
assert is_isomorphic(nx.star_graph(0), nx.empty_graph(1))
|
||||
assert is_isomorphic(nx.star_graph(1), nx.path_graph(2))
|
||||
assert is_isomorphic(nx.star_graph(2), nx.path_graph(3))
|
||||
assert is_isomorphic(nx.star_graph(5), nx.complete_bipartite_graph(1, 5))
|
||||
|
||||
s = nx.star_graph(10)
|
||||
assert sorted(d for n, d in s.degree()) == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10]
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.star_graph, 10, create_using=nx.DiGraph)
|
||||
|
||||
ms = nx.star_graph(10, create_using=nx.MultiGraph)
|
||||
assert edges_equal(ms.edges(), s.edges())
|
||||
|
||||
G = nx.star_graph("abc")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 2
|
||||
|
||||
G = nx.star_graph("abcb")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 2
|
||||
G = nx.star_graph("abcb", create_using=nx.MultiGraph)
|
||||
assert len(G) == 3
|
||||
assert G.size() == 3
|
||||
|
||||
G = nx.star_graph("abcdefg")
|
||||
assert len(G) == 7
|
||||
assert G.size() == 6
|
||||
|
||||
def test_non_int_integers_for_star_graph(self):
|
||||
np = pytest.importorskip("numpy")
|
||||
G = nx.star_graph(np.int32(3))
|
||||
assert len(G) == 4
|
||||
assert G.size() == 3
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [(3, 0), (3, 5), (4, 10), (3, 20)])
|
||||
def test_tadpole_graph_right_sizes(self, m, n):
|
||||
G = nx.tadpole_graph(m, n)
|
||||
assert nx.number_of_nodes(G) == m + n
|
||||
assert nx.number_of_edges(G) == m + n - (m == 2)
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [("ab", ""), ("ab", "c"), ("abc", "defg")])
|
||||
def test_tadpole_graph_size_node_sequences(self, m, n):
|
||||
G = nx.tadpole_graph(m, n)
|
||||
assert nx.number_of_nodes(G) == len(m) + len(n)
|
||||
assert nx.number_of_edges(G) == len(m) + len(n) - (len(m) == 2)
|
||||
|
||||
def test_tadpole_graph_exceptions(self):
|
||||
# Raise NetworkXError if m<2
|
||||
pytest.raises(nx.NetworkXError, nx.tadpole_graph, -1, 3)
|
||||
pytest.raises(nx.NetworkXError, nx.tadpole_graph, 0, 3)
|
||||
pytest.raises(nx.NetworkXError, nx.tadpole_graph, 1, 3)
|
||||
|
||||
# Raise NetworkXError if n<0
|
||||
pytest.raises(nx.NetworkXError, nx.tadpole_graph, 5, -2)
|
||||
|
||||
# Raise NetworkXError for digraphs
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.tadpole_graph(2, 20, create_using=nx.DiGraph)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.tadpole_graph(2, 20, create_using=nx.MultiDiGraph)
|
||||
|
||||
@pytest.mark.parametrize(("m", "n"), [(2, 0), (2, 5), (2, 10), ("ab", 20)])
|
||||
def test_tadpole_graph_same_as_path_when_m_is_2(self, m, n):
|
||||
G = nx.tadpole_graph(m, n)
|
||||
assert is_isomorphic(G, nx.path_graph(n + 2))
|
||||
|
||||
@pytest.mark.parametrize("m", [4, 7])
|
||||
def test_tadpole_graph_same_as_cycle_when_m2_is_0(self, m):
|
||||
G = nx.tadpole_graph(m, 0)
|
||||
assert is_isomorphic(G, nx.cycle_graph(m))
|
||||
|
||||
def test_tadpole_graph_for_multigraph(self):
|
||||
G = nx.tadpole_graph(5, 20)
|
||||
MG = nx.tadpole_graph(5, 20, create_using=nx.MultiGraph)
|
||||
assert edges_equal(MG.edges(), G.edges())
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("m", "n"),
|
||||
[(4, "abc"), ("abcd", 3), ([1, 2, 3, 4], "abc"), ("abcd", [1, 2, 3])],
|
||||
)
|
||||
def test_tadpole_graph_mixing_input_types(self, m, n):
|
||||
expected = nx.compose(nx.cycle_graph(4), nx.path_graph(range(100, 103)))
|
||||
expected.add_edge(0, 100) # Connect cycle and path
|
||||
assert is_isomorphic(nx.tadpole_graph(m, n), expected)
|
||||
|
||||
def test_tadpole_graph_non_builtin_integers(self):
|
||||
np = pytest.importorskip("numpy")
|
||||
G = nx.tadpole_graph(np.int32(4), np.int64(3))
|
||||
expected = nx.compose(nx.cycle_graph(4), nx.path_graph(range(100, 103)))
|
||||
expected.add_edge(0, 100) # Connect cycle and path
|
||||
assert is_isomorphic(G, expected)
|
||||
|
||||
def test_trivial_graph(self):
|
||||
assert nx.number_of_nodes(nx.trivial_graph()) == 1
|
||||
|
||||
def test_turan_graph(self):
|
||||
assert nx.number_of_edges(nx.turan_graph(13, 4)) == 63
|
||||
assert is_isomorphic(
|
||||
nx.turan_graph(13, 4), nx.complete_multipartite_graph(3, 4, 3, 3)
|
||||
)
|
||||
|
||||
def test_wheel_graph(self):
|
||||
for n, G in [
|
||||
("", nx.null_graph()),
|
||||
(0, nx.null_graph()),
|
||||
(1, nx.empty_graph(1)),
|
||||
(2, nx.path_graph(2)),
|
||||
(3, nx.complete_graph(3)),
|
||||
(4, nx.complete_graph(4)),
|
||||
]:
|
||||
g = nx.wheel_graph(n)
|
||||
assert is_isomorphic(g, G)
|
||||
|
||||
g = nx.wheel_graph(10)
|
||||
assert sorted(d for n, d in g.degree()) == [3, 3, 3, 3, 3, 3, 3, 3, 3, 9]
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.wheel_graph, 10, create_using=nx.DiGraph)
|
||||
|
||||
mg = nx.wheel_graph(10, create_using=nx.MultiGraph())
|
||||
assert edges_equal(mg.edges(), g.edges())
|
||||
|
||||
G = nx.wheel_graph("abc")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 3
|
||||
|
||||
G = nx.wheel_graph("abcb")
|
||||
assert len(G) == 3
|
||||
assert G.size() == 4
|
||||
G = nx.wheel_graph("abcb", nx.MultiGraph)
|
||||
assert len(G) == 3
|
||||
assert G.size() == 6
|
||||
|
||||
def test_non_int_integers_for_wheel_graph(self):
|
||||
np = pytest.importorskip("numpy")
|
||||
G = nx.wheel_graph(np.int32(3))
|
||||
assert len(G) == 3
|
||||
assert G.size() == 3
|
||||
|
||||
def test_complete_0_partite_graph(self):
|
||||
"""Tests that the complete 0-partite graph is the null graph."""
|
||||
G = nx.complete_multipartite_graph()
|
||||
H = nx.null_graph()
|
||||
assert nodes_equal(G, H)
|
||||
assert edges_equal(G.edges(), H.edges())
|
||||
|
||||
def test_complete_1_partite_graph(self):
|
||||
"""Tests that the complete 1-partite graph is the empty graph."""
|
||||
G = nx.complete_multipartite_graph(3)
|
||||
H = nx.empty_graph(3)
|
||||
assert nodes_equal(G, H)
|
||||
assert edges_equal(G.edges(), H.edges())
|
||||
|
||||
def test_complete_2_partite_graph(self):
|
||||
"""Tests that the complete 2-partite graph is the complete bipartite
|
||||
graph.
|
||||
|
||||
"""
|
||||
G = nx.complete_multipartite_graph(2, 3)
|
||||
H = nx.complete_bipartite_graph(2, 3)
|
||||
assert nodes_equal(G, H)
|
||||
assert edges_equal(G.edges(), H.edges())
|
||||
|
||||
def test_complete_multipartite_graph(self):
|
||||
"""Tests for generating the complete multipartite graph."""
|
||||
G = nx.complete_multipartite_graph(2, 3, 4)
|
||||
blocks = [(0, 1), (2, 3, 4), (5, 6, 7, 8)]
|
||||
# Within each block, no two vertices should be adjacent.
|
||||
for block in blocks:
|
||||
for u, v in itertools.combinations_with_replacement(block, 2):
|
||||
assert v not in G[u]
|
||||
assert G.nodes[u] == G.nodes[v]
|
||||
# Across blocks, all vertices should be adjacent.
|
||||
for block1, block2 in itertools.combinations(blocks, 2):
|
||||
for u, v in itertools.product(block1, block2):
|
||||
assert v in G[u]
|
||||
assert G.nodes[u] != G.nodes[v]
|
||||
with pytest.raises(nx.NetworkXError, match="Negative number of nodes"):
|
||||
nx.complete_multipartite_graph(2, -3, 4)
|
||||
|
||||
def test_kneser_graph(self):
|
||||
# the petersen graph is a special case of the kneser graph when n=5 and k=2
|
||||
assert is_isomorphic(nx.kneser_graph(5, 2), nx.petersen_graph())
|
||||
|
||||
# when k is 1, the kneser graph returns a complete graph with n vertices
|
||||
for i in range(1, 7):
|
||||
assert is_isomorphic(nx.kneser_graph(i, 1), nx.complete_graph(i))
|
||||
|
||||
# the kneser graph of n and n-1 is the empty graph with n vertices
|
||||
for j in range(3, 7):
|
||||
assert is_isomorphic(nx.kneser_graph(j, j - 1), nx.empty_graph(j))
|
||||
|
||||
# in general the number of edges of the kneser graph is equal to
|
||||
# (n choose k) times (n-k choose k) divided by 2
|
||||
assert nx.number_of_edges(nx.kneser_graph(8, 3)) == 280
|
||||
20
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_cographs.py
vendored
Normal file
20
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_cographs.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
"""Unit tests for the :mod:`networkx.generators.cographs` module.
|
||||
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_random_cograph():
|
||||
n = 3
|
||||
G = nx.random_cograph(n)
|
||||
|
||||
assert len(G) == 2**n
|
||||
|
||||
# Every connected subgraph of G has diameter <= 2
|
||||
if nx.is_connected(G):
|
||||
assert nx.diameter(G) <= 2
|
||||
else:
|
||||
components = nx.connected_components(G)
|
||||
for component in components:
|
||||
assert nx.diameter(G.subgraph(component)) <= 2
|
||||
362
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_community.py
vendored
Normal file
362
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_community.py
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_random_partition_graph():
|
||||
G = nx.random_partition_graph([3, 3, 3], 1, 0, seed=42)
|
||||
C = G.graph["partition"]
|
||||
assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
|
||||
assert len(G) == 9
|
||||
assert len(list(G.edges())) == 9
|
||||
|
||||
G = nx.random_partition_graph([3, 3, 3], 0, 1)
|
||||
C = G.graph["partition"]
|
||||
assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
|
||||
assert len(G) == 9
|
||||
assert len(list(G.edges())) == 27
|
||||
|
||||
G = nx.random_partition_graph([3, 3, 3], 1, 0, directed=True)
|
||||
C = G.graph["partition"]
|
||||
assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
|
||||
assert len(G) == 9
|
||||
assert len(list(G.edges())) == 18
|
||||
|
||||
G = nx.random_partition_graph([3, 3, 3], 0, 1, directed=True)
|
||||
C = G.graph["partition"]
|
||||
assert C == [{0, 1, 2}, {3, 4, 5}, {6, 7, 8}]
|
||||
assert len(G) == 9
|
||||
assert len(list(G.edges())) == 54
|
||||
|
||||
G = nx.random_partition_graph([1, 2, 3, 4, 5], 0.5, 0.1)
|
||||
C = G.graph["partition"]
|
||||
assert C == [{0}, {1, 2}, {3, 4, 5}, {6, 7, 8, 9}, {10, 11, 12, 13, 14}]
|
||||
assert len(G) == 15
|
||||
|
||||
rpg = nx.random_partition_graph
|
||||
pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 1.1, 0.1)
|
||||
pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], -0.1, 0.1)
|
||||
pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 0.1, 1.1)
|
||||
pytest.raises(nx.NetworkXError, rpg, [1, 2, 3], 0.1, -0.1)
|
||||
|
||||
|
||||
def test_planted_partition_graph():
|
||||
G = nx.planted_partition_graph(4, 3, 1, 0, seed=42)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 4
|
||||
assert len(G) == 12
|
||||
assert len(list(G.edges())) == 12
|
||||
|
||||
G = nx.planted_partition_graph(4, 3, 0, 1)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 4
|
||||
assert len(G) == 12
|
||||
assert len(list(G.edges())) == 54
|
||||
|
||||
G = nx.planted_partition_graph(10, 4, 0.5, 0.1, seed=42)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 10
|
||||
assert len(G) == 40
|
||||
|
||||
G = nx.planted_partition_graph(4, 3, 1, 0, directed=True)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 4
|
||||
assert len(G) == 12
|
||||
assert len(list(G.edges())) == 24
|
||||
|
||||
G = nx.planted_partition_graph(4, 3, 0, 1, directed=True)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 4
|
||||
assert len(G) == 12
|
||||
assert len(list(G.edges())) == 108
|
||||
|
||||
G = nx.planted_partition_graph(10, 4, 0.5, 0.1, seed=42, directed=True)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 10
|
||||
assert len(G) == 40
|
||||
|
||||
ppg = nx.planted_partition_graph
|
||||
pytest.raises(nx.NetworkXError, ppg, 3, 3, 1.1, 0.1)
|
||||
pytest.raises(nx.NetworkXError, ppg, 3, 3, -0.1, 0.1)
|
||||
pytest.raises(nx.NetworkXError, ppg, 3, 3, 0.1, 1.1)
|
||||
pytest.raises(nx.NetworkXError, ppg, 3, 3, 0.1, -0.1)
|
||||
|
||||
|
||||
def test_relaxed_caveman_graph():
|
||||
G = nx.relaxed_caveman_graph(4, 3, 0)
|
||||
assert len(G) == 12
|
||||
G = nx.relaxed_caveman_graph(4, 3, 1)
|
||||
assert len(G) == 12
|
||||
G = nx.relaxed_caveman_graph(4, 3, 0.5)
|
||||
assert len(G) == 12
|
||||
G = nx.relaxed_caveman_graph(4, 3, 0.5, seed=42)
|
||||
assert len(G) == 12
|
||||
|
||||
|
||||
def test_connected_caveman_graph():
|
||||
G = nx.connected_caveman_graph(4, 3)
|
||||
assert len(G) == 12
|
||||
|
||||
G = nx.connected_caveman_graph(1, 5)
|
||||
K5 = nx.complete_graph(5)
|
||||
K5.remove_edge(3, 4)
|
||||
assert nx.is_isomorphic(G, K5)
|
||||
|
||||
# need at least 2 nodes in each clique
|
||||
pytest.raises(nx.NetworkXError, nx.connected_caveman_graph, 4, 1)
|
||||
|
||||
|
||||
def test_caveman_graph():
|
||||
G = nx.caveman_graph(4, 3)
|
||||
assert len(G) == 12
|
||||
|
||||
G = nx.caveman_graph(5, 1)
|
||||
E5 = nx.empty_graph(5)
|
||||
assert nx.is_isomorphic(G, E5)
|
||||
|
||||
G = nx.caveman_graph(1, 5)
|
||||
K5 = nx.complete_graph(5)
|
||||
assert nx.is_isomorphic(G, K5)
|
||||
|
||||
|
||||
def test_gaussian_random_partition_graph():
|
||||
G = nx.gaussian_random_partition_graph(100, 10, 10, 0.3, 0.01)
|
||||
assert len(G) == 100
|
||||
G = nx.gaussian_random_partition_graph(100, 10, 10, 0.3, 0.01, directed=True)
|
||||
assert len(G) == 100
|
||||
G = nx.gaussian_random_partition_graph(
|
||||
100, 10, 10, 0.3, 0.01, directed=False, seed=42
|
||||
)
|
||||
assert len(G) == 100
|
||||
assert not isinstance(G, nx.DiGraph)
|
||||
G = nx.gaussian_random_partition_graph(
|
||||
100, 10, 10, 0.3, 0.01, directed=True, seed=42
|
||||
)
|
||||
assert len(G) == 100
|
||||
assert isinstance(G, nx.DiGraph)
|
||||
pytest.raises(
|
||||
nx.NetworkXError, nx.gaussian_random_partition_graph, 100, 101, 10, 1, 0
|
||||
)
|
||||
# Test when clusters are likely less than 1
|
||||
G = nx.gaussian_random_partition_graph(10, 0.5, 0.5, 0.5, 0.5, seed=1)
|
||||
assert len(G) == 10
|
||||
|
||||
|
||||
def test_ring_of_cliques():
|
||||
for i in range(2, 20, 3):
|
||||
for j in range(2, 20, 3):
|
||||
G = nx.ring_of_cliques(i, j)
|
||||
assert G.number_of_nodes() == i * j
|
||||
if i != 2 or j != 1:
|
||||
expected_num_edges = i * (((j * (j - 1)) // 2) + 1)
|
||||
else:
|
||||
# the edge that already exists cannot be duplicated
|
||||
expected_num_edges = i * (((j * (j - 1)) // 2) + 1) - 1
|
||||
assert G.number_of_edges() == expected_num_edges
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="A ring of cliques must have at least two cliques"
|
||||
):
|
||||
nx.ring_of_cliques(1, 5)
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="The cliques must have at least two nodes"
|
||||
):
|
||||
nx.ring_of_cliques(3, 0)
|
||||
|
||||
|
||||
def test_windmill_graph():
|
||||
for n in range(2, 20, 3):
|
||||
for k in range(2, 20, 3):
|
||||
G = nx.windmill_graph(n, k)
|
||||
assert G.number_of_nodes() == (k - 1) * n + 1
|
||||
assert G.number_of_edges() == n * k * (k - 1) / 2
|
||||
assert G.degree(0) == G.number_of_nodes() - 1
|
||||
for i in range(1, G.number_of_nodes()):
|
||||
assert G.degree(i) == k - 1
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="A windmill graph must have at least two cliques"
|
||||
):
|
||||
nx.windmill_graph(1, 3)
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="The cliques must have at least two nodes"
|
||||
):
|
||||
nx.windmill_graph(3, 0)
|
||||
|
||||
|
||||
def test_stochastic_block_model():
|
||||
sizes = [75, 75, 300]
|
||||
probs = [[0.25, 0.05, 0.02], [0.05, 0.35, 0.07], [0.02, 0.07, 0.40]]
|
||||
G = nx.stochastic_block_model(sizes, probs, seed=0)
|
||||
C = G.graph["partition"]
|
||||
assert len(C) == 3
|
||||
assert len(G) == 450
|
||||
assert G.size() == 22160
|
||||
|
||||
GG = nx.stochastic_block_model(sizes, probs, range(450), seed=0)
|
||||
assert G.nodes == GG.nodes
|
||||
|
||||
# Test Exceptions
|
||||
sbm = nx.stochastic_block_model
|
||||
badnodelist = list(range(400)) # not enough nodes to match sizes
|
||||
badprobs1 = [[0.25, 0.05, 1.02], [0.05, 0.35, 0.07], [0.02, 0.07, 0.40]]
|
||||
badprobs2 = [[0.25, 0.05, 0.02], [0.05, -0.35, 0.07], [0.02, 0.07, 0.40]]
|
||||
probs_rect1 = [[0.25, 0.05, 0.02], [0.05, -0.35, 0.07]]
|
||||
probs_rect2 = [[0.25, 0.05], [0.05, -0.35], [0.02, 0.07]]
|
||||
asymprobs = [[0.25, 0.05, 0.01], [0.05, -0.35, 0.07], [0.02, 0.07, 0.40]]
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, badprobs1)
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, badprobs2)
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, probs_rect1, directed=True)
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, probs_rect2, directed=True)
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, asymprobs, directed=False)
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, probs, badnodelist)
|
||||
nodelist = [0] + list(range(449)) # repeated node name in nodelist
|
||||
pytest.raises(nx.NetworkXException, sbm, sizes, probs, nodelist)
|
||||
|
||||
# Extra keyword arguments test
|
||||
GG = nx.stochastic_block_model(sizes, probs, seed=0, selfloops=True)
|
||||
assert G.nodes == GG.nodes
|
||||
GG = nx.stochastic_block_model(sizes, probs, selfloops=True, directed=True)
|
||||
assert G.nodes == GG.nodes
|
||||
GG = nx.stochastic_block_model(sizes, probs, seed=0, sparse=False)
|
||||
assert G.nodes == GG.nodes
|
||||
|
||||
|
||||
def test_generator():
|
||||
n = 250
|
||||
tau1 = 3
|
||||
tau2 = 1.5
|
||||
mu = 0.1
|
||||
G = nx.LFR_benchmark_graph(
|
||||
n, tau1, tau2, mu, average_degree=5, min_community=20, seed=10
|
||||
)
|
||||
assert len(G) == 250
|
||||
C = {frozenset(G.nodes[v]["community"]) for v in G}
|
||||
assert nx.community.is_partition(G.nodes(), C)
|
||||
|
||||
|
||||
def test_invalid_tau1():
|
||||
with pytest.raises(nx.NetworkXError, match="tau2 must be greater than one"):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 1
|
||||
mu = 0.1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
|
||||
|
||||
|
||||
def test_invalid_tau2():
|
||||
with pytest.raises(nx.NetworkXError, match="tau1 must be greater than one"):
|
||||
n = 100
|
||||
tau1 = 1
|
||||
tau2 = 2
|
||||
mu = 0.1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
|
||||
|
||||
|
||||
def test_mu_too_large():
|
||||
with pytest.raises(nx.NetworkXError, match="mu must be in the interval \\[0, 1\\]"):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 1.1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
|
||||
|
||||
|
||||
def test_mu_too_small():
|
||||
with pytest.raises(nx.NetworkXError, match="mu must be in the interval \\[0, 1\\]"):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = -1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2)
|
||||
|
||||
|
||||
def test_both_degrees_none():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError,
|
||||
match="Must assign exactly one of min_degree and average_degree",
|
||||
):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu)
|
||||
|
||||
|
||||
def test_neither_degrees_none():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError,
|
||||
match="Must assign exactly one of min_degree and average_degree",
|
||||
):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, average_degree=5)
|
||||
|
||||
|
||||
def test_max_iters_exceeded():
|
||||
with pytest.raises(
|
||||
nx.ExceededMaxIterations,
|
||||
match="Could not assign communities; try increasing min_community",
|
||||
):
|
||||
n = 10
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 0.1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, max_iters=10, seed=1)
|
||||
|
||||
|
||||
def test_max_deg_out_of_range():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="max_degree must be in the interval \\(0, n\\]"
|
||||
):
|
||||
n = 10
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 0.1
|
||||
nx.LFR_benchmark_graph(
|
||||
n, tau1, tau2, mu, max_degree=n + 1, max_iters=10, seed=1
|
||||
)
|
||||
|
||||
|
||||
def test_max_community():
|
||||
n = 250
|
||||
tau1 = 3
|
||||
tau2 = 1.5
|
||||
mu = 0.1
|
||||
G = nx.LFR_benchmark_graph(
|
||||
n,
|
||||
tau1,
|
||||
tau2,
|
||||
mu,
|
||||
average_degree=5,
|
||||
max_degree=100,
|
||||
min_community=50,
|
||||
max_community=200,
|
||||
seed=10,
|
||||
)
|
||||
assert len(G) == 250
|
||||
C = {frozenset(G.nodes[v]["community"]) for v in G}
|
||||
assert nx.community.is_partition(G.nodes(), C)
|
||||
|
||||
|
||||
def test_powerlaw_iterations_exceeded():
|
||||
with pytest.raises(
|
||||
nx.ExceededMaxIterations, match="Could not create power law sequence"
|
||||
):
|
||||
n = 100
|
||||
tau1 = 2
|
||||
tau2 = 2
|
||||
mu = 1
|
||||
nx.LFR_benchmark_graph(n, tau1, tau2, mu, min_degree=2, max_iters=0)
|
||||
|
||||
|
||||
def test_no_scipy_zeta():
|
||||
zeta2 = 1.6449340668482264
|
||||
assert abs(zeta2 - nx.generators.community._hurwitz_zeta(2, 1, 0.0001)) < 0.01
|
||||
|
||||
|
||||
def test_generate_min_degree_itr():
|
||||
with pytest.raises(
|
||||
nx.ExceededMaxIterations, match="Could not match average_degree"
|
||||
):
|
||||
nx.generators.community._generate_min_degree(2, 2, 1, 0.01, 0)
|
||||
230
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_degree_seq.py
vendored
Normal file
230
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_degree_seq.py
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestConfigurationModel:
|
||||
"""Unit tests for the :func:`~networkx.configuration_model`
|
||||
function.
|
||||
|
||||
"""
|
||||
|
||||
def test_empty_degree_sequence(self):
|
||||
"""Tests that an empty degree sequence yields the null graph."""
|
||||
G = nx.configuration_model([])
|
||||
assert len(G) == 0
|
||||
|
||||
def test_degree_zero(self):
|
||||
"""Tests that a degree sequence of all zeros yields the empty
|
||||
graph.
|
||||
|
||||
"""
|
||||
G = nx.configuration_model([0, 0, 0])
|
||||
assert len(G) == 3
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
def test_degree_sequence(self):
|
||||
"""Tests that the degree sequence of the generated graph matches
|
||||
the input degree sequence.
|
||||
|
||||
"""
|
||||
deg_seq = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
|
||||
G = nx.configuration_model(deg_seq, seed=12345678)
|
||||
assert sorted((d for n, d in G.degree()), reverse=True) == [
|
||||
5,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
]
|
||||
assert sorted((d for n, d in G.degree(range(len(deg_seq)))), reverse=True) == [
|
||||
5,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
]
|
||||
|
||||
def test_random_seed(self):
|
||||
"""Tests that each call with the same random seed generates the
|
||||
same graph.
|
||||
|
||||
"""
|
||||
deg_seq = [3] * 12
|
||||
G1 = nx.configuration_model(deg_seq, seed=1000)
|
||||
G2 = nx.configuration_model(deg_seq, seed=1000)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
G1 = nx.configuration_model(deg_seq, seed=10)
|
||||
G2 = nx.configuration_model(deg_seq, seed=10)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
|
||||
def test_directed_disallowed(self):
|
||||
"""Tests that attempting to create a configuration model graph
|
||||
using a directed graph yields an exception.
|
||||
|
||||
"""
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
nx.configuration_model([], create_using=nx.DiGraph())
|
||||
|
||||
def test_odd_degree_sum(self):
|
||||
"""Tests that a degree sequence whose sum is odd yields an
|
||||
exception.
|
||||
|
||||
"""
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.configuration_model([1, 2])
|
||||
|
||||
|
||||
def test_directed_configuration_raise_unequal():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
zin = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1]
|
||||
zout = [5, 3, 3, 3, 3, 2, 2, 2, 1, 2]
|
||||
nx.directed_configuration_model(zin, zout)
|
||||
|
||||
|
||||
def test_directed_configuration_model():
|
||||
G = nx.directed_configuration_model([], [], seed=0)
|
||||
assert len(G) == 0
|
||||
|
||||
|
||||
def test_simple_directed_configuration_model():
|
||||
G = nx.directed_configuration_model([1, 1], [1, 1], seed=0)
|
||||
assert len(G) == 2
|
||||
|
||||
|
||||
def test_expected_degree_graph_empty():
|
||||
# empty graph has empty degree sequence
|
||||
deg_seq = []
|
||||
G = nx.expected_degree_graph(deg_seq)
|
||||
assert dict(G.degree()) == {}
|
||||
|
||||
|
||||
def test_expected_degree_graph():
|
||||
# test that fixed seed delivers the same graph
|
||||
deg_seq = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
|
||||
G1 = nx.expected_degree_graph(deg_seq, seed=1000)
|
||||
assert len(G1) == 12
|
||||
|
||||
G2 = nx.expected_degree_graph(deg_seq, seed=1000)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
|
||||
G1 = nx.expected_degree_graph(deg_seq, seed=10)
|
||||
G2 = nx.expected_degree_graph(deg_seq, seed=10)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
|
||||
|
||||
def test_expected_degree_graph_selfloops():
|
||||
deg_seq = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
|
||||
G1 = nx.expected_degree_graph(deg_seq, seed=1000, selfloops=False)
|
||||
G2 = nx.expected_degree_graph(deg_seq, seed=1000, selfloops=False)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
assert len(G1) == 12
|
||||
|
||||
|
||||
def test_expected_degree_graph_skew():
|
||||
deg_seq = [10, 2, 2, 2, 2]
|
||||
G1 = nx.expected_degree_graph(deg_seq, seed=1000)
|
||||
G2 = nx.expected_degree_graph(deg_seq, seed=1000)
|
||||
assert nx.is_isomorphic(G1, G2)
|
||||
assert len(G1) == 5
|
||||
|
||||
|
||||
def test_havel_hakimi_construction():
|
||||
G = nx.havel_hakimi_graph([])
|
||||
assert len(G) == 0
|
||||
|
||||
z = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
|
||||
pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
|
||||
z = ["A", 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
|
||||
pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
|
||||
|
||||
z = [5, 4, 3, 3, 3, 2, 2, 2]
|
||||
G = nx.havel_hakimi_graph(z)
|
||||
G = nx.configuration_model(z)
|
||||
z = [6, 5, 4, 4, 2, 1, 1, 1]
|
||||
pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z)
|
||||
|
||||
z = [10, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2]
|
||||
|
||||
G = nx.havel_hakimi_graph(z)
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.havel_hakimi_graph, z, create_using=nx.DiGraph())
|
||||
|
||||
|
||||
def test_directed_havel_hakimi():
|
||||
# Test range of valid directed degree sequences
|
||||
n, r = 100, 10
|
||||
p = 1.0 / r
|
||||
for i in range(r):
|
||||
G1 = nx.erdos_renyi_graph(n, p * (i + 1), None, True)
|
||||
din1 = [d for n, d in G1.in_degree()]
|
||||
dout1 = [d for n, d in G1.out_degree()]
|
||||
G2 = nx.directed_havel_hakimi_graph(din1, dout1)
|
||||
din2 = [d for n, d in G2.in_degree()]
|
||||
dout2 = [d for n, d in G2.out_degree()]
|
||||
assert sorted(din1) == sorted(din2)
|
||||
assert sorted(dout1) == sorted(dout2)
|
||||
|
||||
# Test non-graphical sequence
|
||||
dout = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
|
||||
din = [103, 102, 102, 102, 102, 102, 102, 102, 102, 102]
|
||||
pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
|
||||
# Test valid sequences
|
||||
dout = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
|
||||
din = [2, 2, 2, 2, 2, 2, 2, 2, 0, 2]
|
||||
G2 = nx.directed_havel_hakimi_graph(din, dout)
|
||||
dout2 = (d for n, d in G2.out_degree())
|
||||
din2 = (d for n, d in G2.in_degree())
|
||||
assert sorted(dout) == sorted(dout2)
|
||||
assert sorted(din) == sorted(din2)
|
||||
# Test unequal sums
|
||||
din = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
|
||||
# Test for negative values
|
||||
din = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -2]
|
||||
pytest.raises(nx.exception.NetworkXError, nx.directed_havel_hakimi_graph, din, dout)
|
||||
|
||||
|
||||
def test_degree_sequence_tree():
|
||||
z = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
|
||||
G = nx.degree_sequence_tree(z)
|
||||
assert len(G) == len(z)
|
||||
assert len(list(G.edges())) == sum(z) / 2
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError, nx.degree_sequence_tree, z, create_using=nx.DiGraph()
|
||||
)
|
||||
|
||||
z = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
|
||||
pytest.raises(nx.NetworkXError, nx.degree_sequence_tree, z)
|
||||
|
||||
|
||||
def test_random_degree_sequence_graph():
|
||||
d = [1, 2, 2, 3]
|
||||
G = nx.random_degree_sequence_graph(d, seed=42)
|
||||
assert d == sorted(d for n, d in G.degree())
|
||||
|
||||
|
||||
def test_random_degree_sequence_graph_raise():
|
||||
z = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.random_degree_sequence_graph, z)
|
||||
|
||||
|
||||
def test_random_degree_sequence_large():
|
||||
G1 = nx.fast_gnp_random_graph(100, 0.1, seed=42)
|
||||
d1 = (d for n, d in G1.degree())
|
||||
G2 = nx.random_degree_sequence_graph(d1, seed=42)
|
||||
d2 = (d for n, d in G2.degree())
|
||||
assert sorted(d1) == sorted(d2)
|
||||
162
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_directed.py
vendored
Normal file
162
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_directed.py
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
"""Generators - Directed Graphs
|
||||
----------------------------
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.classes import Graph, MultiDiGraph
|
||||
from networkx.generators.directed import (
|
||||
gn_graph,
|
||||
gnc_graph,
|
||||
gnr_graph,
|
||||
random_k_out_graph,
|
||||
random_uniform_k_out_graph,
|
||||
scale_free_graph,
|
||||
)
|
||||
|
||||
|
||||
class TestGeneratorsDirected:
|
||||
def test_smoke_test_random_graphs(self):
|
||||
gn_graph(100)
|
||||
gnr_graph(100, 0.5)
|
||||
gnc_graph(100)
|
||||
scale_free_graph(100)
|
||||
|
||||
gn_graph(100, seed=42)
|
||||
gnr_graph(100, 0.5, seed=42)
|
||||
gnc_graph(100, seed=42)
|
||||
scale_free_graph(100, seed=42)
|
||||
|
||||
def test_create_using_keyword_arguments(self):
|
||||
pytest.raises(nx.NetworkXError, gn_graph, 100, create_using=Graph())
|
||||
pytest.raises(nx.NetworkXError, gnr_graph, 100, 0.5, create_using=Graph())
|
||||
pytest.raises(nx.NetworkXError, gnc_graph, 100, create_using=Graph())
|
||||
G = gn_graph(100, seed=1)
|
||||
MG = gn_graph(100, create_using=MultiDiGraph(), seed=1)
|
||||
assert sorted(G.edges()) == sorted(MG.edges())
|
||||
G = gnr_graph(100, 0.5, seed=1)
|
||||
MG = gnr_graph(100, 0.5, create_using=MultiDiGraph(), seed=1)
|
||||
assert sorted(G.edges()) == sorted(MG.edges())
|
||||
G = gnc_graph(100, seed=1)
|
||||
MG = gnc_graph(100, create_using=MultiDiGraph(), seed=1)
|
||||
assert sorted(G.edges()) == sorted(MG.edges())
|
||||
|
||||
G = scale_free_graph(
|
||||
100,
|
||||
alpha=0.3,
|
||||
beta=0.4,
|
||||
gamma=0.3,
|
||||
delta_in=0.3,
|
||||
delta_out=0.1,
|
||||
initial_graph=nx.cycle_graph(4, create_using=MultiDiGraph),
|
||||
seed=1,
|
||||
)
|
||||
pytest.raises(ValueError, scale_free_graph, 100, 0.5, 0.4, 0.3)
|
||||
pytest.raises(ValueError, scale_free_graph, 100, alpha=-0.3)
|
||||
pytest.raises(ValueError, scale_free_graph, 100, beta=-0.3)
|
||||
pytest.raises(ValueError, scale_free_graph, 100, gamma=-0.3)
|
||||
|
||||
def test_parameters(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0)
|
||||
|
||||
def kernel(x):
|
||||
return x
|
||||
|
||||
assert nx.is_isomorphic(gn_graph(1), G)
|
||||
assert nx.is_isomorphic(gn_graph(1, kernel=kernel), G)
|
||||
assert nx.is_isomorphic(gnc_graph(1), G)
|
||||
assert nx.is_isomorphic(gnr_graph(1, 0.5), G)
|
||||
|
||||
|
||||
def test_scale_free_graph_negative_delta():
|
||||
with pytest.raises(ValueError, match="delta_in must be >= 0."):
|
||||
scale_free_graph(10, delta_in=-1)
|
||||
with pytest.raises(ValueError, match="delta_out must be >= 0."):
|
||||
scale_free_graph(10, delta_out=-1)
|
||||
|
||||
|
||||
def test_non_numeric_ordering():
|
||||
G = MultiDiGraph([("a", "b"), ("b", "c"), ("c", "a")])
|
||||
s = scale_free_graph(3, initial_graph=G)
|
||||
assert len(s) == 3
|
||||
assert len(s.edges) == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ig", (nx.Graph(), nx.DiGraph([(0, 1)])))
|
||||
def test_scale_free_graph_initial_graph_kwarg(ig):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
scale_free_graph(100, initial_graph=ig)
|
||||
|
||||
|
||||
class TestRandomKOutGraph:
|
||||
"""Unit tests for the
|
||||
:func:`~networkx.generators.directed.random_k_out_graph` function.
|
||||
|
||||
"""
|
||||
|
||||
def test_regularity(self):
|
||||
"""Tests that the generated graph is `k`-out-regular."""
|
||||
n = 10
|
||||
k = 3
|
||||
alpha = 1
|
||||
G = random_k_out_graph(n, k, alpha)
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
G = random_k_out_graph(n, k, alpha, seed=42)
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
|
||||
def test_no_self_loops(self):
|
||||
"""Tests for forbidding self-loops."""
|
||||
n = 10
|
||||
k = 3
|
||||
alpha = 1
|
||||
G = random_k_out_graph(n, k, alpha, self_loops=False)
|
||||
assert nx.number_of_selfloops(G) == 0
|
||||
|
||||
def test_negative_alpha(self):
|
||||
with pytest.raises(ValueError, match="alpha must be positive"):
|
||||
random_k_out_graph(10, 3, -1)
|
||||
|
||||
|
||||
class TestUniformRandomKOutGraph:
|
||||
"""Unit tests for the
|
||||
:func:`~networkx.generators.directed.random_uniform_k_out_graph`
|
||||
function.
|
||||
|
||||
"""
|
||||
|
||||
def test_regularity(self):
|
||||
"""Tests that the generated graph is `k`-out-regular."""
|
||||
n = 10
|
||||
k = 3
|
||||
G = random_uniform_k_out_graph(n, k)
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
G = random_uniform_k_out_graph(n, k, seed=42)
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
|
||||
def test_no_self_loops(self):
|
||||
"""Tests for forbidding self-loops."""
|
||||
n = 10
|
||||
k = 3
|
||||
G = random_uniform_k_out_graph(n, k, self_loops=False)
|
||||
assert nx.number_of_selfloops(G) == 0
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
|
||||
def test_with_replacement(self):
|
||||
n = 10
|
||||
k = 3
|
||||
G = random_uniform_k_out_graph(n, k, with_replacement=True)
|
||||
assert G.is_multigraph()
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
n = 10
|
||||
k = 9
|
||||
G = random_uniform_k_out_graph(n, k, with_replacement=False, self_loops=False)
|
||||
assert nx.number_of_selfloops(G) == 0
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
|
||||
def test_without_replacement(self):
|
||||
n = 10
|
||||
k = 3
|
||||
G = random_uniform_k_out_graph(n, k, with_replacement=False)
|
||||
assert not G.is_multigraph()
|
||||
assert all(d == k for v, d in G.out_degree())
|
||||
84
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_duplication.py
vendored
Normal file
84
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_duplication.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Unit tests for the :mod:`networkx.generators.duplication` module.
|
||||
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.generators.duplication import (
|
||||
duplication_divergence_graph,
|
||||
partial_duplication_graph,
|
||||
)
|
||||
|
||||
|
||||
class TestDuplicationDivergenceGraph:
|
||||
"""Unit tests for the
|
||||
:func:`networkx.generators.duplication.duplication_divergence_graph`
|
||||
function.
|
||||
|
||||
"""
|
||||
|
||||
def test_final_size(self):
|
||||
G = duplication_divergence_graph(3, p=1)
|
||||
assert len(G) == 3
|
||||
G = duplication_divergence_graph(3, p=1, seed=42)
|
||||
assert len(G) == 3
|
||||
|
||||
def test_probability_too_large(self):
|
||||
with pytest.raises(NetworkXError):
|
||||
duplication_divergence_graph(3, p=2)
|
||||
|
||||
def test_probability_too_small(self):
|
||||
with pytest.raises(NetworkXError):
|
||||
duplication_divergence_graph(3, p=-1)
|
||||
|
||||
def test_non_extreme_probability_value(self):
|
||||
G = duplication_divergence_graph(6, p=0.3, seed=42)
|
||||
assert len(G) == 6
|
||||
assert list(G.degree()) == [(0, 2), (1, 3), (2, 2), (3, 3), (4, 1), (5, 1)]
|
||||
|
||||
def test_minimum_desired_nodes(self):
|
||||
with pytest.raises(
|
||||
NetworkXError, match=".*n must be greater than or equal to 2"
|
||||
):
|
||||
duplication_divergence_graph(1, p=1)
|
||||
|
||||
|
||||
class TestPartialDuplicationGraph:
|
||||
"""Unit tests for the
|
||||
:func:`networkx.generators.duplication.partial_duplication_graph`
|
||||
function.
|
||||
|
||||
"""
|
||||
|
||||
def test_final_size(self):
|
||||
N = 10
|
||||
n = 5
|
||||
p = 0.5
|
||||
q = 0.5
|
||||
G = partial_duplication_graph(N, n, p, q)
|
||||
assert len(G) == N
|
||||
G = partial_duplication_graph(N, n, p, q, seed=42)
|
||||
assert len(G) == N
|
||||
|
||||
def test_initial_clique_size(self):
|
||||
N = 10
|
||||
n = 10
|
||||
p = 0.5
|
||||
q = 0.5
|
||||
G = partial_duplication_graph(N, n, p, q)
|
||||
assert len(G) == n
|
||||
|
||||
def test_invalid_initial_size(self):
|
||||
with pytest.raises(NetworkXError):
|
||||
N = 5
|
||||
n = 10
|
||||
p = 0.5
|
||||
q = 0.5
|
||||
G = partial_duplication_graph(N, n, p, q)
|
||||
|
||||
def test_invalid_probabilities(self):
|
||||
N = 1
|
||||
n = 1
|
||||
for p, q in [(0.5, 2), (0.5, -1), (2, 0.5), (-1, 0.5)]:
|
||||
args = (N, n, p, q)
|
||||
pytest.raises(NetworkXError, partial_duplication_graph, *args)
|
||||
39
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_ego.py
vendored
Normal file
39
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_ego.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
ego graph
|
||||
---------
|
||||
"""
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import edges_equal, nodes_equal
|
||||
|
||||
|
||||
class TestGeneratorEgo:
|
||||
def test_ego(self):
|
||||
G = nx.star_graph(3)
|
||||
H = nx.ego_graph(G, 0)
|
||||
assert nx.is_isomorphic(G, H)
|
||||
G.add_edge(1, 11)
|
||||
G.add_edge(2, 22)
|
||||
G.add_edge(3, 33)
|
||||
H = nx.ego_graph(G, 0)
|
||||
assert nx.is_isomorphic(nx.star_graph(3), H)
|
||||
G = nx.path_graph(3)
|
||||
H = nx.ego_graph(G, 0)
|
||||
assert edges_equal(H.edges(), [(0, 1)])
|
||||
H = nx.ego_graph(G, 0, undirected=True)
|
||||
assert edges_equal(H.edges(), [(0, 1)])
|
||||
H = nx.ego_graph(G, 0, center=False)
|
||||
assert edges_equal(H.edges(), [])
|
||||
|
||||
def test_ego_distance(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(0, 1, weight=2, distance=1)
|
||||
G.add_edge(1, 2, weight=2, distance=2)
|
||||
G.add_edge(2, 3, weight=2, distance=1)
|
||||
assert nodes_equal(nx.ego_graph(G, 0, radius=3).nodes(), [0, 1, 2, 3])
|
||||
eg = nx.ego_graph(G, 0, radius=3, distance="weight")
|
||||
assert nodes_equal(eg.nodes(), [0, 1])
|
||||
eg = nx.ego_graph(G, 0, radius=3, distance="weight", undirected=True)
|
||||
assert nodes_equal(eg.nodes(), [0, 1])
|
||||
eg = nx.ego_graph(G, 0, radius=3, distance="distance")
|
||||
assert nodes_equal(eg.nodes(), [0, 1, 2])
|
||||
164
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_expanders.py
vendored
Normal file
164
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_expanders.py
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
"""Unit tests for the :mod:`networkx.generators.expanders` module.
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", (2, 3, 5, 6, 10))
|
||||
def test_margulis_gabber_galil_graph_properties(n):
|
||||
g = nx.margulis_gabber_galil_graph(n)
|
||||
assert g.number_of_nodes() == n * n
|
||||
for node in g:
|
||||
assert g.degree(node) == 8
|
||||
assert len(node) == 2
|
||||
for i in node:
|
||||
assert int(i) == i
|
||||
assert 0 <= i < n
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", (2, 3, 5, 6, 10))
|
||||
def test_margulis_gabber_galil_graph_eigvals(n):
|
||||
np = pytest.importorskip("numpy")
|
||||
sp = pytest.importorskip("scipy")
|
||||
|
||||
g = nx.margulis_gabber_galil_graph(n)
|
||||
# Eigenvalues are already sorted using the scipy eigvalsh,
|
||||
# but the implementation in numpy does not guarantee order.
|
||||
w = sorted(sp.linalg.eigvalsh(nx.adjacency_matrix(g).toarray()))
|
||||
assert w[-2] < 5 * np.sqrt(2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("p", (3, 5, 7, 11)) # Primes
|
||||
def test_chordal_cycle_graph(p):
|
||||
"""Test for the :func:`networkx.chordal_cycle_graph` function."""
|
||||
G = nx.chordal_cycle_graph(p)
|
||||
assert len(G) == p
|
||||
# TODO The second largest eigenvalue should be smaller than a constant,
|
||||
# independent of the number of nodes in the graph:
|
||||
#
|
||||
# eigs = sorted(sp.linalg.eigvalsh(nx.adjacency_matrix(G).toarray()))
|
||||
# assert_less(eigs[-2], ...)
|
||||
#
|
||||
|
||||
|
||||
@pytest.mark.parametrize("p", (3, 5, 7, 11, 13)) # Primes
|
||||
def test_paley_graph(p):
|
||||
"""Test for the :func:`networkx.paley_graph` function."""
|
||||
G = nx.paley_graph(p)
|
||||
# G has p nodes
|
||||
assert len(G) == p
|
||||
# G is (p-1)/2-regular
|
||||
in_degrees = {G.in_degree(node) for node in G.nodes}
|
||||
out_degrees = {G.out_degree(node) for node in G.nodes}
|
||||
assert len(in_degrees) == 1 and in_degrees.pop() == (p - 1) // 2
|
||||
assert len(out_degrees) == 1 and out_degrees.pop() == (p - 1) // 2
|
||||
|
||||
# If p = 1 mod 4, -1 is a square mod 4 and therefore the
|
||||
# edge in the Paley graph are symmetric.
|
||||
if p % 4 == 1:
|
||||
for u, v in G.edges:
|
||||
assert (v, u) in G.edges
|
||||
|
||||
|
||||
@pytest.mark.parametrize("d, n", [(2, 7), (4, 10), (4, 16)])
|
||||
def test_maybe_regular_expander(d, n):
|
||||
pytest.importorskip("numpy")
|
||||
G = nx.maybe_regular_expander(n, d)
|
||||
|
||||
assert len(G) == n, "Should have n nodes"
|
||||
assert len(G.edges) == n * d / 2, "Should have n*d/2 edges"
|
||||
assert nx.is_k_regular(G, d), "Should be d-regular"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", (3, 5, 6, 10))
|
||||
def test_is_regular_expander(n):
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
G = nx.complete_graph(n)
|
||||
|
||||
assert nx.is_regular_expander(G) == True, "Should be a regular expander"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("d, n", [(2, 7), (4, 10), (4, 16)])
|
||||
def test_random_regular_expander(d, n):
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
G = nx.random_regular_expander_graph(n, d)
|
||||
|
||||
assert len(G) == n, "Should have n nodes"
|
||||
assert len(G.edges) == n * d / 2, "Should have n*d/2 edges"
|
||||
assert nx.is_k_regular(G, d), "Should be d-regular"
|
||||
assert nx.is_regular_expander(G) == True, "Should be a regular expander"
|
||||
|
||||
|
||||
def test_random_regular_expander_explicit_construction():
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
G = nx.random_regular_expander_graph(d=4, n=5)
|
||||
|
||||
assert len(G) == 5 and len(G.edges) == 10, "Should be a complete graph"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
|
||||
def test_margulis_gabber_galil_graph_badinput(graph_type):
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="`create_using` must be an undirected multigraph"
|
||||
):
|
||||
nx.margulis_gabber_galil_graph(3, create_using=graph_type)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph, nx.MultiDiGraph))
|
||||
def test_chordal_cycle_graph_badinput(graph_type):
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="`create_using` must be an undirected multigraph"
|
||||
):
|
||||
nx.chordal_cycle_graph(3, create_using=graph_type)
|
||||
|
||||
|
||||
def test_paley_graph_badinput():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="`create_using` cannot be a multigraph."
|
||||
):
|
||||
nx.paley_graph(3, create_using=nx.MultiGraph)
|
||||
|
||||
|
||||
def test_maybe_regular_expander_badinput():
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="n must be a positive integer"):
|
||||
nx.maybe_regular_expander(n=-1, d=2)
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="d must be greater than or equal to 2"):
|
||||
nx.maybe_regular_expander(n=10, d=0)
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="Need n-1>= d to have room"):
|
||||
nx.maybe_regular_expander(n=5, d=6)
|
||||
|
||||
|
||||
def test_is_regular_expander_badinput():
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="epsilon must be non negative"):
|
||||
nx.is_regular_expander(nx.Graph(), epsilon=-1)
|
||||
|
||||
|
||||
def test_random_regular_expander_badinput():
|
||||
pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="n must be a positive integer"):
|
||||
nx.random_regular_expander_graph(n=-1, d=2)
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="d must be greater than or equal to 2"):
|
||||
nx.random_regular_expander_graph(n=10, d=0)
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="Need n-1>= d to have room"):
|
||||
nx.random_regular_expander_graph(n=5, d=6)
|
||||
|
||||
with pytest.raises(nx.NetworkXError, match="epsilon must be non negative"):
|
||||
nx.random_regular_expander_graph(n=4, d=2, epsilon=-1)
|
||||
488
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_geometric.py
vendored
Normal file
488
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_geometric.py
vendored
Normal file
@@ -0,0 +1,488 @@
|
||||
import math
|
||||
import random
|
||||
from itertools import combinations
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def l1dist(x, y):
|
||||
return sum(abs(a - b) for a, b in zip(x, y))
|
||||
|
||||
|
||||
class TestRandomGeometricGraph:
|
||||
"""Unit tests for :func:`~networkx.random_geometric_graph`"""
|
||||
|
||||
def test_number_of_nodes(self):
|
||||
G = nx.random_geometric_graph(50, 0.25, seed=42)
|
||||
assert len(G) == 50
|
||||
G = nx.random_geometric_graph(range(50), 0.25, seed=42)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_distances(self):
|
||||
"""Tests that pairs of vertices adjacent if and only if they are
|
||||
within the prescribed radius.
|
||||
"""
|
||||
# Use the Euclidean metric, the default according to the
|
||||
# documentation.
|
||||
G = nx.random_geometric_graph(50, 0.25)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
# Nonadjacent vertices must be at greater distance.
|
||||
else:
|
||||
assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_p(self):
|
||||
"""Tests for providing an alternate distance metric to the generator."""
|
||||
# Use the L1 metric.
|
||||
G = nx.random_geometric_graph(50, 0.25, p=1)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
# Nonadjacent vertices must be at greater distance.
|
||||
else:
|
||||
assert not l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_node_names(self):
|
||||
"""Tests using values other than sequential numbers as node IDs."""
|
||||
import string
|
||||
|
||||
nodes = list(string.ascii_lowercase)
|
||||
G = nx.random_geometric_graph(nodes, 0.25)
|
||||
assert len(G) == len(nodes)
|
||||
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
# Nonadjacent vertices must be at greater distance.
|
||||
else:
|
||||
assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_pos_name(self):
|
||||
G = nx.random_geometric_graph(50, 0.25, seed=42, pos_name="coords")
|
||||
assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
|
||||
|
||||
|
||||
class TestSoftRandomGeometricGraph:
|
||||
"""Unit tests for :func:`~networkx.soft_random_geometric_graph`"""
|
||||
|
||||
def test_number_of_nodes(self):
|
||||
G = nx.soft_random_geometric_graph(50, 0.25, seed=42)
|
||||
assert len(G) == 50
|
||||
G = nx.soft_random_geometric_graph(range(50), 0.25, seed=42)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_distances(self):
|
||||
"""Tests that pairs of vertices adjacent if and only if they are
|
||||
within the prescribed radius.
|
||||
"""
|
||||
# Use the Euclidean metric, the default according to the
|
||||
# documentation.
|
||||
G = nx.soft_random_geometric_graph(50, 0.25)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_p(self):
|
||||
"""Tests for providing an alternate distance metric to the generator."""
|
||||
|
||||
# Use the L1 metric.
|
||||
def dist(x, y):
|
||||
return sum(abs(a - b) for a, b in zip(x, y))
|
||||
|
||||
G = nx.soft_random_geometric_graph(50, 0.25, p=1)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_node_names(self):
|
||||
"""Tests using values other than sequential numbers as node IDs."""
|
||||
import string
|
||||
|
||||
nodes = list(string.ascii_lowercase)
|
||||
G = nx.soft_random_geometric_graph(nodes, 0.25)
|
||||
assert len(G) == len(nodes)
|
||||
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_p_dist_default(self):
|
||||
"""Tests default p_dict = 0.5 returns graph with edge count <= RGG with
|
||||
same n, radius, dim and positions
|
||||
"""
|
||||
nodes = 50
|
||||
dim = 2
|
||||
pos = {v: [random.random() for i in range(dim)] for v in range(nodes)}
|
||||
RGG = nx.random_geometric_graph(50, 0.25, pos=pos)
|
||||
SRGG = nx.soft_random_geometric_graph(50, 0.25, pos=pos)
|
||||
assert len(SRGG.edges()) <= len(RGG.edges())
|
||||
|
||||
def test_p_dist_zero(self):
|
||||
"""Tests if p_dict = 0 returns disconnected graph with 0 edges"""
|
||||
|
||||
def p_dist(dist):
|
||||
return 0
|
||||
|
||||
G = nx.soft_random_geometric_graph(50, 0.25, p_dist=p_dist)
|
||||
assert len(G.edges) == 0
|
||||
|
||||
def test_pos_name(self):
|
||||
G = nx.soft_random_geometric_graph(50, 0.25, seed=42, pos_name="coords")
|
||||
assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
|
||||
|
||||
|
||||
def join(G, u, v, theta, alpha, metric):
|
||||
"""Returns ``True`` if and only if the nodes whose attributes are
|
||||
``du`` and ``dv`` should be joined, according to the threshold
|
||||
condition for geographical threshold graphs.
|
||||
|
||||
``G`` is an undirected NetworkX graph, and ``u`` and ``v`` are nodes
|
||||
in that graph. The nodes must have node attributes ``'pos'`` and
|
||||
``'weight'``.
|
||||
|
||||
``metric`` is a distance metric.
|
||||
"""
|
||||
du, dv = G.nodes[u], G.nodes[v]
|
||||
u_pos, v_pos = du["pos"], dv["pos"]
|
||||
u_weight, v_weight = du["weight"], dv["weight"]
|
||||
return (u_weight + v_weight) * metric(u_pos, v_pos) ** alpha >= theta
|
||||
|
||||
|
||||
class TestGeographicalThresholdGraph:
|
||||
"""Unit tests for :func:`~networkx.geographical_threshold_graph`"""
|
||||
|
||||
def test_number_of_nodes(self):
|
||||
G = nx.geographical_threshold_graph(50, 100, seed=42)
|
||||
assert len(G) == 50
|
||||
G = nx.geographical_threshold_graph(range(50), 100, seed=42)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_distances(self):
|
||||
"""Tests that pairs of vertices adjacent if and only if their
|
||||
distances meet the given threshold.
|
||||
"""
|
||||
# Use the Euclidean metric and alpha = -2
|
||||
# the default according to the documentation.
|
||||
G = nx.geographical_threshold_graph(50, 10)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must exceed the threshold.
|
||||
if v in G[u]:
|
||||
assert join(G, u, v, 10, -2, math.dist)
|
||||
# Nonadjacent vertices must not exceed the threshold.
|
||||
else:
|
||||
assert not join(G, u, v, 10, -2, math.dist)
|
||||
|
||||
def test_metric(self):
|
||||
"""Tests for providing an alternate distance metric to the generator."""
|
||||
# Use the L1 metric.
|
||||
G = nx.geographical_threshold_graph(50, 10, metric=l1dist)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must exceed the threshold.
|
||||
if v in G[u]:
|
||||
assert join(G, u, v, 10, -2, l1dist)
|
||||
# Nonadjacent vertices must not exceed the threshold.
|
||||
else:
|
||||
assert not join(G, u, v, 10, -2, l1dist)
|
||||
|
||||
def test_p_dist_zero(self):
|
||||
"""Tests if p_dict = 0 returns disconnected graph with 0 edges"""
|
||||
|
||||
def p_dist(dist):
|
||||
return 0
|
||||
|
||||
G = nx.geographical_threshold_graph(50, 1, p_dist=p_dist)
|
||||
assert len(G.edges) == 0
|
||||
|
||||
def test_pos_weight_name(self):
|
||||
gtg = nx.geographical_threshold_graph
|
||||
G = gtg(50, 100, seed=42, pos_name="coords", weight_name="wt")
|
||||
assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
|
||||
assert all(d["wt"] > 0 for n, d in G.nodes.items())
|
||||
|
||||
|
||||
class TestWaxmanGraph:
|
||||
"""Unit tests for the :func:`~networkx.waxman_graph` function."""
|
||||
|
||||
def test_number_of_nodes_1(self):
|
||||
G = nx.waxman_graph(50, 0.5, 0.1, seed=42)
|
||||
assert len(G) == 50
|
||||
G = nx.waxman_graph(range(50), 0.5, 0.1, seed=42)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_number_of_nodes_2(self):
|
||||
G = nx.waxman_graph(50, 0.5, 0.1, L=1)
|
||||
assert len(G) == 50
|
||||
G = nx.waxman_graph(range(50), 0.5, 0.1, L=1)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_metric(self):
|
||||
"""Tests for providing an alternate distance metric to the generator."""
|
||||
# Use the L1 metric.
|
||||
G = nx.waxman_graph(50, 0.5, 0.1, metric=l1dist)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_pos_name(self):
|
||||
G = nx.waxman_graph(50, 0.5, 0.1, seed=42, pos_name="coords")
|
||||
assert all(len(d["coords"]) == 2 for n, d in G.nodes.items())
|
||||
|
||||
|
||||
class TestNavigableSmallWorldGraph:
|
||||
def test_navigable_small_world(self):
|
||||
G = nx.navigable_small_world_graph(5, p=1, q=0, seed=42)
|
||||
gg = nx.grid_2d_graph(5, 5).to_directed()
|
||||
assert nx.is_isomorphic(G, gg)
|
||||
|
||||
G = nx.navigable_small_world_graph(5, p=1, q=0, dim=3)
|
||||
gg = nx.grid_graph([5, 5, 5]).to_directed()
|
||||
assert nx.is_isomorphic(G, gg)
|
||||
|
||||
G = nx.navigable_small_world_graph(5, p=1, q=0, dim=1)
|
||||
gg = nx.grid_graph([5]).to_directed()
|
||||
assert nx.is_isomorphic(G, gg)
|
||||
|
||||
def test_invalid_diameter_value(self):
|
||||
with pytest.raises(nx.NetworkXException, match=".*p must be >= 1"):
|
||||
nx.navigable_small_world_graph(5, p=0, q=0, dim=1)
|
||||
|
||||
def test_invalid_long_range_connections_value(self):
|
||||
with pytest.raises(nx.NetworkXException, match=".*q must be >= 0"):
|
||||
nx.navigable_small_world_graph(5, p=1, q=-1, dim=1)
|
||||
|
||||
def test_invalid_exponent_for_decaying_probability_value(self):
|
||||
with pytest.raises(nx.NetworkXException, match=".*r must be >= 0"):
|
||||
nx.navigable_small_world_graph(5, p=1, q=0, r=-1, dim=1)
|
||||
|
||||
def test_r_between_0_and_1(self):
|
||||
"""Smoke test for radius in range [0, 1]"""
|
||||
# q=0 means no long-range connections
|
||||
G = nx.navigable_small_world_graph(3, p=1, q=0, r=0.5, dim=2, seed=42)
|
||||
expected = nx.grid_2d_graph(3, 3, create_using=nx.DiGraph)
|
||||
assert nx.utils.graphs_equal(G, expected)
|
||||
|
||||
@pytest.mark.parametrize("seed", range(2478, 2578, 10))
|
||||
def test_r_general_scaling(self, seed):
|
||||
"""The probability of adding a long-range edge scales with `1 / dist**r`,
|
||||
so a navigable_small_world graph created with r < 1 should generally
|
||||
result in more edges than a navigable_small_world graph with r >= 1
|
||||
(for 0 < q << n).
|
||||
|
||||
N.B. this is probabilistic, so this test may not hold for all seeds."""
|
||||
G1 = nx.navigable_small_world_graph(7, q=3, r=0.5, seed=seed)
|
||||
G2 = nx.navigable_small_world_graph(7, q=3, r=1, seed=seed)
|
||||
G3 = nx.navigable_small_world_graph(7, q=3, r=2, seed=seed)
|
||||
assert G1.number_of_edges() > G2.number_of_edges()
|
||||
assert G2.number_of_edges() > G3.number_of_edges()
|
||||
|
||||
|
||||
class TestThresholdedRandomGeometricGraph:
|
||||
"""Unit tests for :func:`~networkx.thresholded_random_geometric_graph`"""
|
||||
|
||||
def test_number_of_nodes(self):
|
||||
G = nx.thresholded_random_geometric_graph(50, 0.2, 0.1, seed=42)
|
||||
assert len(G) == 50
|
||||
G = nx.thresholded_random_geometric_graph(range(50), 0.2, 0.1, seed=42)
|
||||
assert len(G) == 50
|
||||
|
||||
def test_distances(self):
|
||||
"""Tests that pairs of vertices adjacent if and only if they are
|
||||
within the prescribed radius.
|
||||
"""
|
||||
# Use the Euclidean metric, the default according to the
|
||||
# documentation.
|
||||
G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, seed=42)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_p(self):
|
||||
"""Tests for providing an alternate distance metric to the generator."""
|
||||
|
||||
# Use the L1 metric.
|
||||
def dist(x, y):
|
||||
return sum(abs(a - b) for a, b in zip(x, y))
|
||||
|
||||
G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, p=1, seed=42)
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_node_names(self):
|
||||
"""Tests using values other than sequential numbers as node IDs."""
|
||||
import string
|
||||
|
||||
nodes = list(string.ascii_lowercase)
|
||||
G = nx.thresholded_random_geometric_graph(nodes, 0.25, 0.1, seed=42)
|
||||
assert len(G) == len(nodes)
|
||||
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
|
||||
|
||||
def test_theta(self):
|
||||
"""Tests that pairs of vertices adjacent if and only if their sum
|
||||
weights exceeds the threshold parameter theta.
|
||||
"""
|
||||
G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, seed=42)
|
||||
|
||||
for u, v in combinations(G, 2):
|
||||
# Adjacent vertices must be within the given distance.
|
||||
if v in G[u]:
|
||||
assert (G.nodes[u]["weight"] + G.nodes[v]["weight"]) >= 0.1
|
||||
|
||||
def test_pos_name(self):
|
||||
trgg = nx.thresholded_random_geometric_graph
|
||||
G = trgg(50, 0.25, 0.1, seed=42, pos_name="p", weight_name="wt")
|
||||
assert all(len(d["p"]) == 2 for n, d in G.nodes.items())
|
||||
assert all(d["wt"] > 0 for n, d in G.nodes.items())
|
||||
|
||||
|
||||
def test_geometric_edges_pos_attribute():
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(
|
||||
[
|
||||
(0, {"position": (0, 0)}),
|
||||
(1, {"position": (0, 1)}),
|
||||
(2, {"position": (1, 0)}),
|
||||
]
|
||||
)
|
||||
expected_edges = [(0, 1), (0, 2)]
|
||||
assert expected_edges == nx.geometric_edges(G, radius=1, pos_name="position")
|
||||
|
||||
|
||||
def test_geometric_edges_raises_no_pos():
|
||||
G = nx.path_graph(3)
|
||||
msg = "all nodes. must have a '"
|
||||
with pytest.raises(nx.NetworkXError, match=msg):
|
||||
nx.geometric_edges(G, radius=1)
|
||||
|
||||
|
||||
def test_number_of_nodes_S1():
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, n=100, gamma=2.7, mean_degree=10, seed=42
|
||||
)
|
||||
assert len(G) == 100
|
||||
|
||||
|
||||
def test_set_attributes_S1():
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, n=100, gamma=2.7, mean_degree=10, seed=42
|
||||
)
|
||||
kappas = nx.get_node_attributes(G, "kappa")
|
||||
assert len(kappas) == 100
|
||||
thetas = nx.get_node_attributes(G, "theta")
|
||||
assert len(thetas) == 100
|
||||
radii = nx.get_node_attributes(G, "radius")
|
||||
assert len(radii) == 100
|
||||
|
||||
|
||||
def test_mean_kappas_mean_degree_S1():
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=2.5, n=50, gamma=2.7, mean_degree=10, seed=8023
|
||||
)
|
||||
|
||||
kappas = nx.get_node_attributes(G, "kappa")
|
||||
mean_kappas = sum(kappas.values()) / len(kappas)
|
||||
assert math.fabs(mean_kappas - 10) < 0.5
|
||||
|
||||
degrees = dict(G.degree())
|
||||
mean_degree = sum(degrees.values()) / len(degrees)
|
||||
assert math.fabs(mean_degree - 10) < 1
|
||||
|
||||
|
||||
def test_dict_kappas_S1():
|
||||
kappas = {i: 10 for i in range(1000)}
|
||||
G = nx.geometric_soft_configuration_graph(beta=1, kappas=kappas)
|
||||
assert len(G) == 1000
|
||||
kappas = nx.get_node_attributes(G, "kappa")
|
||||
assert all(kappa == 10 for kappa in kappas.values())
|
||||
|
||||
|
||||
def test_beta_clustering_S1():
|
||||
G1 = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, n=100, gamma=3.5, mean_degree=10, seed=42
|
||||
)
|
||||
G2 = nx.geometric_soft_configuration_graph(
|
||||
beta=3.0, n=100, gamma=3.5, mean_degree=10, seed=42
|
||||
)
|
||||
assert nx.average_clustering(G1) < nx.average_clustering(G2)
|
||||
|
||||
|
||||
def test_wrong_parameters_S1():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError,
|
||||
match="Please provide either kappas, or all 3 of: n, gamma and mean_degree.",
|
||||
):
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, gamma=3.5, mean_degree=10, seed=42
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
nx.NetworkXError,
|
||||
match="When kappas is input, n, gamma and mean_degree must not be.",
|
||||
):
|
||||
kappas = {i: 10 for i in range(1000)}
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, kappas=kappas, gamma=2.3, seed=42
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
nx.NetworkXError,
|
||||
match="Please provide either kappas, or all 3 of: n, gamma and mean_degree.",
|
||||
):
|
||||
G = nx.geometric_soft_configuration_graph(beta=1.5, seed=42)
|
||||
|
||||
|
||||
def test_negative_beta_S1():
|
||||
with pytest.raises(
|
||||
nx.NetworkXError, match="The parameter beta cannot be smaller or equal to 0."
|
||||
):
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=-1, n=100, gamma=2.3, mean_degree=10, seed=42
|
||||
)
|
||||
|
||||
|
||||
def test_non_zero_clustering_beta_lower_one_S1():
|
||||
G = nx.geometric_soft_configuration_graph(
|
||||
beta=0.5, n=100, gamma=3.5, mean_degree=10, seed=42
|
||||
)
|
||||
assert nx.average_clustering(G) > 0
|
||||
|
||||
|
||||
def test_mean_degree_influence_on_connectivity_S1():
|
||||
low_mean_degree = 2
|
||||
high_mean_degree = 20
|
||||
G_low = nx.geometric_soft_configuration_graph(
|
||||
beta=1.2, n=100, gamma=2.7, mean_degree=low_mean_degree, seed=42
|
||||
)
|
||||
G_high = nx.geometric_soft_configuration_graph(
|
||||
beta=1.2, n=100, gamma=2.7, mean_degree=high_mean_degree, seed=42
|
||||
)
|
||||
assert nx.number_connected_components(G_low) > nx.number_connected_components(
|
||||
G_high
|
||||
)
|
||||
|
||||
|
||||
def test_compare_mean_kappas_different_gammas_S1():
|
||||
G1 = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, n=20, gamma=2.7, mean_degree=5, seed=42
|
||||
)
|
||||
G2 = nx.geometric_soft_configuration_graph(
|
||||
beta=1.5, n=20, gamma=3.5, mean_degree=5, seed=42
|
||||
)
|
||||
kappas1 = nx.get_node_attributes(G1, "kappa")
|
||||
mean_kappas1 = sum(kappas1.values()) / len(kappas1)
|
||||
kappas2 = nx.get_node_attributes(G2, "kappa")
|
||||
mean_kappas2 = sum(kappas2.values()) / len(kappas2)
|
||||
assert math.fabs(mean_kappas1 - mean_kappas2) < 1
|
||||
134
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_harary_graph.py
vendored
Normal file
134
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_harary_graph.py
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
"""Unit tests for the :mod:`networkx.generators.harary_graph` module.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.isomorphism.isomorph import is_isomorphic
|
||||
from networkx.generators.harary_graph import hkn_harary_graph, hnm_harary_graph
|
||||
|
||||
|
||||
class TestHararyGraph:
|
||||
"""
|
||||
Suppose n nodes, m >= n-1 edges, d = 2m // n, r = 2m % n
|
||||
"""
|
||||
|
||||
def test_hnm_harary_graph(self):
|
||||
# When d is even and r = 0, the hnm_harary_graph(n,m) is
|
||||
# the circulant_graph(n, list(range(1,d/2+1)))
|
||||
for n, m in [(5, 5), (6, 12), (7, 14)]:
|
||||
G1 = hnm_harary_graph(n, m)
|
||||
d = 2 * m // n
|
||||
G2 = nx.circulant_graph(n, list(range(1, d // 2 + 1)))
|
||||
assert is_isomorphic(G1, G2)
|
||||
|
||||
# When d is even and r > 0, the hnm_harary_graph(n,m) is
|
||||
# the circulant_graph(n, list(range(1,d/2+1)))
|
||||
# with r edges added arbitrarily
|
||||
for n, m in [(5, 7), (6, 13), (7, 16)]:
|
||||
G1 = hnm_harary_graph(n, m)
|
||||
d = 2 * m // n
|
||||
G2 = nx.circulant_graph(n, list(range(1, d // 2 + 1)))
|
||||
assert set(G2.edges) < set(G1.edges)
|
||||
assert G1.number_of_edges() == m
|
||||
|
||||
# When d is odd and n is even and r = 0, the hnm_harary_graph(n,m)
|
||||
# is the circulant_graph(n, list(range(1,(d+1)/2) plus [n//2])
|
||||
for n, m in [(6, 9), (8, 12), (10, 15)]:
|
||||
G1 = hnm_harary_graph(n, m)
|
||||
d = 2 * m // n
|
||||
L = list(range(1, (d + 1) // 2))
|
||||
L.append(n // 2)
|
||||
G2 = nx.circulant_graph(n, L)
|
||||
assert is_isomorphic(G1, G2)
|
||||
|
||||
# When d is odd and n is even and r > 0, the hnm_harary_graph(n,m)
|
||||
# is the circulant_graph(n, list(range(1,(d+1)/2) plus [n//2])
|
||||
# with r edges added arbitrarily
|
||||
for n, m in [(6, 10), (8, 13), (10, 17)]:
|
||||
G1 = hnm_harary_graph(n, m)
|
||||
d = 2 * m // n
|
||||
L = list(range(1, (d + 1) // 2))
|
||||
L.append(n // 2)
|
||||
G2 = nx.circulant_graph(n, L)
|
||||
assert set(G2.edges) < set(G1.edges)
|
||||
assert G1.number_of_edges() == m
|
||||
|
||||
# When d is odd and n is odd, the hnm_harary_graph(n,m) is
|
||||
# the circulant_graph(n, list(range(1,(d+1)/2))
|
||||
# with m - n*(d-1)/2 edges added arbitrarily
|
||||
for n, m in [(5, 4), (7, 12), (9, 14)]:
|
||||
G1 = hnm_harary_graph(n, m)
|
||||
d = 2 * m // n
|
||||
L = list(range(1, (d + 1) // 2))
|
||||
G2 = nx.circulant_graph(n, L)
|
||||
assert set(G2.edges) < set(G1.edges)
|
||||
assert G1.number_of_edges() == m
|
||||
|
||||
# Raise NetworkXError if n<1
|
||||
n = 0
|
||||
m = 0
|
||||
pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
|
||||
|
||||
# Raise NetworkXError if m < n-1
|
||||
n = 6
|
||||
m = 4
|
||||
pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
|
||||
|
||||
# Raise NetworkXError if m > n(n-1)/2
|
||||
n = 6
|
||||
m = 16
|
||||
pytest.raises(nx.NetworkXError, hnm_harary_graph, n, m)
|
||||
|
||||
"""
|
||||
Suppose connectivity k, number of nodes n
|
||||
"""
|
||||
|
||||
def test_hkn_harary_graph(self):
|
||||
# When k == 1, the hkn_harary_graph(k,n) is
|
||||
# the path_graph(n)
|
||||
for k, n in [(1, 6), (1, 7)]:
|
||||
G1 = hkn_harary_graph(k, n)
|
||||
G2 = nx.path_graph(n)
|
||||
assert is_isomorphic(G1, G2)
|
||||
|
||||
# When k is even, the hkn_harary_graph(k,n) is
|
||||
# the circulant_graph(n, list(range(1,k/2+1)))
|
||||
for k, n in [(2, 6), (2, 7), (4, 6), (4, 7)]:
|
||||
G1 = hkn_harary_graph(k, n)
|
||||
G2 = nx.circulant_graph(n, list(range(1, k // 2 + 1)))
|
||||
assert is_isomorphic(G1, G2)
|
||||
|
||||
# When k is odd and n is even, the hkn_harary_graph(k,n) is
|
||||
# the circulant_graph(n, list(range(1,(k+1)/2)) plus [n/2])
|
||||
for k, n in [(3, 6), (5, 8), (7, 10)]:
|
||||
G1 = hkn_harary_graph(k, n)
|
||||
L = list(range(1, (k + 1) // 2))
|
||||
L.append(n // 2)
|
||||
G2 = nx.circulant_graph(n, L)
|
||||
assert is_isomorphic(G1, G2)
|
||||
|
||||
# When k is odd and n is odd, the hkn_harary_graph(k,n) is
|
||||
# the circulant_graph(n, list(range(1,(k+1)/2))) with
|
||||
# n//2+1 edges added between node i and node i+n//2+1
|
||||
for k, n in [(3, 5), (5, 9), (7, 11)]:
|
||||
G1 = hkn_harary_graph(k, n)
|
||||
G2 = nx.circulant_graph(n, list(range(1, (k + 1) // 2)))
|
||||
eSet1 = set(G1.edges)
|
||||
eSet2 = set(G2.edges)
|
||||
eSet3 = set()
|
||||
half = n // 2
|
||||
for i in range(half + 1):
|
||||
# add half+1 edges between i and i+half
|
||||
eSet3.add((i, (i + half) % n))
|
||||
assert eSet1 == eSet2 | eSet3
|
||||
|
||||
# Raise NetworkXError if k<1
|
||||
k = 0
|
||||
n = 0
|
||||
pytest.raises(nx.NetworkXError, hkn_harary_graph, k, n)
|
||||
|
||||
# Raise NetworkXError if n<k+1
|
||||
k = 6
|
||||
n = 6
|
||||
pytest.raises(nx.NetworkXError, hkn_harary_graph, k, n)
|
||||
176
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_internet_as_graphs.py
vendored
Normal file
176
.CondaPkg/env/Lib/site-packages/networkx/generators/tests/test_internet_as_graphs.py
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
from pytest import approx
|
||||
|
||||
from networkx import is_connected, neighbors
|
||||
from networkx.generators.internet_as_graphs import random_internet_as_graph
|
||||
|
||||
|
||||
class TestInternetASTopology:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.n = 1000
|
||||
cls.seed = 42
|
||||
cls.G = random_internet_as_graph(cls.n, cls.seed)
|
||||
cls.T = []
|
||||
cls.M = []
|
||||
cls.C = []
|
||||
cls.CP = []
|
||||
cls.customers = {}
|
||||
cls.providers = {}
|
||||
|
||||
for i in cls.G.nodes():
|
||||
if cls.G.nodes[i]["type"] == "T":
|
||||
cls.T.append(i)
|
||||
elif cls.G.nodes[i]["type"] == "M":
|
||||
cls.M.append(i)
|
||||
elif cls.G.nodes[i]["type"] == "C":
|
||||
cls.C.append(i)
|
||||
elif cls.G.nodes[i]["type"] == "CP":
|
||||
cls.CP.append(i)
|
||||
else:
|
||||
raise ValueError("Inconsistent data in the graph node attributes")
|
||||
cls.set_customers(i)
|
||||
cls.set_providers(i)
|
||||
|
||||
@classmethod
|
||||
def set_customers(cls, i):
|
||||
if i not in cls.customers:
|
||||
cls.customers[i] = set()
|
||||
for j in neighbors(cls.G, i):
|
||||
e = cls.G.edges[(i, j)]
|
||||
if e["type"] == "transit":
|
||||
customer = int(e["customer"])
|
||||
if j == customer:
|
||||
cls.set_customers(j)
|
||||
cls.customers[i] = cls.customers[i].union(cls.customers[j])
|
||||
cls.customers[i].add(j)
|
||||
elif i != customer:
|
||||
raise ValueError(
|
||||
"Inconsistent data in the graph edge attributes"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def set_providers(cls, i):
|
||||
if i not in cls.providers:
|
||||
cls.providers[i] = set()
|
||||
for j in neighbors(cls.G, i):
|
||||
e = cls.G.edges[(i, j)]
|
||||
if e["type"] == "transit":
|
||||
customer = int(e["customer"])
|
||||
if i == customer:
|
||||
cls.set_providers(j)
|
||||
cls.providers[i] = cls.providers[i].union(cls.providers[j])
|
||||
cls.providers[i].add(j)
|
||||
elif j != customer:
|
||||
raise ValueError(
|
||||
"Inconsistent data in the graph edge attributes"
|
||||
)
|
||||
|
||||
def test_wrong_input(self):
|
||||
G = random_internet_as_graph(0)
|
||||
assert len(G.nodes()) == 0
|
||||
|
||||
G = random_internet_as_graph(-1)
|
||||
assert len(G.nodes()) == 0
|
||||
|
||||
G = random_internet_as_graph(1)
|
||||
assert len(G.nodes()) == 1
|
||||
|
||||
def test_node_numbers(self):
|
||||
assert len(self.G.nodes()) == self.n
|
||||
assert len(self.T) < 7
|
||||
assert len(self.M) == round(self.n * 0.15)
|
||||
assert len(self.CP) == round(self.n * 0.05)
|
||||
numb = self.n - len(self.T) - len(self.M) - len(self.CP)
|
||||
assert len(self.C) == numb
|
||||
|
||||
def test_connectivity(self):
|
||||
assert is_connected(self.G)
|
||||
|
||||
def test_relationships(self):
|
||||
# T nodes are not customers of anyone
|
||||
for i in self.T:
|
||||
assert len(self.providers[i]) == 0
|
||||
|
||||
# C nodes are not providers of anyone
|
||||
for i in self.C:
|
||||
assert len(self.customers[i]) == 0
|
||||
|
||||
# CP nodes are not providers of anyone
|
||||
for i in self.CP:
|
||||
assert len(self.customers[i]) == 0
|
||||
|
||||
# test whether there is a customer-provider loop
|
||||
for i in self.G.nodes():
|
||||
assert len(self.customers[i].intersection(self.providers[i])) == 0
|
||||
|
||||
# test whether there is a peering with a customer or provider
|
||||
for i, j in self.G.edges():
|
||||
if self.G.edges[(i, j)]["type"] == "peer":
|
||||
assert j not in self.customers[i]
|
||||
assert i not in self.customers[j]
|
||||
assert j not in self.providers[i]
|
||||
assert i not in self.providers[j]
|
||||
|
||||
def test_degree_values(self):
|
||||
d_m = 0 # multihoming degree for M nodes
|
||||
d_cp = 0 # multihoming degree for CP nodes
|
||||
d_c = 0 # multihoming degree for C nodes
|
||||
p_m_m = 0 # avg number of peering edges between M and M
|
||||
p_cp_m = 0 # avg number of peering edges between CP and M
|
||||
p_cp_cp = 0 # avg number of peering edges between CP and CP
|
||||
t_m = 0 # probability M's provider is T
|
||||
t_cp = 0 # probability CP's provider is T
|
||||
t_c = 0 # probability C's provider is T
|
||||
|
||||
for i, j in self.G.edges():
|
||||
e = self.G.edges[(i, j)]
|
||||
if e["type"] == "transit":
|
||||
cust = int(e["customer"])
|
||||
if i == cust:
|
||||
prov = j
|
||||
elif j == cust:
|
||||
prov = i
|
||||
else:
|
||||
raise ValueError("Inconsistent data in the graph edge attributes")
|
||||
if cust in self.M:
|
||||
d_m += 1
|
||||
if self.G.nodes[prov]["type"] == "T":
|
||||
t_m += 1
|
||||
elif cust in self.C:
|
||||
d_c += 1
|
||||
if self.G.nodes[prov]["type"] == "T":
|
||||
t_c += 1
|
||||
elif cust in self.CP:
|
||||
d_cp += 1
|
||||
if self.G.nodes[prov]["type"] == "T":
|
||||
t_cp += 1
|
||||
else:
|
||||
raise ValueError("Inconsistent data in the graph edge attributes")
|
||||
elif e["type"] == "peer":
|
||||
if self.G.nodes[i]["type"] == "M" and self.G.nodes[j]["type"] == "M":
|
||||
p_m_m += 1
|
||||
if self.G.nodes[i]["type"] == "CP" and self.G.nodes[j]["type"] == "CP":
|
||||
p_cp_cp += 1
|
||||
if (
|
||||
self.G.nodes[i]["type"] == "M"
|
||||
and self.G.nodes[j]["type"] == "CP"
|
||||
or self.G.nodes[i]["type"] == "CP"
|
||||
and self.G.nodes[j]["type"] == "M"
|
||||
):
|
||||
p_cp_m += 1
|
||||
else:
|
||||
raise ValueError("Unexpected data in the graph edge attributes")
|
||||
|
||||
assert d_m / len(self.M) == approx((2 + (2.5 * self.n) / 10000), abs=1e-0)
|
||||
assert d_cp / len(self.CP) == approx((2 + (1.5 * self.n) / 10000), abs=1e-0)
|
||||
assert d_c / len(self.C) == approx((1 + (5 * self.n) / 100000), abs=1e-0)
|
||||
|
||||
assert p_m_m / len(self.M) == approx((1 + (2 * self.n) / 10000), abs=1e-0)
|
||||
assert p_cp_m / len(self.CP) == approx((0.2 + (2 * self.n) / 10000), abs=1e-0)
|
||||
assert p_cp_cp / len(self.CP) == approx(
|
||||
(0.05 + (2 * self.n) / 100000), abs=1e-0
|
||||
)
|
||||
|
||||
assert t_m / d_m == approx(0.375, abs=1e-1)
|
||||
assert t_cp / d_cp == approx(0.375, abs=1e-1)
|
||||
assert t_c / d_c == approx(0.125, abs=1e-1)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user