Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
|
@ -0,0 +1,4 @@
|
|||
from networkx.algorithms.operators.all import *
|
||||
from networkx.algorithms.operators.binary import *
|
||||
from networkx.algorithms.operators.product import *
|
||||
from networkx.algorithms.operators.unary import *
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
163
venv/Lib/site-packages/networkx/algorithms/operators/all.py
Normal file
163
venv/Lib/site-packages/networkx/algorithms/operators/all.py
Normal file
|
@ -0,0 +1,163 @@
|
|||
"""Operations on many graphs.
|
||||
"""
|
||||
from itertools import zip_longest
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["union_all", "compose_all", "disjoint_union_all", "intersection_all"]
|
||||
|
||||
|
||||
def union_all(graphs, rename=(None,)):
|
||||
"""Returns the union of all graphs.
|
||||
|
||||
The graphs must be disjoint, otherwise an exception is raised.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graphs : list of graphs
|
||||
List of NetworkX graphs
|
||||
|
||||
rename : bool , default=(None, None)
|
||||
Node names of G and H can be changed by specifying the tuple
|
||||
rename=('G-','H-') (for example). Node "u" in G is then renamed
|
||||
"G-u" and "v" in H is renamed "H-v".
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : a graph with the same type as the first graph in list
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `graphs` is an empty list.
|
||||
|
||||
Notes
|
||||
-----
|
||||
To force a disjoint union with node relabeling, use
|
||||
disjoint_union_all(G,H) or convert_node_labels_to integers().
|
||||
|
||||
Graph, edge, and node attributes are propagated to the union graph.
|
||||
If a graph attribute is present in multiple graphs, then the value
|
||||
from the last graph in the list with that attribute is used.
|
||||
|
||||
See Also
|
||||
--------
|
||||
union
|
||||
disjoint_union_all
|
||||
"""
|
||||
if not graphs:
|
||||
raise ValueError("cannot apply union_all to an empty list")
|
||||
graphs_names = zip_longest(graphs, rename)
|
||||
U, gname = next(graphs_names)
|
||||
for H, hname in graphs_names:
|
||||
U = nx.union(U, H, (gname, hname))
|
||||
gname = None
|
||||
return U
|
||||
|
||||
|
||||
def disjoint_union_all(graphs):
|
||||
"""Returns the disjoint union of all graphs.
|
||||
|
||||
This operation forces distinct integer node labels starting with 0
|
||||
for the first graph in the list and numbering consecutively.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graphs : list
|
||||
List of NetworkX graphs
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : A graph with the same type as the first graph in list
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `graphs` is an empty list.
|
||||
|
||||
Notes
|
||||
-----
|
||||
It is recommended that the graphs be either all directed or all undirected.
|
||||
|
||||
Graph, edge, and node attributes are propagated to the union graph.
|
||||
If a graph attribute is present in multiple graphs, then the value
|
||||
from the last graph in the list with that attribute is used.
|
||||
"""
|
||||
if not graphs:
|
||||
raise ValueError("cannot apply disjoint_union_all to an empty list")
|
||||
graphs = iter(graphs)
|
||||
U = next(graphs)
|
||||
for H in graphs:
|
||||
U = nx.disjoint_union(U, H)
|
||||
return U
|
||||
|
||||
|
||||
def compose_all(graphs):
|
||||
"""Returns the composition of all graphs.
|
||||
|
||||
Composition is the simple union of the node sets and edge sets.
|
||||
The node sets of the supplied graphs need not be disjoint.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graphs : list
|
||||
List of NetworkX graphs
|
||||
|
||||
Returns
|
||||
-------
|
||||
C : A graph with the same type as the first graph in list
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `graphs` is an empty list.
|
||||
|
||||
Notes
|
||||
-----
|
||||
It is recommended that the supplied graphs be either all directed or all
|
||||
undirected.
|
||||
|
||||
Graph, edge, and node attributes are propagated to the union graph.
|
||||
If a graph attribute is present in multiple graphs, then the value
|
||||
from the last graph in the list with that attribute is used.
|
||||
"""
|
||||
if not graphs:
|
||||
raise ValueError("cannot apply compose_all to an empty list")
|
||||
graphs = iter(graphs)
|
||||
C = next(graphs)
|
||||
for H in graphs:
|
||||
C = nx.compose(C, H)
|
||||
return C
|
||||
|
||||
|
||||
def intersection_all(graphs):
|
||||
"""Returns a new graph that contains only the edges that exist in
|
||||
all graphs.
|
||||
|
||||
All supplied graphs must have the same node set.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
graphs : list
|
||||
List of NetworkX graphs
|
||||
|
||||
Returns
|
||||
-------
|
||||
R : A new graph with the same type as the first graph in list
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `graphs` is an empty list.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Attributes from the graph, nodes, and edges are not copied to the new
|
||||
graph.
|
||||
"""
|
||||
if not graphs:
|
||||
raise ValueError("cannot apply intersection_all to an empty list")
|
||||
graphs = iter(graphs)
|
||||
R = next(graphs)
|
||||
for H in graphs:
|
||||
R = nx.intersection(R, H)
|
||||
return R
|
404
venv/Lib/site-packages/networkx/algorithms/operators/binary.py
Normal file
404
venv/Lib/site-packages/networkx/algorithms/operators/binary.py
Normal file
|
@ -0,0 +1,404 @@
|
|||
"""
|
||||
Operations on graphs including union, intersection, difference.
|
||||
"""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = [
|
||||
"union",
|
||||
"compose",
|
||||
"disjoint_union",
|
||||
"intersection",
|
||||
"difference",
|
||||
"symmetric_difference",
|
||||
"full_join",
|
||||
]
|
||||
|
||||
|
||||
def union(G, H, rename=(None, None), name=None):
|
||||
""" Return the union of graphs G and H.
|
||||
|
||||
Graphs G and H must be disjoint, otherwise an exception is raised.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph
|
||||
|
||||
rename : bool , default=(None, None)
|
||||
Node names of G and H can be changed by specifying the tuple
|
||||
rename=('G-','H-') (for example). Node "u" in G is then renamed
|
||||
"G-u" and "v" in H is renamed "H-v".
|
||||
|
||||
name : string
|
||||
Specify the name for the union graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : A union graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
To force a disjoint union with node relabeling, use
|
||||
disjoint_union(G,H) or convert_node_labels_to integers().
|
||||
|
||||
Graph, edge, and node attributes are propagated from G and H
|
||||
to the union graph. If a graph attribute is present in both
|
||||
G and H the value from H is used.
|
||||
|
||||
See Also
|
||||
--------
|
||||
disjoint_union
|
||||
"""
|
||||
if not G.is_multigraph() == H.is_multigraph():
|
||||
raise nx.NetworkXError("G and H must both be graphs or multigraphs.")
|
||||
# Union is the same type as G
|
||||
R = G.__class__()
|
||||
# add graph attributes, H attributes take precedent over G attributes
|
||||
R.graph.update(G.graph)
|
||||
R.graph.update(H.graph)
|
||||
|
||||
# rename graph to obtain disjoint node labels
|
||||
def add_prefix(graph, prefix):
|
||||
if prefix is None:
|
||||
return graph
|
||||
|
||||
def label(x):
|
||||
if isinstance(x, str):
|
||||
name = prefix + x
|
||||
else:
|
||||
name = prefix + repr(x)
|
||||
return name
|
||||
|
||||
return nx.relabel_nodes(graph, label)
|
||||
|
||||
G = add_prefix(G, rename[0])
|
||||
H = add_prefix(H, rename[1])
|
||||
if set(G) & set(H):
|
||||
raise nx.NetworkXError(
|
||||
"The node sets of G and H are not disjoint.",
|
||||
"Use appropriate rename=(Gprefix,Hprefix)" "or use disjoint_union(G,H).",
|
||||
)
|
||||
if G.is_multigraph():
|
||||
G_edges = G.edges(keys=True, data=True)
|
||||
else:
|
||||
G_edges = G.edges(data=True)
|
||||
if H.is_multigraph():
|
||||
H_edges = H.edges(keys=True, data=True)
|
||||
else:
|
||||
H_edges = H.edges(data=True)
|
||||
|
||||
# add nodes
|
||||
R.add_nodes_from(G)
|
||||
R.add_nodes_from(H)
|
||||
# add edges
|
||||
R.add_edges_from(G_edges)
|
||||
R.add_edges_from(H_edges)
|
||||
# add node attributes
|
||||
for n in G:
|
||||
R.nodes[n].update(G.nodes[n])
|
||||
for n in H:
|
||||
R.nodes[n].update(H.nodes[n])
|
||||
|
||||
return R
|
||||
|
||||
|
||||
def disjoint_union(G, H):
|
||||
""" Return the disjoint union of graphs G and H.
|
||||
|
||||
This algorithm forces distinct integer node labels.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : A union graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
A new graph is created, of the same class as G. It is recommended
|
||||
that G and H be either both directed or both undirected.
|
||||
|
||||
The nodes of G are relabeled 0 to len(G)-1, and the nodes of H are
|
||||
relabeled len(G) to len(G)+len(H)-1.
|
||||
|
||||
Graph, edge, and node attributes are propagated from G and H
|
||||
to the union graph. If a graph attribute is present in both
|
||||
G and H the value from H is used.
|
||||
"""
|
||||
R1 = nx.convert_node_labels_to_integers(G)
|
||||
R2 = nx.convert_node_labels_to_integers(H, first_label=len(R1))
|
||||
R = union(R1, R2)
|
||||
R.graph.update(G.graph)
|
||||
R.graph.update(H.graph)
|
||||
return R
|
||||
|
||||
|
||||
def intersection(G, H):
|
||||
"""Returns a new graph that contains only the edges that exist in
|
||||
both G and H.
|
||||
|
||||
The node sets of H and G must be the same.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph. G and H must have the same node sets.
|
||||
|
||||
Returns
|
||||
-------
|
||||
GH : A new graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Attributes from the graph, nodes, and edges are not copied to the new
|
||||
graph. If you want a new graph of the intersection of G and H
|
||||
with the attributes (including edge data) from G use remove_nodes_from()
|
||||
as follows
|
||||
|
||||
>>> G = nx.path_graph(3)
|
||||
>>> H = nx.path_graph(5)
|
||||
>>> R = G.copy()
|
||||
>>> R.remove_nodes_from(n for n in G if n not in H)
|
||||
"""
|
||||
# create new graph
|
||||
R = nx.create_empty_copy(G)
|
||||
|
||||
if not G.is_multigraph() == H.is_multigraph():
|
||||
raise nx.NetworkXError("G and H must both be graphs or multigraphs.")
|
||||
if set(G) != set(H):
|
||||
raise nx.NetworkXError("Node sets of graphs are not equal")
|
||||
|
||||
if G.number_of_edges() <= H.number_of_edges():
|
||||
if G.is_multigraph():
|
||||
edges = G.edges(keys=True)
|
||||
else:
|
||||
edges = G.edges()
|
||||
for e in edges:
|
||||
if H.has_edge(*e):
|
||||
R.add_edge(*e)
|
||||
else:
|
||||
if H.is_multigraph():
|
||||
edges = H.edges(keys=True)
|
||||
else:
|
||||
edges = H.edges()
|
||||
for e in edges:
|
||||
if G.has_edge(*e):
|
||||
R.add_edge(*e)
|
||||
return R
|
||||
|
||||
|
||||
def difference(G, H):
|
||||
"""Returns a new graph that contains the edges that exist in G but not in H.
|
||||
|
||||
The node sets of H and G must be the same.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph. G and H must have the same node sets.
|
||||
|
||||
Returns
|
||||
-------
|
||||
D : A new graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Attributes from the graph, nodes, and edges are not copied to the new
|
||||
graph. If you want a new graph of the difference of G and H with
|
||||
with the attributes (including edge data) from G use remove_nodes_from()
|
||||
as follows:
|
||||
|
||||
>>> G = nx.path_graph(3)
|
||||
>>> H = nx.path_graph(5)
|
||||
>>> R = G.copy()
|
||||
>>> R.remove_nodes_from(n for n in G if n in H)
|
||||
"""
|
||||
# create new graph
|
||||
if not G.is_multigraph() == H.is_multigraph():
|
||||
raise nx.NetworkXError("G and H must both be graphs or multigraphs.")
|
||||
R = nx.create_empty_copy(G)
|
||||
|
||||
if set(G) != set(H):
|
||||
raise nx.NetworkXError("Node sets of graphs not equal")
|
||||
|
||||
if G.is_multigraph():
|
||||
edges = G.edges(keys=True)
|
||||
else:
|
||||
edges = G.edges()
|
||||
for e in edges:
|
||||
if not H.has_edge(*e):
|
||||
R.add_edge(*e)
|
||||
return R
|
||||
|
||||
|
||||
def symmetric_difference(G, H):
|
||||
"""Returns new graph with edges that exist in either G or H but not both.
|
||||
|
||||
The node sets of H and G must be the same.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph. G and H must have the same node sets.
|
||||
|
||||
Returns
|
||||
-------
|
||||
D : A new graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Attributes from the graph, nodes, and edges are not copied to the new
|
||||
graph.
|
||||
"""
|
||||
# create new graph
|
||||
if not G.is_multigraph() == H.is_multigraph():
|
||||
raise nx.NetworkXError("G and H must both be graphs or multigraphs.")
|
||||
R = nx.create_empty_copy(G)
|
||||
|
||||
if set(G) != set(H):
|
||||
raise nx.NetworkXError("Node sets of graphs not equal")
|
||||
|
||||
gnodes = set(G) # set of nodes in G
|
||||
hnodes = set(H) # set of nodes in H
|
||||
nodes = gnodes.symmetric_difference(hnodes)
|
||||
R.add_nodes_from(nodes)
|
||||
|
||||
if G.is_multigraph():
|
||||
edges = G.edges(keys=True)
|
||||
else:
|
||||
edges = G.edges()
|
||||
# we could copy the data here but then this function doesn't
|
||||
# match intersection and difference
|
||||
for e in edges:
|
||||
if not H.has_edge(*e):
|
||||
R.add_edge(*e)
|
||||
|
||||
if H.is_multigraph():
|
||||
edges = H.edges(keys=True)
|
||||
else:
|
||||
edges = H.edges()
|
||||
for e in edges:
|
||||
if not G.has_edge(*e):
|
||||
R.add_edge(*e)
|
||||
return R
|
||||
|
||||
|
||||
def compose(G, H):
|
||||
"""Returns a new graph of G composed with H.
|
||||
|
||||
Composition is the simple union of the node sets and edge sets.
|
||||
The node sets of G and H do not need to be disjoint.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H : graph
|
||||
A NetworkX graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
C: A new graph with the same type as G
|
||||
|
||||
Notes
|
||||
-----
|
||||
It is recommended that G and H be either both directed or both undirected.
|
||||
Attributes from H take precedent over attributes from G.
|
||||
|
||||
For MultiGraphs, the edges are identified by incident nodes AND edge-key.
|
||||
This can cause surprises (i.e., edge `(1, 2)` may or may not be the same
|
||||
in two graphs) if you use MultiGraph without keeping track of edge keys.
|
||||
"""
|
||||
if not G.is_multigraph() == H.is_multigraph():
|
||||
raise nx.NetworkXError("G and H must both be graphs or multigraphs.")
|
||||
|
||||
R = G.__class__()
|
||||
# add graph attributes, H attributes take precedent over G attributes
|
||||
R.graph.update(G.graph)
|
||||
R.graph.update(H.graph)
|
||||
|
||||
R.add_nodes_from(G.nodes(data=True))
|
||||
R.add_nodes_from(H.nodes(data=True))
|
||||
|
||||
if G.is_multigraph():
|
||||
R.add_edges_from(G.edges(keys=True, data=True))
|
||||
else:
|
||||
R.add_edges_from(G.edges(data=True))
|
||||
if H.is_multigraph():
|
||||
R.add_edges_from(H.edges(keys=True, data=True))
|
||||
else:
|
||||
R.add_edges_from(H.edges(data=True))
|
||||
return R
|
||||
|
||||
|
||||
def full_join(G, H, rename=(None, None)):
|
||||
"""Returns the full join of graphs G and H.
|
||||
|
||||
Full join is the union of G and H in which all edges between
|
||||
G and H are added.
|
||||
The node sets of G and H must be disjoint,
|
||||
otherwise an exception is raised.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H : graph
|
||||
A NetworkX graph
|
||||
|
||||
rename : bool , default=(None, None)
|
||||
Node names of G and H can be changed by specifying the tuple
|
||||
rename=('G-','H-') (for example). Node "u" in G is then renamed
|
||||
"G-u" and "v" in H is renamed "H-v".
|
||||
|
||||
Returns
|
||||
-------
|
||||
U : The full join graph with the same type as G.
|
||||
|
||||
Notes
|
||||
-----
|
||||
It is recommended that G and H be either both directed or both undirected.
|
||||
|
||||
If G is directed, then edges from G to H are added as well as from H to G.
|
||||
|
||||
Note that full_join() does not produce parallel edges for MultiGraphs.
|
||||
|
||||
The full join operation of graphs G and H is the same as getting
|
||||
their complement, performing a disjoint union, and finally getting
|
||||
the complement of the resulting graph.
|
||||
|
||||
Graph, edge, and node attributes are propagated from G and H
|
||||
to the union graph. If a graph attribute is present in both
|
||||
G and H the value from H is used.
|
||||
|
||||
See Also
|
||||
--------
|
||||
union
|
||||
disjoint_union
|
||||
"""
|
||||
R = union(G, H, rename)
|
||||
|
||||
def add_prefix(graph, prefix):
|
||||
if prefix is None:
|
||||
return graph
|
||||
|
||||
def label(x):
|
||||
if isinstance(x, str):
|
||||
name = prefix + x
|
||||
else:
|
||||
name = prefix + repr(x)
|
||||
return name
|
||||
|
||||
return nx.relabel_nodes(graph, label)
|
||||
|
||||
G = add_prefix(G, rename[0])
|
||||
H = add_prefix(H, rename[1])
|
||||
|
||||
for i in G:
|
||||
for j in H:
|
||||
R.add_edge(i, j)
|
||||
if R.is_directed():
|
||||
for i in H:
|
||||
for j in G:
|
||||
R.add_edge(i, j)
|
||||
|
||||
return R
|
461
venv/Lib/site-packages/networkx/algorithms/operators/product.py
Normal file
461
venv/Lib/site-packages/networkx/algorithms/operators/product.py
Normal file
|
@ -0,0 +1,461 @@
|
|||
"""
|
||||
Graph products.
|
||||
"""
|
||||
from itertools import product
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"tensor_product",
|
||||
"cartesian_product",
|
||||
"lexicographic_product",
|
||||
"strong_product",
|
||||
"power",
|
||||
"rooted_product",
|
||||
]
|
||||
|
||||
|
||||
def _dict_product(d1, d2):
|
||||
return {k: (d1.get(k), d2.get(k)) for k in set(d1) | set(d2)}
|
||||
|
||||
|
||||
# Generators for producting graph products
|
||||
def _node_product(G, H):
|
||||
for u, v in product(G, H):
|
||||
yield ((u, v), _dict_product(G.nodes[u], H.nodes[v]))
|
||||
|
||||
|
||||
def _directed_edges_cross_edges(G, H):
|
||||
if not G.is_multigraph() and not H.is_multigraph():
|
||||
for u, v, c in G.edges(data=True):
|
||||
for x, y, d in H.edges(data=True):
|
||||
yield (u, x), (v, y), _dict_product(c, d)
|
||||
if not G.is_multigraph() and H.is_multigraph():
|
||||
for u, v, c in G.edges(data=True):
|
||||
for x, y, k, d in H.edges(data=True, keys=True):
|
||||
yield (u, x), (v, y), k, _dict_product(c, d)
|
||||
if G.is_multigraph() and not H.is_multigraph():
|
||||
for u, v, k, c in G.edges(data=True, keys=True):
|
||||
for x, y, d in H.edges(data=True):
|
||||
yield (u, x), (v, y), k, _dict_product(c, d)
|
||||
if G.is_multigraph() and H.is_multigraph():
|
||||
for u, v, j, c in G.edges(data=True, keys=True):
|
||||
for x, y, k, d in H.edges(data=True, keys=True):
|
||||
yield (u, x), (v, y), (j, k), _dict_product(c, d)
|
||||
|
||||
|
||||
def _undirected_edges_cross_edges(G, H):
|
||||
if not G.is_multigraph() and not H.is_multigraph():
|
||||
for u, v, c in G.edges(data=True):
|
||||
for x, y, d in H.edges(data=True):
|
||||
yield (v, x), (u, y), _dict_product(c, d)
|
||||
if not G.is_multigraph() and H.is_multigraph():
|
||||
for u, v, c in G.edges(data=True):
|
||||
for x, y, k, d in H.edges(data=True, keys=True):
|
||||
yield (v, x), (u, y), k, _dict_product(c, d)
|
||||
if G.is_multigraph() and not H.is_multigraph():
|
||||
for u, v, k, c in G.edges(data=True, keys=True):
|
||||
for x, y, d in H.edges(data=True):
|
||||
yield (v, x), (u, y), k, _dict_product(c, d)
|
||||
if G.is_multigraph() and H.is_multigraph():
|
||||
for u, v, j, c in G.edges(data=True, keys=True):
|
||||
for x, y, k, d in H.edges(data=True, keys=True):
|
||||
yield (v, x), (u, y), (j, k), _dict_product(c, d)
|
||||
|
||||
|
||||
def _edges_cross_nodes(G, H):
|
||||
if G.is_multigraph():
|
||||
for u, v, k, d in G.edges(data=True, keys=True):
|
||||
for x in H:
|
||||
yield (u, x), (v, x), k, d
|
||||
else:
|
||||
for u, v, d in G.edges(data=True):
|
||||
for x in H:
|
||||
if H.is_multigraph():
|
||||
yield (u, x), (v, x), None, d
|
||||
else:
|
||||
yield (u, x), (v, x), d
|
||||
|
||||
|
||||
def _nodes_cross_edges(G, H):
|
||||
if H.is_multigraph():
|
||||
for x in G:
|
||||
for u, v, k, d in H.edges(data=True, keys=True):
|
||||
yield (x, u), (x, v), k, d
|
||||
else:
|
||||
for x in G:
|
||||
for u, v, d in H.edges(data=True):
|
||||
if G.is_multigraph():
|
||||
yield (x, u), (x, v), None, d
|
||||
else:
|
||||
yield (x, u), (x, v), d
|
||||
|
||||
|
||||
def _edges_cross_nodes_and_nodes(G, H):
|
||||
if G.is_multigraph():
|
||||
for u, v, k, d in G.edges(data=True, keys=True):
|
||||
for x in H:
|
||||
for y in H:
|
||||
yield (u, x), (v, y), k, d
|
||||
else:
|
||||
for u, v, d in G.edges(data=True):
|
||||
for x in H:
|
||||
for y in H:
|
||||
if H.is_multigraph():
|
||||
yield (u, x), (v, y), None, d
|
||||
else:
|
||||
yield (u, x), (v, y), d
|
||||
|
||||
|
||||
def _init_product_graph(G, H):
|
||||
if not G.is_directed() == H.is_directed():
|
||||
msg = "G and H must be both directed or both undirected"
|
||||
raise nx.NetworkXError(msg)
|
||||
if G.is_multigraph() or H.is_multigraph():
|
||||
GH = nx.MultiGraph()
|
||||
else:
|
||||
GH = nx.Graph()
|
||||
if G.is_directed():
|
||||
GH = GH.to_directed()
|
||||
return GH
|
||||
|
||||
|
||||
def tensor_product(G, H):
|
||||
r"""Returns the tensor product of G and H.
|
||||
|
||||
The tensor product $P$ of the graphs $G$ and $H$ has a node set that
|
||||
is the tensor product of the node sets, $V(P)=V(G) \times V(H)$.
|
||||
$P$ has an edge $((u,v), (x,y))$ if and only if $(u,x)$ is an edge in $G$
|
||||
and $(v,y)$ is an edge in $H$.
|
||||
|
||||
Tensor product is sometimes also referred to as the categorical product,
|
||||
direct product, cardinal product or conjunction.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H: graphs
|
||||
Networkx graphs.
|
||||
|
||||
Returns
|
||||
-------
|
||||
P: NetworkX graph
|
||||
The tensor product of G and H. P will be a multi-graph if either G
|
||||
or H is a multi-graph, will be a directed if G and H are directed,
|
||||
and undirected if G and H are undirected.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If G and H are not both directed or both undirected.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Node attributes in P are two-tuple of the G and H node attributes.
|
||||
Missing attributes are assigned None.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> H = nx.Graph()
|
||||
>>> G.add_node(0, a1=True)
|
||||
>>> H.add_node("a", a2="Spam")
|
||||
>>> P = nx.tensor_product(G, H)
|
||||
>>> list(P)
|
||||
[(0, 'a')]
|
||||
|
||||
Edge attributes and edge keys (for multigraphs) are also copied to the
|
||||
new product graph
|
||||
"""
|
||||
GH = _init_product_graph(G, H)
|
||||
GH.add_nodes_from(_node_product(G, H))
|
||||
GH.add_edges_from(_directed_edges_cross_edges(G, H))
|
||||
if not GH.is_directed():
|
||||
GH.add_edges_from(_undirected_edges_cross_edges(G, H))
|
||||
return GH
|
||||
|
||||
|
||||
def cartesian_product(G, H):
|
||||
r"""Returns the Cartesian product of G and H.
|
||||
|
||||
The Cartesian product $P$ of the graphs $G$ and $H$ has a node set that
|
||||
is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
|
||||
$P$ has an edge $((u,v),(x,y))$ if and only if either $u$ is equal to $x$
|
||||
and both $v$ and $y$ are adjacent in $H$ or if $v$ is equal to $y$ and
|
||||
both $u$ and $x$ are adjacent in $G$.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H: graphs
|
||||
Networkx graphs.
|
||||
|
||||
Returns
|
||||
-------
|
||||
P: NetworkX graph
|
||||
The Cartesian product of G and H. P will be a multi-graph if either G
|
||||
or H is a multi-graph. Will be a directed if G and H are directed,
|
||||
and undirected if G and H are undirected.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If G and H are not both directed or both undirected.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Node attributes in P are two-tuple of the G and H node attributes.
|
||||
Missing attributes are assigned None.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> H = nx.Graph()
|
||||
>>> G.add_node(0, a1=True)
|
||||
>>> H.add_node("a", a2="Spam")
|
||||
>>> P = nx.cartesian_product(G, H)
|
||||
>>> list(P)
|
||||
[(0, 'a')]
|
||||
|
||||
Edge attributes and edge keys (for multigraphs) are also copied to the
|
||||
new product graph
|
||||
"""
|
||||
GH = _init_product_graph(G, H)
|
||||
GH.add_nodes_from(_node_product(G, H))
|
||||
GH.add_edges_from(_edges_cross_nodes(G, H))
|
||||
GH.add_edges_from(_nodes_cross_edges(G, H))
|
||||
return GH
|
||||
|
||||
|
||||
def lexicographic_product(G, H):
|
||||
r"""Returns the lexicographic product of G and H.
|
||||
|
||||
The lexicographical product $P$ of the graphs $G$ and $H$ has a node set
|
||||
that is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
|
||||
$P$ has an edge $((u,v), (x,y))$ if and only if $(u,v)$ is an edge in $G$
|
||||
or $u==v$ and $(x,y)$ is an edge in $H$.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H: graphs
|
||||
Networkx graphs.
|
||||
|
||||
Returns
|
||||
-------
|
||||
P: NetworkX graph
|
||||
The Cartesian product of G and H. P will be a multi-graph if either G
|
||||
or H is a multi-graph. Will be a directed if G and H are directed,
|
||||
and undirected if G and H are undirected.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If G and H are not both directed or both undirected.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Node attributes in P are two-tuple of the G and H node attributes.
|
||||
Missing attributes are assigned None.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> H = nx.Graph()
|
||||
>>> G.add_node(0, a1=True)
|
||||
>>> H.add_node("a", a2="Spam")
|
||||
>>> P = nx.lexicographic_product(G, H)
|
||||
>>> list(P)
|
||||
[(0, 'a')]
|
||||
|
||||
Edge attributes and edge keys (for multigraphs) are also copied to the
|
||||
new product graph
|
||||
"""
|
||||
GH = _init_product_graph(G, H)
|
||||
GH.add_nodes_from(_node_product(G, H))
|
||||
# Edges in G regardless of H designation
|
||||
GH.add_edges_from(_edges_cross_nodes_and_nodes(G, H))
|
||||
# For each x in G, only if there is an edge in H
|
||||
GH.add_edges_from(_nodes_cross_edges(G, H))
|
||||
return GH
|
||||
|
||||
|
||||
def strong_product(G, H):
|
||||
r"""Returns the strong product of G and H.
|
||||
|
||||
The strong product $P$ of the graphs $G$ and $H$ has a node set that
|
||||
is the Cartesian product of the node sets, $V(P)=V(G) \times V(H)$.
|
||||
$P$ has an edge $((u,v), (x,y))$ if and only if
|
||||
$u==v$ and $(x,y)$ is an edge in $H$, or
|
||||
$x==y$ and $(u,v)$ is an edge in $G$, or
|
||||
$(u,v)$ is an edge in $G$ and $(x,y)$ is an edge in $H$.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G, H: graphs
|
||||
Networkx graphs.
|
||||
|
||||
Returns
|
||||
-------
|
||||
P: NetworkX graph
|
||||
The Cartesian product of G and H. P will be a multi-graph if either G
|
||||
or H is a multi-graph. Will be a directed if G and H are directed,
|
||||
and undirected if G and H are undirected.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If G and H are not both directed or both undirected.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Node attributes in P are two-tuple of the G and H node attributes.
|
||||
Missing attributes are assigned None.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph()
|
||||
>>> H = nx.Graph()
|
||||
>>> G.add_node(0, a1=True)
|
||||
>>> H.add_node("a", a2="Spam")
|
||||
>>> P = nx.strong_product(G, H)
|
||||
>>> list(P)
|
||||
[(0, 'a')]
|
||||
|
||||
Edge attributes and edge keys (for multigraphs) are also copied to the
|
||||
new product graph
|
||||
"""
|
||||
GH = _init_product_graph(G, H)
|
||||
GH.add_nodes_from(_node_product(G, H))
|
||||
GH.add_edges_from(_nodes_cross_edges(G, H))
|
||||
GH.add_edges_from(_edges_cross_nodes(G, H))
|
||||
GH.add_edges_from(_directed_edges_cross_edges(G, H))
|
||||
if not GH.is_directed():
|
||||
GH.add_edges_from(_undirected_edges_cross_edges(G, H))
|
||||
return GH
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
@not_implemented_for("multigraph")
|
||||
def power(G, k):
|
||||
"""Returns the specified power of a graph.
|
||||
|
||||
The $k$th power of a simple graph $G$, denoted $G^k$, is a
|
||||
graph on the same set of nodes in which two distinct nodes $u$ and
|
||||
$v$ are adjacent in $G^k$ if and only if the shortest path
|
||||
distance between $u$ and $v$ in $G$ is at most $k$.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX simple graph object.
|
||||
|
||||
k : positive integer
|
||||
The power to which to raise the graph `G`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NetworkX simple graph
|
||||
`G` to the power `k`.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If the exponent `k` is not positive.
|
||||
|
||||
NetworkXNotImplemented
|
||||
If `G` is not a simple graph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The number of edges will never decrease when taking successive
|
||||
powers:
|
||||
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> list(nx.power(G, 2).edges)
|
||||
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]
|
||||
>>> list(nx.power(G, 3).edges)
|
||||
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
|
||||
|
||||
The `k`th power of a cycle graph on *n* nodes is the complete graph
|
||||
on *n* nodes, if `k` is at least ``n // 2``:
|
||||
|
||||
>>> G = nx.cycle_graph(5)
|
||||
>>> H = nx.complete_graph(5)
|
||||
>>> nx.is_isomorphic(nx.power(G, 2), H)
|
||||
True
|
||||
>>> G = nx.cycle_graph(8)
|
||||
>>> H = nx.complete_graph(8)
|
||||
>>> nx.is_isomorphic(nx.power(G, 4), H)
|
||||
True
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] J. A. Bondy, U. S. R. Murty, *Graph Theory*. Springer, 2008.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This definition of "power graph" comes from Exercise 3.1.6 of
|
||||
*Graph Theory* by Bondy and Murty [1]_.
|
||||
|
||||
"""
|
||||
if k <= 0:
|
||||
raise ValueError("k must be a positive integer")
|
||||
H = nx.Graph()
|
||||
H.add_nodes_from(G)
|
||||
# update BFS code to ignore self loops.
|
||||
for n in G:
|
||||
seen = {} # level (number of hops) when seen in BFS
|
||||
level = 1 # the current level
|
||||
nextlevel = G[n]
|
||||
while nextlevel:
|
||||
thislevel = nextlevel # advance to next level
|
||||
nextlevel = {} # and start a new list (fringe)
|
||||
for v in thislevel:
|
||||
if v == n: # avoid self loop
|
||||
continue
|
||||
if v not in seen:
|
||||
seen[v] = level # set the level of vertex v
|
||||
nextlevel.update(G[v]) # add neighbors of v
|
||||
if k <= level:
|
||||
break
|
||||
level += 1
|
||||
H.add_edges_from((n, nbr) for nbr in seen)
|
||||
return H
|
||||
|
||||
|
||||
@not_implemented_for("multigraph")
|
||||
def rooted_product(G, H, root):
|
||||
""" Return the rooted product of graphs G and H rooted at root in H.
|
||||
|
||||
A new graph is constructed representing the rooted product of
|
||||
the inputted graphs, G and H, with a root in H.
|
||||
A rooted product duplicates H for each nodes in G with the root
|
||||
of H corresponding to the node in G. Nodes are renamed as the direct
|
||||
product of G and H. The result is a subgraph of the cartesian product.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G,H : graph
|
||||
A NetworkX graph
|
||||
root : node
|
||||
A node in H
|
||||
|
||||
Returns
|
||||
-------
|
||||
R : The rooted product of G and H with a specified root in H
|
||||
|
||||
Notes
|
||||
-----
|
||||
The nodes of R are the Cartesian Product of the nodes of G and H.
|
||||
The nodes of G and H are not relabeled.
|
||||
"""
|
||||
if root not in H:
|
||||
raise nx.NetworkXError("root must be a vertex in H")
|
||||
|
||||
R = nx.Graph()
|
||||
R.add_nodes_from(product(G, H))
|
||||
|
||||
R.add_edges_from(((e[0], root), (e[1], root)) for e in G.edges())
|
||||
R.add_edges_from(((g, e[0]), (g, e[1])) for g in G for e in H.edges())
|
||||
|
||||
return R
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,248 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal
|
||||
|
||||
|
||||
def test_union_all_attributes():
|
||||
g = nx.Graph()
|
||||
g.add_node(0, x=4)
|
||||
g.add_node(1, x=5)
|
||||
g.add_edge(0, 1, size=5)
|
||||
g.graph["name"] = "g"
|
||||
|
||||
h = g.copy()
|
||||
h.graph["name"] = "h"
|
||||
h.graph["attr"] = "attr"
|
||||
h.nodes[0]["x"] = 7
|
||||
|
||||
j = g.copy()
|
||||
j.graph["name"] = "j"
|
||||
j.graph["attr"] = "attr"
|
||||
j.nodes[0]["x"] = 7
|
||||
|
||||
ghj = nx.union_all([g, h, j], rename=("g", "h", "j"))
|
||||
assert set(ghj.nodes()) == {"h0", "h1", "g0", "g1", "j0", "j1"}
|
||||
for n in ghj:
|
||||
graph, node = n
|
||||
assert ghj.nodes[n] == eval(graph).nodes[int(node)]
|
||||
|
||||
assert ghj.graph["attr"] == "attr"
|
||||
assert ghj.graph["name"] == "j" # j graph attributes take precendent
|
||||
|
||||
|
||||
def test_intersection_all():
|
||||
G = nx.Graph()
|
||||
H = nx.Graph()
|
||||
R = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 3)
|
||||
H.add_nodes_from([1, 2, 3, 4])
|
||||
H.add_edge(2, 3)
|
||||
H.add_edge(3, 4)
|
||||
R.add_nodes_from([1, 2, 3, 4])
|
||||
R.add_edge(2, 3)
|
||||
R.add_edge(4, 1)
|
||||
I = nx.intersection_all([G, H, R])
|
||||
assert set(I.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(I.edges()) == [(2, 3)]
|
||||
|
||||
|
||||
def test_intersection_all_attributes():
|
||||
g = nx.Graph()
|
||||
g.add_node(0, x=4)
|
||||
g.add_node(1, x=5)
|
||||
g.add_edge(0, 1, size=5)
|
||||
g.graph["name"] = "g"
|
||||
|
||||
h = g.copy()
|
||||
h.graph["name"] = "h"
|
||||
h.graph["attr"] = "attr"
|
||||
h.nodes[0]["x"] = 7
|
||||
|
||||
gh = nx.intersection_all([g, h])
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == sorted(g.edges())
|
||||
|
||||
h.remove_node(0)
|
||||
pytest.raises(nx.NetworkXError, nx.intersection, g, h)
|
||||
|
||||
|
||||
def test_intersection_all_multigraph_attributes():
|
||||
g = nx.MultiGraph()
|
||||
g.add_edge(0, 1, key=0)
|
||||
g.add_edge(0, 1, key=1)
|
||||
g.add_edge(0, 1, key=2)
|
||||
h = nx.MultiGraph()
|
||||
h.add_edge(0, 1, key=0)
|
||||
h.add_edge(0, 1, key=3)
|
||||
gh = nx.intersection_all([g, h])
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == [(0, 1)]
|
||||
assert sorted(gh.edges(keys=True)) == [(0, 1, 0)]
|
||||
|
||||
|
||||
def test_union_all_and_compose_all():
|
||||
K3 = nx.complete_graph(3)
|
||||
P3 = nx.path_graph(3)
|
||||
|
||||
G1 = nx.DiGraph()
|
||||
G1.add_edge("A", "B")
|
||||
G1.add_edge("A", "C")
|
||||
G1.add_edge("A", "D")
|
||||
G2 = nx.DiGraph()
|
||||
G2.add_edge("1", "2")
|
||||
G2.add_edge("1", "3")
|
||||
G2.add_edge("1", "4")
|
||||
|
||||
G = nx.union_all([G1, G2])
|
||||
H = nx.compose_all([G1, G2])
|
||||
assert_edges_equal(G.edges(), H.edges())
|
||||
assert not G.has_edge("A", "1")
|
||||
pytest.raises(nx.NetworkXError, nx.union, K3, P3)
|
||||
H1 = nx.union_all([H, G1], rename=("H", "G1"))
|
||||
assert sorted(H1.nodes()) == [
|
||||
"G1A",
|
||||
"G1B",
|
||||
"G1C",
|
||||
"G1D",
|
||||
"H1",
|
||||
"H2",
|
||||
"H3",
|
||||
"H4",
|
||||
"HA",
|
||||
"HB",
|
||||
"HC",
|
||||
"HD",
|
||||
]
|
||||
|
||||
H2 = nx.union_all([H, G2], rename=("H", ""))
|
||||
assert sorted(H2.nodes()) == [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"H1",
|
||||
"H2",
|
||||
"H3",
|
||||
"H4",
|
||||
"HA",
|
||||
"HB",
|
||||
"HC",
|
||||
"HD",
|
||||
]
|
||||
|
||||
assert not H1.has_edge("NB", "NA")
|
||||
|
||||
G = nx.compose_all([G, G])
|
||||
assert_edges_equal(G.edges(), H.edges())
|
||||
|
||||
G2 = nx.union_all([G2, G2], rename=("", "copy"))
|
||||
assert sorted(G2.nodes()) == [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"copy1",
|
||||
"copy2",
|
||||
"copy3",
|
||||
"copy4",
|
||||
]
|
||||
|
||||
assert sorted(G2.neighbors("copy4")) == []
|
||||
assert sorted(G2.neighbors("copy1")) == ["copy2", "copy3", "copy4"]
|
||||
assert len(G) == 8
|
||||
assert nx.number_of_edges(G) == 6
|
||||
|
||||
E = nx.disjoint_union_all([G, G])
|
||||
assert len(E) == 16
|
||||
assert nx.number_of_edges(E) == 12
|
||||
|
||||
E = nx.disjoint_union_all([G1, G2])
|
||||
assert sorted(E.nodes()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
G1 = nx.DiGraph()
|
||||
G1.add_edge("A", "B")
|
||||
G2 = nx.DiGraph()
|
||||
G2.add_edge(1, 2)
|
||||
G3 = nx.DiGraph()
|
||||
G3.add_edge(11, 22)
|
||||
G4 = nx.union_all([G1, G2, G3], rename=("G1", "G2", "G3"))
|
||||
assert sorted(G4.nodes()) == ["G1A", "G1B", "G21", "G22", "G311", "G322"]
|
||||
|
||||
|
||||
def test_union_all_multigraph():
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key=0)
|
||||
G.add_edge(1, 2, key=1)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(3, 4, key=0)
|
||||
H.add_edge(3, 4, key=1)
|
||||
GH = nx.union_all([G, H])
|
||||
assert set(GH) == set(G) | set(H)
|
||||
assert set(GH.edges(keys=True)) == set(G.edges(keys=True)) | set(H.edges(keys=True))
|
||||
|
||||
|
||||
def test_input_output():
|
||||
l = [nx.Graph([(1, 2)]), nx.Graph([(3, 4)])]
|
||||
U = nx.disjoint_union_all(l)
|
||||
assert len(l) == 2
|
||||
C = nx.compose_all(l)
|
||||
assert len(l) == 2
|
||||
l = [nx.Graph([(1, 2)]), nx.Graph([(1, 2)])]
|
||||
R = nx.intersection_all(l)
|
||||
assert len(l) == 2
|
||||
|
||||
|
||||
def test_mixed_type_union():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
H = nx.MultiGraph()
|
||||
I = nx.Graph()
|
||||
U = nx.union_all([G, H, I])
|
||||
|
||||
|
||||
def test_mixed_type_disjoint_union():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
H = nx.MultiGraph()
|
||||
I = nx.Graph()
|
||||
U = nx.disjoint_union_all([G, H, I])
|
||||
|
||||
|
||||
def test_mixed_type_intersection():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
H = nx.MultiGraph()
|
||||
I = nx.Graph()
|
||||
U = nx.intersection_all([G, H, I])
|
||||
|
||||
|
||||
def test_mixed_type_compose():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.Graph()
|
||||
H = nx.MultiGraph()
|
||||
I = nx.Graph()
|
||||
U = nx.compose_all([G, H, I])
|
||||
|
||||
|
||||
def test_empty_union():
|
||||
with pytest.raises(ValueError):
|
||||
nx.union_all([])
|
||||
|
||||
|
||||
def test_empty_disjoint_union():
|
||||
with pytest.raises(ValueError):
|
||||
nx.disjoint_union_all([])
|
||||
|
||||
|
||||
def test_empty_compose_all():
|
||||
with pytest.raises(ValueError):
|
||||
nx.compose_all([])
|
||||
|
||||
|
||||
def test_empty_intersection_all():
|
||||
with pytest.raises(ValueError):
|
||||
nx.intersection_all([])
|
|
@ -0,0 +1,403 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal
|
||||
|
||||
|
||||
def test_union_attributes():
|
||||
g = nx.Graph()
|
||||
g.add_node(0, x=4)
|
||||
g.add_node(1, x=5)
|
||||
g.add_edge(0, 1, size=5)
|
||||
g.graph["name"] = "g"
|
||||
|
||||
h = g.copy()
|
||||
h.graph["name"] = "h"
|
||||
h.graph["attr"] = "attr"
|
||||
h.nodes[0]["x"] = 7
|
||||
|
||||
gh = nx.union(g, h, rename=("g", "h"))
|
||||
assert set(gh.nodes()) == {"h0", "h1", "g0", "g1"}
|
||||
for n in gh:
|
||||
graph, node = n
|
||||
assert gh.nodes[n] == eval(graph).nodes[int(node)]
|
||||
|
||||
assert gh.graph["attr"] == "attr"
|
||||
assert gh.graph["name"] == "h" # h graph attributes take precendent
|
||||
|
||||
|
||||
def test_intersection():
|
||||
G = nx.Graph()
|
||||
H = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 3)
|
||||
H.add_nodes_from([1, 2, 3, 4])
|
||||
H.add_edge(2, 3)
|
||||
H.add_edge(3, 4)
|
||||
I = nx.intersection(G, H)
|
||||
assert set(I.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(I.edges()) == [(2, 3)]
|
||||
|
||||
|
||||
def test_intersection_attributes():
|
||||
g = nx.Graph()
|
||||
g.add_node(0, x=4)
|
||||
g.add_node(1, x=5)
|
||||
g.add_edge(0, 1, size=5)
|
||||
g.graph["name"] = "g"
|
||||
|
||||
h = g.copy()
|
||||
h.graph["name"] = "h"
|
||||
h.graph["attr"] = "attr"
|
||||
h.nodes[0]["x"] = 7
|
||||
|
||||
gh = nx.intersection(g, h)
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == sorted(g.edges())
|
||||
|
||||
h.remove_node(0)
|
||||
pytest.raises(nx.NetworkXError, nx.intersection, g, h)
|
||||
|
||||
|
||||
def test_intersection_multigraph_attributes():
|
||||
g = nx.MultiGraph()
|
||||
g.add_edge(0, 1, key=0)
|
||||
g.add_edge(0, 1, key=1)
|
||||
g.add_edge(0, 1, key=2)
|
||||
h = nx.MultiGraph()
|
||||
h.add_edge(0, 1, key=0)
|
||||
h.add_edge(0, 1, key=3)
|
||||
gh = nx.intersection(g, h)
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == [(0, 1)]
|
||||
assert sorted(gh.edges(keys=True)) == [(0, 1, 0)]
|
||||
|
||||
|
||||
def test_difference():
|
||||
G = nx.Graph()
|
||||
H = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 3)
|
||||
H.add_nodes_from([1, 2, 3, 4])
|
||||
H.add_edge(2, 3)
|
||||
H.add_edge(3, 4)
|
||||
D = nx.difference(G, H)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == [(1, 2)]
|
||||
D = nx.difference(H, G)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == [(3, 4)]
|
||||
D = nx.symmetric_difference(G, H)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == [(1, 2), (3, 4)]
|
||||
|
||||
|
||||
def test_difference2():
|
||||
G = nx.Graph()
|
||||
H = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
H.add_nodes_from([1, 2, 3, 4])
|
||||
G.add_edge(1, 2)
|
||||
H.add_edge(1, 2)
|
||||
G.add_edge(2, 3)
|
||||
D = nx.difference(G, H)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == [(2, 3)]
|
||||
D = nx.difference(H, G)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == []
|
||||
H.add_edge(3, 4)
|
||||
D = nx.difference(H, G)
|
||||
assert set(D.nodes()) == {1, 2, 3, 4}
|
||||
assert sorted(D.edges()) == [(3, 4)]
|
||||
|
||||
|
||||
def test_difference_attributes():
|
||||
g = nx.Graph()
|
||||
g.add_node(0, x=4)
|
||||
g.add_node(1, x=5)
|
||||
g.add_edge(0, 1, size=5)
|
||||
g.graph["name"] = "g"
|
||||
|
||||
h = g.copy()
|
||||
h.graph["name"] = "h"
|
||||
h.graph["attr"] = "attr"
|
||||
h.nodes[0]["x"] = 7
|
||||
|
||||
gh = nx.difference(g, h)
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == []
|
||||
|
||||
h.remove_node(0)
|
||||
pytest.raises(nx.NetworkXError, nx.intersection, g, h)
|
||||
|
||||
|
||||
def test_difference_multigraph_attributes():
|
||||
g = nx.MultiGraph()
|
||||
g.add_edge(0, 1, key=0)
|
||||
g.add_edge(0, 1, key=1)
|
||||
g.add_edge(0, 1, key=2)
|
||||
h = nx.MultiGraph()
|
||||
h.add_edge(0, 1, key=0)
|
||||
h.add_edge(0, 1, key=3)
|
||||
gh = nx.difference(g, h)
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == [(0, 1), (0, 1)]
|
||||
assert sorted(gh.edges(keys=True)) == [(0, 1, 1), (0, 1, 2)]
|
||||
|
||||
|
||||
def test_difference_raise():
|
||||
G = nx.path_graph(4)
|
||||
H = nx.path_graph(3)
|
||||
pytest.raises(nx.NetworkXError, nx.difference, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.symmetric_difference, G, H)
|
||||
|
||||
|
||||
def test_symmetric_difference_multigraph():
|
||||
g = nx.MultiGraph()
|
||||
g.add_edge(0, 1, key=0)
|
||||
g.add_edge(0, 1, key=1)
|
||||
g.add_edge(0, 1, key=2)
|
||||
h = nx.MultiGraph()
|
||||
h.add_edge(0, 1, key=0)
|
||||
h.add_edge(0, 1, key=3)
|
||||
gh = nx.symmetric_difference(g, h)
|
||||
assert set(gh.nodes()) == set(g.nodes())
|
||||
assert set(gh.nodes()) == set(h.nodes())
|
||||
assert sorted(gh.edges()) == 3 * [(0, 1)]
|
||||
assert sorted(sorted(e) for e in gh.edges(keys=True)) == [
|
||||
[0, 1, 1],
|
||||
[0, 1, 2],
|
||||
[0, 1, 3],
|
||||
]
|
||||
|
||||
|
||||
def test_union_and_compose():
|
||||
K3 = nx.complete_graph(3)
|
||||
P3 = nx.path_graph(3)
|
||||
|
||||
G1 = nx.DiGraph()
|
||||
G1.add_edge("A", "B")
|
||||
G1.add_edge("A", "C")
|
||||
G1.add_edge("A", "D")
|
||||
G2 = nx.DiGraph()
|
||||
G2.add_edge("1", "2")
|
||||
G2.add_edge("1", "3")
|
||||
G2.add_edge("1", "4")
|
||||
|
||||
G = nx.union(G1, G2)
|
||||
H = nx.compose(G1, G2)
|
||||
assert_edges_equal(G.edges(), H.edges())
|
||||
assert not G.has_edge("A", 1)
|
||||
pytest.raises(nx.NetworkXError, nx.union, K3, P3)
|
||||
H1 = nx.union(H, G1, rename=("H", "G1"))
|
||||
assert sorted(H1.nodes()) == [
|
||||
"G1A",
|
||||
"G1B",
|
||||
"G1C",
|
||||
"G1D",
|
||||
"H1",
|
||||
"H2",
|
||||
"H3",
|
||||
"H4",
|
||||
"HA",
|
||||
"HB",
|
||||
"HC",
|
||||
"HD",
|
||||
]
|
||||
|
||||
H2 = nx.union(H, G2, rename=("H", ""))
|
||||
assert sorted(H2.nodes()) == [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"H1",
|
||||
"H2",
|
||||
"H3",
|
||||
"H4",
|
||||
"HA",
|
||||
"HB",
|
||||
"HC",
|
||||
"HD",
|
||||
]
|
||||
|
||||
assert not H1.has_edge("NB", "NA")
|
||||
|
||||
G = nx.compose(G, G)
|
||||
assert_edges_equal(G.edges(), H.edges())
|
||||
|
||||
G2 = nx.union(G2, G2, rename=("", "copy"))
|
||||
assert sorted(G2.nodes()) == [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"copy1",
|
||||
"copy2",
|
||||
"copy3",
|
||||
"copy4",
|
||||
]
|
||||
|
||||
assert sorted(G2.neighbors("copy4")) == []
|
||||
assert sorted(G2.neighbors("copy1")) == ["copy2", "copy3", "copy4"]
|
||||
assert len(G) == 8
|
||||
assert nx.number_of_edges(G) == 6
|
||||
|
||||
E = nx.disjoint_union(G, G)
|
||||
assert len(E) == 16
|
||||
assert nx.number_of_edges(E) == 12
|
||||
|
||||
E = nx.disjoint_union(G1, G2)
|
||||
assert sorted(E.nodes()) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
G = nx.Graph()
|
||||
H = nx.Graph()
|
||||
G.add_nodes_from([(1, {"a1": 1})])
|
||||
H.add_nodes_from([(1, {"b1": 1})])
|
||||
R = nx.compose(G, H)
|
||||
assert R.nodes == {1: {"a1": 1, "b1": 1}}
|
||||
|
||||
|
||||
def test_union_multigraph():
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key=0)
|
||||
G.add_edge(1, 2, key=1)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(3, 4, key=0)
|
||||
H.add_edge(3, 4, key=1)
|
||||
GH = nx.union(G, H)
|
||||
assert set(GH) == set(G) | set(H)
|
||||
assert set(GH.edges(keys=True)) == set(G.edges(keys=True)) | set(H.edges(keys=True))
|
||||
|
||||
|
||||
def test_disjoint_union_multigraph():
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(0, 1, key=0)
|
||||
G.add_edge(0, 1, key=1)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(2, 3, key=0)
|
||||
H.add_edge(2, 3, key=1)
|
||||
GH = nx.disjoint_union(G, H)
|
||||
assert set(GH) == set(G) | set(H)
|
||||
assert set(GH.edges(keys=True)) == set(G.edges(keys=True)) | set(H.edges(keys=True))
|
||||
|
||||
|
||||
def test_compose_multigraph():
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key=0)
|
||||
G.add_edge(1, 2, key=1)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(3, 4, key=0)
|
||||
H.add_edge(3, 4, key=1)
|
||||
GH = nx.compose(G, H)
|
||||
assert set(GH) == set(G) | set(H)
|
||||
assert set(GH.edges(keys=True)) == set(G.edges(keys=True)) | set(H.edges(keys=True))
|
||||
H.add_edge(1, 2, key=2)
|
||||
GH = nx.compose(G, H)
|
||||
assert set(GH) == set(G) | set(H)
|
||||
assert set(GH.edges(keys=True)) == set(G.edges(keys=True)) | set(H.edges(keys=True))
|
||||
|
||||
|
||||
def test_full_join_graph():
|
||||
# Simple Graphs
|
||||
G = nx.Graph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
H = nx.Graph()
|
||||
H.add_edge(3, 4)
|
||||
|
||||
U = nx.full_join(G, H)
|
||||
assert set(U) == set(G) | set(H)
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H)
|
||||
|
||||
# Rename
|
||||
U = nx.full_join(G, H, rename=("g", "h"))
|
||||
assert set(U) == {"g0", "g1", "g2", "h3", "h4"}
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H)
|
||||
|
||||
# Rename graphs with string-like nodes
|
||||
G = nx.Graph()
|
||||
G.add_node("a")
|
||||
G.add_edge("b", "c")
|
||||
H = nx.Graph()
|
||||
H.add_edge("d", "e")
|
||||
|
||||
U = nx.full_join(G, H, rename=("g", "h"))
|
||||
assert set(U) == {"ga", "gb", "gc", "hd", "he"}
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H)
|
||||
|
||||
# DiGraphs
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
H = nx.DiGraph()
|
||||
H.add_edge(3, 4)
|
||||
|
||||
U = nx.full_join(G, H)
|
||||
assert set(U) == set(G) | set(H)
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H) * 2
|
||||
|
||||
# DiGraphs Rename
|
||||
U = nx.full_join(G, H, rename=("g", "h"))
|
||||
assert set(U) == {"g0", "g1", "g2", "h3", "h4"}
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H) * 2
|
||||
|
||||
|
||||
def test_full_join_multigraph():
|
||||
# MultiGraphs
|
||||
G = nx.MultiGraph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(3, 4)
|
||||
|
||||
U = nx.full_join(G, H)
|
||||
assert set(U) == set(G) | set(H)
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H)
|
||||
|
||||
# MultiGraphs rename
|
||||
U = nx.full_join(G, H, rename=("g", "h"))
|
||||
assert set(U) == {"g0", "g1", "g2", "h3", "h4"}
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H)
|
||||
|
||||
# MultiDiGraphs
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
H = nx.MultiDiGraph()
|
||||
H.add_edge(3, 4)
|
||||
|
||||
U = nx.full_join(G, H)
|
||||
assert set(U) == set(G) | set(H)
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H) * 2
|
||||
|
||||
# MultiDiGraphs rename
|
||||
U = nx.full_join(G, H, rename=("g", "h"))
|
||||
assert set(U) == {"g0", "g1", "g2", "h3", "h4"}
|
||||
assert len(U) == len(G) + len(H)
|
||||
assert len(U.edges()) == len(G.edges()) + len(H.edges()) + len(G) * len(H) * 2
|
||||
|
||||
|
||||
def test_mixed_type_union():
|
||||
G = nx.Graph()
|
||||
H = nx.MultiGraph()
|
||||
pytest.raises(nx.NetworkXError, nx.union, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.disjoint_union, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.intersection, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.difference, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.symmetric_difference, G, H)
|
||||
pytest.raises(nx.NetworkXError, nx.compose, G, H)
|
|
@ -0,0 +1,426 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal
|
||||
|
||||
|
||||
def test_tensor_product_raises():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
P = nx.tensor_product(nx.DiGraph(), nx.Graph())
|
||||
|
||||
|
||||
def test_tensor_product_null():
|
||||
null = nx.null_graph()
|
||||
empty10 = nx.empty_graph(10)
|
||||
K3 = nx.complete_graph(3)
|
||||
K10 = nx.complete_graph(10)
|
||||
P3 = nx.path_graph(3)
|
||||
P10 = nx.path_graph(10)
|
||||
# null graph
|
||||
G = nx.tensor_product(null, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
# null_graph X anything = null_graph and v.v.
|
||||
G = nx.tensor_product(null, empty10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(null, K3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(null, K10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(null, P3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(null, P10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(empty10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(K3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(K10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(P3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.tensor_product(P10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
|
||||
|
||||
def test_tensor_product_size():
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
K5 = nx.complete_graph(5)
|
||||
|
||||
G = nx.tensor_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.tensor_product(K3, K5)
|
||||
assert nx.number_of_nodes(G) == 3 * 5
|
||||
|
||||
|
||||
def test_tensor_product_combinations():
|
||||
# basic smoke test, more realistic tests would be useful
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.tensor_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.tensor_product(P5, nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.tensor_product(nx.MultiGraph(P5), K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.tensor_product(nx.MultiGraph(P5), nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
|
||||
G = nx.tensor_product(nx.DiGraph(P5), nx.DiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
|
||||
|
||||
def test_tensor_product_classic_result():
|
||||
K2 = nx.complete_graph(2)
|
||||
G = nx.petersen_graph()
|
||||
G = nx.tensor_product(G, K2)
|
||||
assert nx.is_isomorphic(G, nx.desargues_graph())
|
||||
|
||||
G = nx.cycle_graph(5)
|
||||
G = nx.tensor_product(G, K2)
|
||||
assert nx.is_isomorphic(G, nx.cycle_graph(10))
|
||||
|
||||
G = nx.tetrahedral_graph()
|
||||
G = nx.tensor_product(G, K2)
|
||||
assert nx.is_isomorphic(G, nx.cubical_graph())
|
||||
|
||||
|
||||
def test_tensor_product_random():
|
||||
G = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
H = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
GH = nx.tensor_product(G, H)
|
||||
|
||||
for (u_G, u_H) in GH.nodes():
|
||||
for (v_G, v_H) in GH.nodes():
|
||||
if H.has_edge(u_H, v_H) and G.has_edge(u_G, v_G):
|
||||
assert GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
else:
|
||||
assert not GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
|
||||
|
||||
def test_cartesian_product_multigraph():
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(1, 2, key=0)
|
||||
G.add_edge(1, 2, key=1)
|
||||
H = nx.MultiGraph()
|
||||
H.add_edge(3, 4, key=0)
|
||||
H.add_edge(3, 4, key=1)
|
||||
GH = nx.cartesian_product(G, H)
|
||||
assert set(GH) == {(1, 3), (2, 3), (2, 4), (1, 4)}
|
||||
assert {(frozenset([u, v]), k) for u, v, k in GH.edges(keys=True)} == {
|
||||
(frozenset([u, v]), k)
|
||||
for u, v, k in [
|
||||
((1, 3), (2, 3), 0),
|
||||
((1, 3), (2, 3), 1),
|
||||
((1, 3), (1, 4), 0),
|
||||
((1, 3), (1, 4), 1),
|
||||
((2, 3), (2, 4), 0),
|
||||
((2, 3), (2, 4), 1),
|
||||
((2, 4), (1, 4), 0),
|
||||
((2, 4), (1, 4), 1),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_cartesian_product_raises():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
P = nx.cartesian_product(nx.DiGraph(), nx.Graph())
|
||||
|
||||
|
||||
def test_cartesian_product_null():
|
||||
null = nx.null_graph()
|
||||
empty10 = nx.empty_graph(10)
|
||||
K3 = nx.complete_graph(3)
|
||||
K10 = nx.complete_graph(10)
|
||||
P3 = nx.path_graph(3)
|
||||
P10 = nx.path_graph(10)
|
||||
# null graph
|
||||
G = nx.cartesian_product(null, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
# null_graph X anything = null_graph and v.v.
|
||||
G = nx.cartesian_product(null, empty10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(null, K3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(null, K10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(null, P3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(null, P10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(empty10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(K3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(K10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(P3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.cartesian_product(P10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
|
||||
|
||||
def test_cartesian_product_size():
|
||||
# order(GXH)=order(G)*order(H)
|
||||
K5 = nx.complete_graph(5)
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.cartesian_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
assert nx.number_of_edges(G) == nx.number_of_edges(P5) * nx.number_of_nodes(
|
||||
K3
|
||||
) + nx.number_of_edges(K3) * nx.number_of_nodes(P5)
|
||||
G = nx.cartesian_product(K3, K5)
|
||||
assert nx.number_of_nodes(G) == 3 * 5
|
||||
assert nx.number_of_edges(G) == nx.number_of_edges(K5) * nx.number_of_nodes(
|
||||
K3
|
||||
) + nx.number_of_edges(K3) * nx.number_of_nodes(K5)
|
||||
|
||||
|
||||
def test_cartesian_product_classic():
|
||||
# test some classic product graphs
|
||||
P2 = nx.path_graph(2)
|
||||
P3 = nx.path_graph(3)
|
||||
# cube = 2-path X 2-path
|
||||
G = nx.cartesian_product(P2, P2)
|
||||
G = nx.cartesian_product(P2, G)
|
||||
assert nx.is_isomorphic(G, nx.cubical_graph())
|
||||
|
||||
# 3x3 grid
|
||||
G = nx.cartesian_product(P3, P3)
|
||||
assert nx.is_isomorphic(G, nx.grid_2d_graph(3, 3))
|
||||
|
||||
|
||||
def test_cartesian_product_random():
|
||||
G = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
H = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
GH = nx.cartesian_product(G, H)
|
||||
|
||||
for (u_G, u_H) in GH.nodes():
|
||||
for (v_G, v_H) in GH.nodes():
|
||||
if (u_G == v_G and H.has_edge(u_H, v_H)) or (
|
||||
u_H == v_H and G.has_edge(u_G, v_G)
|
||||
):
|
||||
assert GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
else:
|
||||
assert not GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
|
||||
|
||||
def test_lexicographic_product_raises():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
P = nx.lexicographic_product(nx.DiGraph(), nx.Graph())
|
||||
|
||||
|
||||
def test_lexicographic_product_null():
|
||||
null = nx.null_graph()
|
||||
empty10 = nx.empty_graph(10)
|
||||
K3 = nx.complete_graph(3)
|
||||
K10 = nx.complete_graph(10)
|
||||
P3 = nx.path_graph(3)
|
||||
P10 = nx.path_graph(10)
|
||||
# null graph
|
||||
G = nx.lexicographic_product(null, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
# null_graph X anything = null_graph and v.v.
|
||||
G = nx.lexicographic_product(null, empty10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(null, K3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(null, K10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(null, P3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(null, P10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(empty10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(K3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(K10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(P3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.lexicographic_product(P10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
|
||||
|
||||
def test_lexicographic_product_size():
|
||||
K5 = nx.complete_graph(5)
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.lexicographic_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.lexicographic_product(K3, K5)
|
||||
assert nx.number_of_nodes(G) == 3 * 5
|
||||
|
||||
|
||||
def test_lexicographic_product_combinations():
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.lexicographic_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.lexicographic_product(nx.MultiGraph(P5), K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.lexicographic_product(P5, nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.lexicographic_product(nx.MultiGraph(P5), nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
|
||||
# No classic easily found classic results for lexicographic product
|
||||
|
||||
|
||||
def test_lexicographic_product_random():
|
||||
G = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
H = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
GH = nx.lexicographic_product(G, H)
|
||||
|
||||
for (u_G, u_H) in GH.nodes():
|
||||
for (v_G, v_H) in GH.nodes():
|
||||
if G.has_edge(u_G, v_G) or (u_G == v_G and H.has_edge(u_H, v_H)):
|
||||
assert GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
else:
|
||||
assert not GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
|
||||
|
||||
def test_strong_product_raises():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
P = nx.strong_product(nx.DiGraph(), nx.Graph())
|
||||
|
||||
|
||||
def test_strong_product_null():
|
||||
null = nx.null_graph()
|
||||
empty10 = nx.empty_graph(10)
|
||||
K3 = nx.complete_graph(3)
|
||||
K10 = nx.complete_graph(10)
|
||||
P3 = nx.path_graph(3)
|
||||
P10 = nx.path_graph(10)
|
||||
# null graph
|
||||
G = nx.strong_product(null, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
# null_graph X anything = null_graph and v.v.
|
||||
G = nx.strong_product(null, empty10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(null, K3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(null, K10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(null, P3)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(null, P10)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(empty10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(K3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(K10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(P3, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
G = nx.strong_product(P10, null)
|
||||
assert nx.is_isomorphic(G, null)
|
||||
|
||||
|
||||
def test_strong_product_size():
|
||||
K5 = nx.complete_graph(5)
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.strong_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.strong_product(K3, K5)
|
||||
assert nx.number_of_nodes(G) == 3 * 5
|
||||
|
||||
|
||||
def test_strong_product_combinations():
|
||||
P5 = nx.path_graph(5)
|
||||
K3 = nx.complete_graph(3)
|
||||
G = nx.strong_product(P5, K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.strong_product(nx.MultiGraph(P5), K3)
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.strong_product(P5, nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
G = nx.strong_product(nx.MultiGraph(P5), nx.MultiGraph(K3))
|
||||
assert nx.number_of_nodes(G) == 5 * 3
|
||||
|
||||
# No classic easily found classic results for strong product
|
||||
|
||||
|
||||
def test_strong_product_random():
|
||||
G = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
H = nx.erdos_renyi_graph(10, 2 / 10.0)
|
||||
GH = nx.strong_product(G, H)
|
||||
|
||||
for (u_G, u_H) in GH.nodes():
|
||||
for (v_G, v_H) in GH.nodes():
|
||||
if (
|
||||
(u_G == v_G and H.has_edge(u_H, v_H))
|
||||
or (u_H == v_H and G.has_edge(u_G, v_G))
|
||||
or (G.has_edge(u_G, v_G) and H.has_edge(u_H, v_H))
|
||||
):
|
||||
assert GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
else:
|
||||
assert not GH.has_edge((u_G, u_H), (v_G, v_H))
|
||||
|
||||
|
||||
def test_graph_power_raises():
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
nx.power(nx.MultiDiGraph(), 2)
|
||||
|
||||
|
||||
def test_graph_power():
|
||||
# wikipedia example for graph power
|
||||
G = nx.cycle_graph(7)
|
||||
G.add_edge(6, 7)
|
||||
G.add_edge(7, 8)
|
||||
G.add_edge(8, 9)
|
||||
G.add_edge(9, 2)
|
||||
H = nx.power(G, 2)
|
||||
|
||||
assert_edges_equal(
|
||||
list(H.edges()),
|
||||
[
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 5),
|
||||
(0, 6),
|
||||
(0, 7),
|
||||
(1, 9),
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 6),
|
||||
(2, 3),
|
||||
(2, 4),
|
||||
(2, 8),
|
||||
(2, 9),
|
||||
(3, 4),
|
||||
(3, 5),
|
||||
(3, 9),
|
||||
(4, 5),
|
||||
(4, 6),
|
||||
(5, 6),
|
||||
(5, 7),
|
||||
(6, 7),
|
||||
(6, 8),
|
||||
(7, 8),
|
||||
(7, 9),
|
||||
(8, 9),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_graph_power_negative():
|
||||
with pytest.raises(ValueError):
|
||||
nx.power(nx.Graph(), -1)
|
||||
|
||||
|
||||
def test_rooted_product_raises():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.rooted_product(nx.Graph(), nx.path_graph(2), 10)
|
||||
|
||||
|
||||
def test_rooted_product():
|
||||
G = nx.cycle_graph(5)
|
||||
H = nx.Graph()
|
||||
H.add_edges_from([("a", "b"), ("b", "c"), ("b", "d")])
|
||||
R = nx.rooted_product(G, H, "a")
|
||||
assert len(R) == len(G) * len(H)
|
||||
assert R.size() == G.size() + len(G) * H.size()
|
|
@ -0,0 +1,54 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_complement():
|
||||
null = nx.null_graph()
|
||||
empty1 = nx.empty_graph(1)
|
||||
empty10 = nx.empty_graph(10)
|
||||
K3 = nx.complete_graph(3)
|
||||
K5 = nx.complete_graph(5)
|
||||
K10 = nx.complete_graph(10)
|
||||
P2 = nx.path_graph(2)
|
||||
P3 = nx.path_graph(3)
|
||||
P5 = nx.path_graph(5)
|
||||
P10 = nx.path_graph(10)
|
||||
# complement of the complete graph is empty
|
||||
|
||||
G = nx.complement(K3)
|
||||
assert nx.is_isomorphic(G, nx.empty_graph(3))
|
||||
G = nx.complement(K5)
|
||||
assert nx.is_isomorphic(G, nx.empty_graph(5))
|
||||
# for any G, G=complement(complement(G))
|
||||
P3cc = nx.complement(nx.complement(P3))
|
||||
assert nx.is_isomorphic(P3, P3cc)
|
||||
nullcc = nx.complement(nx.complement(null))
|
||||
assert nx.is_isomorphic(null, nullcc)
|
||||
b = nx.bull_graph()
|
||||
bcc = nx.complement(nx.complement(b))
|
||||
assert nx.is_isomorphic(b, bcc)
|
||||
|
||||
|
||||
def test_complement_2():
|
||||
G1 = nx.DiGraph()
|
||||
G1.add_edge("A", "B")
|
||||
G1.add_edge("A", "C")
|
||||
G1.add_edge("A", "D")
|
||||
G1C = nx.complement(G1)
|
||||
assert sorted(G1C.edges()) == [
|
||||
("B", "A"),
|
||||
("B", "C"),
|
||||
("B", "D"),
|
||||
("C", "A"),
|
||||
("C", "B"),
|
||||
("C", "D"),
|
||||
("D", "A"),
|
||||
("D", "B"),
|
||||
("D", "C"),
|
||||
]
|
||||
|
||||
|
||||
def test_reverse1():
|
||||
# Other tests for reverse are done by the DiGraph and MultiDigraph.
|
||||
G1 = nx.Graph()
|
||||
pytest.raises(nx.NetworkXError, nx.reverse, G1)
|
|
@ -0,0 +1,54 @@
|
|||
"""Unary operations on graphs"""
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["complement", "reverse"]
|
||||
|
||||
|
||||
def complement(G):
|
||||
"""Returns the graph complement of G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : graph
|
||||
A NetworkX graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
GC : A new graph.
|
||||
|
||||
Notes
|
||||
------
|
||||
Note that complement() does not create self-loops and also
|
||||
does not produce parallel edges for MultiGraphs.
|
||||
|
||||
Graph, node, and edge data are not propagated to the new graph.
|
||||
"""
|
||||
R = G.__class__()
|
||||
R.add_nodes_from(G)
|
||||
R.add_edges_from(
|
||||
((n, n2) for n, nbrs in G.adjacency() for n2 in G if n2 not in nbrs if n != n2)
|
||||
)
|
||||
return R
|
||||
|
||||
|
||||
def reverse(G, copy=True):
|
||||
"""Returns the reverse directed graph of G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : directed graph
|
||||
A NetworkX directed graph
|
||||
copy : bool
|
||||
If True, then a new graph is returned. If False, then the graph is
|
||||
reversed in place.
|
||||
|
||||
Returns
|
||||
-------
|
||||
H : directed graph
|
||||
The reversed G.
|
||||
|
||||
"""
|
||||
if not G.is_directed():
|
||||
raise nx.NetworkXError("Cannot reverse an undirected graph.")
|
||||
else:
|
||||
return G.reverse(copy=copy)
|
Loading…
Add table
Add a link
Reference in a new issue