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
13
venv/Lib/site-packages/networkx/classes/__init__.py
Normal file
13
venv/Lib/site-packages/networkx/classes/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from .graph import Graph
|
||||
from .digraph import DiGraph
|
||||
from .multigraph import MultiGraph
|
||||
from .multidigraph import MultiDiGraph
|
||||
from .ordered import *
|
||||
|
||||
from .function import *
|
||||
|
||||
import networkx.classes.filters
|
||||
|
||||
import networkx.classes.coreviews
|
||||
import networkx.classes.graphviews
|
||||
import networkx.classes.reportviews
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
439
venv/Lib/site-packages/networkx/classes/coreviews.py
Normal file
439
venv/Lib/site-packages/networkx/classes/coreviews.py
Normal file
|
@ -0,0 +1,439 @@
|
|||
"""
|
||||
"""
|
||||
from collections.abc import Mapping
|
||||
|
||||
__all__ = [
|
||||
"AtlasView",
|
||||
"AdjacencyView",
|
||||
"MultiAdjacencyView",
|
||||
"UnionAtlas",
|
||||
"UnionAdjacency",
|
||||
"UnionMultiInner",
|
||||
"UnionMultiAdjacency",
|
||||
"FilterAtlas",
|
||||
"FilterAdjacency",
|
||||
"FilterMultiInner",
|
||||
"FilterMultiAdjacency",
|
||||
]
|
||||
|
||||
|
||||
class AtlasView(Mapping):
|
||||
"""An AtlasView is a Read-only Mapping of Mappings.
|
||||
|
||||
It is a View into a dict-of-dict data structure.
|
||||
The inner level of dict is read-write. But the
|
||||
outer level is read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
AdjacencyView - View into dict-of-dict-of-dict
|
||||
MultiAdjacencyView - View into dict-of-dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = ("_atlas",)
|
||||
|
||||
def __getstate__(self):
|
||||
return {"_atlas": self._atlas}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._atlas = state["_atlas"]
|
||||
|
||||
def __init__(self, d):
|
||||
self._atlas = d
|
||||
|
||||
def __len__(self):
|
||||
return len(self._atlas)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._atlas)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._atlas[key]
|
||||
|
||||
def copy(self):
|
||||
return {n: self[n].copy() for n in self._atlas}
|
||||
|
||||
def __str__(self):
|
||||
return str(self._atlas) # {nbr: self[nbr] for nbr in self})
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self._atlas!r})"
|
||||
|
||||
|
||||
class AdjacencyView(AtlasView):
|
||||
"""An AdjacencyView is a Read-only Map of Maps of Maps.
|
||||
|
||||
It is a View into a dict-of-dict-of-dict data structure.
|
||||
The inner level of dict is read-write. But the
|
||||
outer levels are read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
AtlasView - View into dict-of-dict
|
||||
MultiAdjacencyView - View into dict-of-dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = () # Still uses AtlasView slots names _atlas
|
||||
|
||||
def __getitem__(self, name):
|
||||
return AtlasView(self._atlas[name])
|
||||
|
||||
def copy(self):
|
||||
return {n: self[n].copy() for n in self._atlas}
|
||||
|
||||
|
||||
class MultiAdjacencyView(AdjacencyView):
|
||||
"""An MultiAdjacencyView is a Read-only Map of Maps of Maps of Maps.
|
||||
|
||||
It is a View into a dict-of-dict-of-dict-of-dict data structure.
|
||||
The inner level of dict is read-write. But the
|
||||
outer levels are read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
AtlasView - View into dict-of-dict
|
||||
AdjacencyView - View into dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = () # Still uses AtlasView slots names _atlas
|
||||
|
||||
def __getitem__(self, name):
|
||||
return AdjacencyView(self._atlas[name])
|
||||
|
||||
def copy(self):
|
||||
return {n: self[n].copy() for n in self._atlas}
|
||||
|
||||
|
||||
class UnionAtlas(Mapping):
|
||||
"""A read-only union of two atlases (dict-of-dict).
|
||||
|
||||
The two dict-of-dicts represent the inner dict of
|
||||
an Adjacency: `G.succ[node]` and `G.pred[node]`.
|
||||
The inner level of dict of both hold attribute key:value
|
||||
pairs and is read-write. But the outer level is read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
UnionAdjacency - View into dict-of-dict-of-dict
|
||||
UnionMultiAdjacency - View into dict-of-dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = ("_succ", "_pred")
|
||||
|
||||
def __getstate__(self):
|
||||
return {"_succ": self._succ, "_pred": self._pred}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._succ = state["_succ"]
|
||||
self._pred = state["_pred"]
|
||||
|
||||
def __init__(self, succ, pred):
|
||||
self._succ = succ
|
||||
self._pred = pred
|
||||
|
||||
def __len__(self):
|
||||
return len(self._succ) + len(self._pred)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(set(self._succ.keys()) | set(self._pred.keys()))
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return self._succ[key]
|
||||
except KeyError:
|
||||
return self._pred[key]
|
||||
|
||||
def copy(self):
|
||||
result = {nbr: dd.copy() for nbr, dd in self._succ.items()}
|
||||
for nbr, dd in self._pred.items():
|
||||
if nbr in result:
|
||||
result[nbr].update(dd)
|
||||
else:
|
||||
result[nbr] = dd.copy()
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
return str({nbr: self[nbr] for nbr in self})
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})"
|
||||
|
||||
|
||||
class UnionAdjacency(Mapping):
|
||||
"""A read-only union of dict Adjacencies as a Map of Maps of Maps.
|
||||
|
||||
The two input dict-of-dict-of-dicts represent the union of
|
||||
`G.succ` and `G.pred`. Return values are UnionAtlas
|
||||
The inner level of dict is read-write. But the
|
||||
middle and outer levels are read-only.
|
||||
|
||||
succ : a dict-of-dict-of-dict {node: nbrdict}
|
||||
pred : a dict-of-dict-of-dict {node: nbrdict}
|
||||
The keys for the two dicts should be the same
|
||||
|
||||
See Also
|
||||
========
|
||||
UnionAtlas - View into dict-of-dict
|
||||
UnionMultiAdjacency - View into dict-of-dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = ("_succ", "_pred")
|
||||
|
||||
def __getstate__(self):
|
||||
return {"_succ": self._succ, "_pred": self._pred}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._succ = state["_succ"]
|
||||
self._pred = state["_pred"]
|
||||
|
||||
def __init__(self, succ, pred):
|
||||
# keys must be the same for two input dicts
|
||||
assert len(set(succ.keys()) ^ set(pred.keys())) == 0
|
||||
self._succ = succ
|
||||
self._pred = pred
|
||||
|
||||
def __len__(self):
|
||||
return len(self._succ) # length of each dict should be the same
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._succ)
|
||||
|
||||
def __getitem__(self, nbr):
|
||||
return UnionAtlas(self._succ[nbr], self._pred[nbr])
|
||||
|
||||
def copy(self):
|
||||
return {n: self[n].copy() for n in self._succ}
|
||||
|
||||
def __str__(self):
|
||||
return str({nbr: self[nbr] for nbr in self})
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})"
|
||||
|
||||
|
||||
class UnionMultiInner(UnionAtlas):
|
||||
"""A read-only union of two inner dicts of MultiAdjacencies.
|
||||
|
||||
The two input dict-of-dict-of-dicts represent the union of
|
||||
`G.succ[node]` and `G.pred[node]` for MultiDiGraphs.
|
||||
Return values are UnionAtlas.
|
||||
The inner level of dict is read-write. But the outer levels are read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
UnionAtlas - View into dict-of-dict
|
||||
UnionAdjacency - View into dict-of-dict-of-dict
|
||||
UnionMultiAdjacency - View into dict-of-dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = () # Still uses UnionAtlas slots names _succ, _pred
|
||||
|
||||
def __getitem__(self, node):
|
||||
in_succ = node in self._succ
|
||||
in_pred = node in self._pred
|
||||
if in_succ:
|
||||
if in_pred:
|
||||
return UnionAtlas(self._succ[node], self._pred[node])
|
||||
return UnionAtlas(self._succ[node], {})
|
||||
return UnionAtlas({}, self._pred[node])
|
||||
|
||||
def copy(self):
|
||||
nodes = set(self._succ.keys()) | set(self._pred.keys())
|
||||
return {n: self[n].copy() for n in nodes}
|
||||
|
||||
|
||||
class UnionMultiAdjacency(UnionAdjacency):
|
||||
"""A read-only union of two dict MultiAdjacencies.
|
||||
|
||||
The two input dict-of-dict-of-dict-of-dicts represent the union of
|
||||
`G.succ` and `G.pred` for MultiDiGraphs. Return values are UnionAdjacency.
|
||||
The inner level of dict is read-write. But the outer levels are read-only.
|
||||
|
||||
See Also
|
||||
========
|
||||
UnionAtlas - View into dict-of-dict
|
||||
UnionMultiInner - View into dict-of-dict-of-dict
|
||||
"""
|
||||
|
||||
__slots__ = () # Still uses UnionAdjacency slots names _succ, _pred
|
||||
|
||||
def __getitem__(self, node):
|
||||
return UnionMultiInner(self._succ[node], self._pred[node])
|
||||
|
||||
|
||||
class FilterAtlas(Mapping): # nodedict, nbrdict, keydict
|
||||
def __init__(self, d, NODE_OK):
|
||||
self._atlas = d
|
||||
self.NODE_OK = NODE_OK
|
||||
|
||||
def __len__(self):
|
||||
return sum(1 for n in self)
|
||||
|
||||
def __iter__(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
return (n for n in self.NODE_OK.nodes if n in self._atlas)
|
||||
return (n for n in self._atlas if self.NODE_OK(n))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self._atlas and self.NODE_OK(key):
|
||||
return self._atlas[key]
|
||||
raise KeyError(f"Key {key} not found")
|
||||
|
||||
def copy(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
return {u: self._atlas[u] for u in self.NODE_OK.nodes if u in self._atlas}
|
||||
return {u: d for u, d in self._atlas.items() if self.NODE_OK(u)}
|
||||
|
||||
def __str__(self):
|
||||
return str({nbr: self[nbr] for nbr in self})
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self._atlas!r}, {self.NODE_OK!r})"
|
||||
|
||||
|
||||
class FilterAdjacency(Mapping): # edgedict
|
||||
def __init__(self, d, NODE_OK, EDGE_OK):
|
||||
self._atlas = d
|
||||
self.NODE_OK = NODE_OK
|
||||
self.EDGE_OK = EDGE_OK
|
||||
|
||||
def __len__(self):
|
||||
return sum(1 for n in self)
|
||||
|
||||
def __iter__(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
return (n for n in self.NODE_OK.nodes if n in self._atlas)
|
||||
return (n for n in self._atlas if self.NODE_OK(n))
|
||||
|
||||
def __getitem__(self, node):
|
||||
if node in self._atlas and self.NODE_OK(node):
|
||||
|
||||
def new_node_ok(nbr):
|
||||
return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr)
|
||||
|
||||
return FilterAtlas(self._atlas[node], new_node_ok)
|
||||
raise KeyError(f"Key {node} not found")
|
||||
|
||||
def copy(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
return {
|
||||
u: {
|
||||
v: d
|
||||
for v, d in self._atlas[u].items()
|
||||
if self.NODE_OK(v)
|
||||
if self.EDGE_OK(u, v)
|
||||
}
|
||||
for u in self.NODE_OK.nodes
|
||||
if u in self._atlas
|
||||
}
|
||||
return {
|
||||
u: {v: d for v, d in nbrs.items() if self.NODE_OK(v) if self.EDGE_OK(u, v)}
|
||||
for u, nbrs in self._atlas.items()
|
||||
if self.NODE_OK(u)
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return str({nbr: self[nbr] for nbr in self})
|
||||
|
||||
def __repr__(self):
|
||||
name = self.__class__.__name__
|
||||
return f"{name}({self._atlas!r}, {self.NODE_OK!r}, {self.EDGE_OK!r})"
|
||||
|
||||
|
||||
class FilterMultiInner(FilterAdjacency): # muliedge_seconddict
|
||||
def __iter__(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
my_nodes = (n for n in self.NODE_OK.nodes if n in self._atlas)
|
||||
else:
|
||||
my_nodes = (n for n in self._atlas if self.NODE_OK(n))
|
||||
for n in my_nodes:
|
||||
some_keys_ok = False
|
||||
for key in self._atlas[n]:
|
||||
if self.EDGE_OK(n, key):
|
||||
some_keys_ok = True
|
||||
break
|
||||
if some_keys_ok is True:
|
||||
yield n
|
||||
|
||||
def __getitem__(self, nbr):
|
||||
if nbr in self._atlas and self.NODE_OK(nbr):
|
||||
|
||||
def new_node_ok(key):
|
||||
return self.EDGE_OK(nbr, key)
|
||||
|
||||
return FilterAtlas(self._atlas[nbr], new_node_ok)
|
||||
raise KeyError(f"Key {nbr} not found")
|
||||
|
||||
def copy(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
return {
|
||||
v: {k: d for k, d in self._atlas[v].items() if self.EDGE_OK(v, k)}
|
||||
for v in self.NODE_OK.nodes
|
||||
if v in self._atlas
|
||||
}
|
||||
return {
|
||||
v: {k: d for k, d in nbrs.items() if self.EDGE_OK(v, k)}
|
||||
for v, nbrs in self._atlas.items()
|
||||
if self.NODE_OK(v)
|
||||
}
|
||||
|
||||
|
||||
class FilterMultiAdjacency(FilterAdjacency): # multiedgedict
|
||||
def __getitem__(self, node):
|
||||
if node in self._atlas and self.NODE_OK(node):
|
||||
|
||||
def edge_ok(nbr, key):
|
||||
return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr, key)
|
||||
|
||||
return FilterMultiInner(self._atlas[node], self.NODE_OK, edge_ok)
|
||||
raise KeyError(f"Key {node} not found")
|
||||
|
||||
def copy(self):
|
||||
try: # check that NODE_OK has attr 'nodes'
|
||||
node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
|
||||
except AttributeError:
|
||||
node_ok_shorter = False
|
||||
if node_ok_shorter:
|
||||
my_nodes = self.NODE_OK.nodes
|
||||
return {
|
||||
u: {
|
||||
v: {k: d for k, d in kd.items() if self.EDGE_OK(u, v, k)}
|
||||
for v, kd in self._atlas[u].items()
|
||||
if v in my_nodes
|
||||
}
|
||||
for u in my_nodes
|
||||
if u in self._atlas
|
||||
}
|
||||
return {
|
||||
u: {
|
||||
v: {k: d for k, d in kd.items() if self.EDGE_OK(u, v, k)}
|
||||
for v, kd in nbrs.items()
|
||||
if self.NODE_OK(v)
|
||||
}
|
||||
for u, nbrs in self._atlas.items()
|
||||
if self.NODE_OK(u)
|
||||
}
|
1219
venv/Lib/site-packages/networkx/classes/digraph.py
Normal file
1219
venv/Lib/site-packages/networkx/classes/digraph.py
Normal file
File diff suppressed because it is too large
Load diff
75
venv/Lib/site-packages/networkx/classes/filters.py
Normal file
75
venv/Lib/site-packages/networkx/classes/filters.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""Filter factories to hide or show sets of nodes and edges.
|
||||
|
||||
These filters return the function used when creating `SubGraph`.
|
||||
"""
|
||||
__all__ = [
|
||||
"no_filter",
|
||||
"hide_nodes",
|
||||
"hide_edges",
|
||||
"hide_multiedges",
|
||||
"hide_diedges",
|
||||
"hide_multidiedges",
|
||||
"show_nodes",
|
||||
"show_edges",
|
||||
"show_multiedges",
|
||||
"show_diedges",
|
||||
"show_multidiedges",
|
||||
]
|
||||
|
||||
|
||||
def no_filter(*items):
|
||||
return True
|
||||
|
||||
|
||||
def hide_nodes(nodes):
|
||||
nodes = set(nodes)
|
||||
return lambda node: node not in nodes
|
||||
|
||||
|
||||
def hide_diedges(edges):
|
||||
edges = {(u, v) for u, v in edges}
|
||||
return lambda u, v: (u, v) not in edges
|
||||
|
||||
|
||||
def hide_edges(edges):
|
||||
alledges = set(edges) | {(v, u) for (u, v) in edges}
|
||||
return lambda u, v: (u, v) not in alledges
|
||||
|
||||
|
||||
def hide_multidiedges(edges):
|
||||
edges = {(u, v, k) for u, v, k in edges}
|
||||
return lambda u, v, k: (u, v, k) not in edges
|
||||
|
||||
|
||||
def hide_multiedges(edges):
|
||||
alledges = set(edges) | {(v, u, k) for (u, v, k) in edges}
|
||||
return lambda u, v, k: (u, v, k) not in alledges
|
||||
|
||||
|
||||
# write show_nodes as a class to make SubGraph pickleable
|
||||
class show_nodes:
|
||||
def __init__(self, nodes):
|
||||
self.nodes = set(nodes)
|
||||
|
||||
def __call__(self, node):
|
||||
return node in self.nodes
|
||||
|
||||
|
||||
def show_diedges(edges):
|
||||
edges = {(u, v) for u, v in edges}
|
||||
return lambda u, v: (u, v) in edges
|
||||
|
||||
|
||||
def show_edges(edges):
|
||||
alledges = set(edges) | {(v, u) for (u, v) in edges}
|
||||
return lambda u, v: (u, v) in alledges
|
||||
|
||||
|
||||
def show_multidiedges(edges):
|
||||
edges = {(u, v, k) for u, v, k in edges}
|
||||
return lambda u, v, k: (u, v, k) in edges
|
||||
|
||||
|
||||
def show_multiedges(edges):
|
||||
alledges = set(edges) | {(v, u, k) for (u, v, k) in edges}
|
||||
return lambda u, v, k: (u, v, k) in alledges
|
1295
venv/Lib/site-packages/networkx/classes/function.py
Normal file
1295
venv/Lib/site-packages/networkx/classes/function.py
Normal file
File diff suppressed because it is too large
Load diff
1913
venv/Lib/site-packages/networkx/classes/graph.py
Normal file
1913
venv/Lib/site-packages/networkx/classes/graph.py
Normal file
File diff suppressed because it is too large
Load diff
206
venv/Lib/site-packages/networkx/classes/graphviews.py
Normal file
206
venv/Lib/site-packages/networkx/classes/graphviews.py
Normal file
|
@ -0,0 +1,206 @@
|
|||
"""View of Graphs as SubGraph, Reverse, Directed, Undirected.
|
||||
|
||||
In some algorithms it is convenient to temporarily morph
|
||||
a graph to exclude some nodes or edges. It should be better
|
||||
to do that via a view than to remove and then re-add.
|
||||
In other algorithms it is convenient to temporarily morph
|
||||
a graph to reverse directed edges, or treat a directed graph
|
||||
as undirected, etc. This module provides those graph views.
|
||||
|
||||
The resulting views are essentially read-only graphs that
|
||||
report data from the orignal graph object. We provide an
|
||||
attribute G._graph which points to the underlying graph object.
|
||||
|
||||
Note: Since graphviews look like graphs, one can end up with
|
||||
view-of-view-of-view chains. Be careful with chains because
|
||||
they become very slow with about 15 nested views.
|
||||
For the common simple case of node induced subgraphs created
|
||||
from the graph class, we short-cut the chain by returning a
|
||||
subgraph of the original graph directly rather than a subgraph
|
||||
of a subgraph. We are careful not to disrupt any edge filter in
|
||||
the middle subgraph. In general, determining how to short-cut
|
||||
the chain is tricky and much harder with restricted_views than
|
||||
with induced subgraphs.
|
||||
Often it is easiest to use .copy() to avoid chains.
|
||||
"""
|
||||
from networkx.classes.coreviews import (
|
||||
UnionAdjacency,
|
||||
UnionMultiAdjacency,
|
||||
FilterAtlas,
|
||||
FilterAdjacency,
|
||||
FilterMultiAdjacency,
|
||||
)
|
||||
from networkx.classes.filters import no_filter
|
||||
from networkx.exception import NetworkXError
|
||||
from networkx.utils import not_implemented_for
|
||||
|
||||
import networkx as nx
|
||||
|
||||
__all__ = ["generic_graph_view", "subgraph_view", "reverse_view"]
|
||||
|
||||
|
||||
def generic_graph_view(G, create_using=None):
|
||||
if create_using is None:
|
||||
newG = G.__class__()
|
||||
else:
|
||||
newG = nx.empty_graph(0, create_using)
|
||||
if G.is_multigraph() != newG.is_multigraph():
|
||||
raise NetworkXError("Multigraph for G must agree with create_using")
|
||||
newG = nx.freeze(newG)
|
||||
|
||||
# create view by assigning attributes from G
|
||||
newG._graph = G
|
||||
newG.graph = G.graph
|
||||
|
||||
newG._node = G._node
|
||||
if newG.is_directed():
|
||||
if G.is_directed():
|
||||
newG._succ = G._succ
|
||||
newG._pred = G._pred
|
||||
newG._adj = G._succ
|
||||
else:
|
||||
newG._succ = G._adj
|
||||
newG._pred = G._adj
|
||||
newG._adj = G._adj
|
||||
elif G.is_directed():
|
||||
if G.is_multigraph():
|
||||
newG._adj = UnionMultiAdjacency(G._succ, G._pred)
|
||||
else:
|
||||
newG._adj = UnionAdjacency(G._succ, G._pred)
|
||||
else:
|
||||
newG._adj = G._adj
|
||||
return newG
|
||||
|
||||
|
||||
def subgraph_view(G, filter_node=no_filter, filter_edge=no_filter):
|
||||
""" View of `G` applying a filter on nodes and edges.
|
||||
|
||||
`subgraph_view` provides a read-only view of the input graph that excludes
|
||||
nodes and edges based on the outcome of two filter functions `filter_node`
|
||||
and `filter_edge`.
|
||||
|
||||
The `filter_node` function takes one argument --- the node --- and returns
|
||||
`True` if the node should be included in the subgraph, and `False` if it
|
||||
should not be included.
|
||||
|
||||
The `filter_edge` function takes two (or three arguments if `G` is a
|
||||
multi-graph) --- the nodes describing an edge, plus the edge-key if
|
||||
parallel edges are possible --- and returns `True` if the edge should be
|
||||
included in the subgraph, and `False` if it should not be included.
|
||||
|
||||
Both node and edge filter functions are called on graph elements as they
|
||||
are queried, meaning there is no up-front cost to creating the view.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : networkx.Graph
|
||||
A directed/undirected graph/multigraph
|
||||
|
||||
filter_node : callable, optional
|
||||
A function taking a node as input, which returns `True` if the node
|
||||
should appear in the view.
|
||||
|
||||
filter_edge : callable, optional
|
||||
A function taking as input the two nodes describing an edge (plus the
|
||||
edge-key if `G` is a multi-graph), which returns `True` if the edge
|
||||
should appear in the view.
|
||||
|
||||
Returns
|
||||
-------
|
||||
graph : networkx.Graph
|
||||
A read-only graph view of the input graph.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(6)
|
||||
|
||||
Filter functions operate on the node, and return `True` if the node should
|
||||
appear in the view:
|
||||
|
||||
>>> def filter_node(n1):
|
||||
... return n1 != 5
|
||||
...
|
||||
>>> view = nx.subgraph_view(G, filter_node=filter_node)
|
||||
>>> view.nodes()
|
||||
NodeView((0, 1, 2, 3, 4))
|
||||
|
||||
We can use a closure pattern to filter graph elements based on additional
|
||||
data --- for example, filtering on edge data attached to the graph:
|
||||
|
||||
>>> G[3][4]["cross_me"] = False
|
||||
>>> def filter_edge(n1, n2):
|
||||
... return G[n1][n2].get("cross_me", True)
|
||||
...
|
||||
>>> view = nx.subgraph_view(G, filter_edge=filter_edge)
|
||||
>>> view.edges()
|
||||
EdgeView([(0, 1), (1, 2), (2, 3), (4, 5)])
|
||||
|
||||
>>> view = nx.subgraph_view(G, filter_node=filter_node, filter_edge=filter_edge,)
|
||||
>>> view.nodes()
|
||||
NodeView((0, 1, 2, 3, 4))
|
||||
>>> view.edges()
|
||||
EdgeView([(0, 1), (1, 2), (2, 3)])
|
||||
"""
|
||||
newG = nx.freeze(G.__class__())
|
||||
newG._NODE_OK = filter_node
|
||||
newG._EDGE_OK = filter_edge
|
||||
|
||||
# create view by assigning attributes from G
|
||||
newG._graph = G
|
||||
newG.graph = G.graph
|
||||
|
||||
newG._node = FilterAtlas(G._node, filter_node)
|
||||
if G.is_multigraph():
|
||||
Adj = FilterMultiAdjacency
|
||||
|
||||
def reverse_edge(u, v, k):
|
||||
return filter_edge(v, u, k)
|
||||
|
||||
else:
|
||||
Adj = FilterAdjacency
|
||||
|
||||
def reverse_edge(u, v):
|
||||
return filter_edge(v, u)
|
||||
|
||||
if G.is_directed():
|
||||
newG._succ = Adj(G._succ, filter_node, filter_edge)
|
||||
newG._pred = Adj(G._pred, filter_node, reverse_edge)
|
||||
newG._adj = newG._succ
|
||||
else:
|
||||
newG._adj = Adj(G._adj, filter_node, filter_edge)
|
||||
return newG
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def reverse_view(G):
|
||||
""" View of `G` with edge directions reversed
|
||||
|
||||
`reverse_view` returns a read-only view of the input graph where
|
||||
edge directions are reversed.
|
||||
|
||||
Identical to digraph.reverse(copy=False)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : networkx.DiGraph
|
||||
|
||||
Returns
|
||||
-------
|
||||
graph : networkx.DiGraph
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.DiGraph()
|
||||
>>> G.add_edge(1, 2)
|
||||
>>> G.add_edge(2, 3)
|
||||
>>> G.edges()
|
||||
OutEdgeView([(1, 2), (2, 3)])
|
||||
|
||||
>>> view = nx.reverse_view(G)
|
||||
>>> view.edges()
|
||||
OutEdgeView([(2, 1), (3, 2)])
|
||||
"""
|
||||
newG = generic_graph_view(G)
|
||||
newG._succ, newG._pred = G._pred, G._succ
|
||||
newG._adj = newG._succ
|
||||
return newG
|
871
venv/Lib/site-packages/networkx/classes/multidigraph.py
Normal file
871
venv/Lib/site-packages/networkx/classes/multidigraph.py
Normal file
|
@ -0,0 +1,871 @@
|
|||
"""Base class for MultiDiGraph."""
|
||||
from copy import deepcopy
|
||||
|
||||
import networkx as nx
|
||||
from networkx.classes.digraph import DiGraph
|
||||
from networkx.classes.multigraph import MultiGraph
|
||||
from networkx.classes.coreviews import MultiAdjacencyView
|
||||
from networkx.classes.reportviews import (
|
||||
OutMultiEdgeView,
|
||||
InMultiEdgeView,
|
||||
DiMultiDegreeView,
|
||||
OutMultiDegreeView,
|
||||
InMultiDegreeView,
|
||||
)
|
||||
from networkx.exception import NetworkXError
|
||||
|
||||
|
||||
class MultiDiGraph(MultiGraph, DiGraph):
|
||||
"""A directed graph class that can store multiedges.
|
||||
|
||||
Multiedges are multiple edges between two nodes. Each edge
|
||||
can hold optional data or attributes.
|
||||
|
||||
A MultiDiGraph holds directed edges. Self loops are allowed.
|
||||
|
||||
Nodes can be arbitrary (hashable) Python objects with optional
|
||||
key/value attributes. By convention `None` is not used as a node.
|
||||
|
||||
Edges are represented as links between nodes with optional
|
||||
key/value attributes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
incoming_graph_data : input graph (optional, default: None)
|
||||
Data to initialize graph. If None (default) an empty
|
||||
graph is created. The data can be any format that is supported
|
||||
by the to_networkx_graph() function, currently including edge list,
|
||||
dict of dicts, dict of lists, NetworkX graph, NumPy matrix
|
||||
or 2d ndarray, SciPy sparse matrix, or PyGraphviz graph.
|
||||
|
||||
attr : keyword arguments, optional (default= no attributes)
|
||||
Attributes to add to graph as key=value pairs.
|
||||
|
||||
See Also
|
||||
--------
|
||||
Graph
|
||||
DiGraph
|
||||
MultiGraph
|
||||
OrderedMultiDiGraph
|
||||
|
||||
Examples
|
||||
--------
|
||||
Create an empty graph structure (a "null graph") with no nodes and
|
||||
no edges.
|
||||
|
||||
>>> G = nx.MultiDiGraph()
|
||||
|
||||
G can be grown in several ways.
|
||||
|
||||
**Nodes:**
|
||||
|
||||
Add one node at a time:
|
||||
|
||||
>>> G.add_node(1)
|
||||
|
||||
Add the nodes from any container (a list, dict, set or
|
||||
even the lines from a file or the nodes from another graph).
|
||||
|
||||
>>> G.add_nodes_from([2, 3])
|
||||
>>> G.add_nodes_from(range(100, 110))
|
||||
>>> H = nx.path_graph(10)
|
||||
>>> G.add_nodes_from(H)
|
||||
|
||||
In addition to strings and integers any hashable Python object
|
||||
(except None) can represent a node, e.g. a customized node object,
|
||||
or even another Graph.
|
||||
|
||||
>>> G.add_node(H)
|
||||
|
||||
**Edges:**
|
||||
|
||||
G can also be grown by adding edges.
|
||||
|
||||
Add one edge,
|
||||
|
||||
>>> key = G.add_edge(1, 2)
|
||||
|
||||
a list of edges,
|
||||
|
||||
>>> keys = G.add_edges_from([(1, 2), (1, 3)])
|
||||
|
||||
or a collection of edges,
|
||||
|
||||
>>> keys = G.add_edges_from(H.edges)
|
||||
|
||||
If some edges connect nodes not yet in the graph, the nodes
|
||||
are added automatically. If an edge already exists, an additional
|
||||
edge is created and stored using a key to identify the edge.
|
||||
By default the key is the lowest unused integer.
|
||||
|
||||
>>> keys = G.add_edges_from([(4, 5, dict(route=282)), (4, 5, dict(route=37))])
|
||||
>>> G[4]
|
||||
AdjacencyView({5: {0: {}, 1: {'route': 282}, 2: {'route': 37}}})
|
||||
|
||||
**Attributes:**
|
||||
|
||||
Each graph, node, and edge can hold key/value attribute pairs
|
||||
in an associated attribute dictionary (the keys must be hashable).
|
||||
By default these are empty, but can be added or changed using
|
||||
add_edge, add_node or direct manipulation of the attribute
|
||||
dictionaries named graph, node and edge respectively.
|
||||
|
||||
>>> G = nx.MultiDiGraph(day="Friday")
|
||||
>>> G.graph
|
||||
{'day': 'Friday'}
|
||||
|
||||
Add node attributes using add_node(), add_nodes_from() or G.nodes
|
||||
|
||||
>>> G.add_node(1, time="5pm")
|
||||
>>> G.add_nodes_from([3], time="2pm")
|
||||
>>> G.nodes[1]
|
||||
{'time': '5pm'}
|
||||
>>> G.nodes[1]["room"] = 714
|
||||
>>> del G.nodes[1]["room"] # remove attribute
|
||||
>>> list(G.nodes(data=True))
|
||||
[(1, {'time': '5pm'}), (3, {'time': '2pm'})]
|
||||
|
||||
Add edge attributes using add_edge(), add_edges_from(), subscript
|
||||
notation, or G.edges.
|
||||
|
||||
>>> key = G.add_edge(1, 2, weight=4.7)
|
||||
>>> keys = G.add_edges_from([(3, 4), (4, 5)], color="red")
|
||||
>>> keys = G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})])
|
||||
>>> G[1][2][0]["weight"] = 4.7
|
||||
>>> G.edges[1, 2, 0]["weight"] = 4
|
||||
|
||||
Warning: we protect the graph data structure by making `G.edges[1, 2]` a
|
||||
read-only dict-like structure. However, you can assign to attributes
|
||||
in e.g. `G.edges[1, 2]`. Thus, use 2 sets of brackets to add/change
|
||||
data attributes: `G.edges[1, 2]['weight'] = 4`
|
||||
(For multigraphs: `MG.edges[u, v, key][name] = value`).
|
||||
|
||||
**Shortcuts:**
|
||||
|
||||
Many common graph features allow python syntax to speed reporting.
|
||||
|
||||
>>> 1 in G # check if node in graph
|
||||
True
|
||||
>>> [n for n in G if n < 3] # iterate through nodes
|
||||
[1, 2]
|
||||
>>> len(G) # number of nodes in graph
|
||||
5
|
||||
>>> G[1] # adjacency dict-like view keyed by neighbor to edge attributes
|
||||
AdjacencyView({2: {0: {'weight': 4}, 1: {'color': 'blue'}}})
|
||||
|
||||
Often the best way to traverse all edges of a graph is via the neighbors.
|
||||
The neighbors are available as an adjacency-view `G.adj` object or via
|
||||
the method `G.adjacency()`.
|
||||
|
||||
>>> for n, nbrsdict in G.adjacency():
|
||||
... for nbr, keydict in nbrsdict.items():
|
||||
... for key, eattr in keydict.items():
|
||||
... if "weight" in eattr:
|
||||
... # Do something useful with the edges
|
||||
... pass
|
||||
|
||||
But the edges() method is often more convenient:
|
||||
|
||||
>>> for u, v, keys, weight in G.edges(data="weight", keys=True):
|
||||
... if weight is not None:
|
||||
... # Do something useful with the edges
|
||||
... pass
|
||||
|
||||
**Reporting:**
|
||||
|
||||
Simple graph information is obtained using methods and object-attributes.
|
||||
Reporting usually provides views instead of containers to reduce memory
|
||||
usage. The views update as the graph is updated similarly to dict-views.
|
||||
The objects `nodes, `edges` and `adj` provide access to data attributes
|
||||
via lookup (e.g. `nodes[n], `edges[u, v]`, `adj[u][v]`) and iteration
|
||||
(e.g. `nodes.items()`, `nodes.data('color')`,
|
||||
`nodes.data('color', default='blue')` and similarly for `edges`)
|
||||
Views exist for `nodes`, `edges`, `neighbors()`/`adj` and `degree`.
|
||||
|
||||
For details on these and other miscellaneous methods, see below.
|
||||
|
||||
**Subclasses (Advanced):**
|
||||
|
||||
The MultiDiGraph class uses a dict-of-dict-of-dict-of-dict structure.
|
||||
The outer dict (node_dict) holds adjacency information keyed by node.
|
||||
The next dict (adjlist_dict) represents the adjacency information and holds
|
||||
edge_key dicts keyed by neighbor. The edge_key dict holds each edge_attr
|
||||
dict keyed by edge key. The inner dict (edge_attr_dict) represents
|
||||
the edge data and holds edge attribute values keyed by attribute names.
|
||||
|
||||
Each of these four dicts in the dict-of-dict-of-dict-of-dict
|
||||
structure can be replaced by a user defined dict-like object.
|
||||
In general, the dict-like features should be maintained but
|
||||
extra features can be added. To replace one of the dicts create
|
||||
a new graph class by changing the class(!) variable holding the
|
||||
factory for that dict-like structure. The variable names are
|
||||
node_dict_factory, node_attr_dict_factory, adjlist_inner_dict_factory,
|
||||
adjlist_outer_dict_factory, edge_key_dict_factory, edge_attr_dict_factory
|
||||
and graph_attr_dict_factory.
|
||||
|
||||
node_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the dict containing node
|
||||
attributes, keyed by node id.
|
||||
It should require no arguments and return a dict-like object
|
||||
|
||||
node_attr_dict_factory: function, (default: dict)
|
||||
Factory function to be used to create the node attribute
|
||||
dict which holds attribute values keyed by attribute name.
|
||||
It should require no arguments and return a dict-like object
|
||||
|
||||
adjlist_outer_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the outer-most dict
|
||||
in the data structure that holds adjacency info keyed by node.
|
||||
It should require no arguments and return a dict-like object.
|
||||
|
||||
adjlist_inner_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the adjacency list
|
||||
dict which holds multiedge key dicts keyed by neighbor.
|
||||
It should require no arguments and return a dict-like object.
|
||||
|
||||
edge_key_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the edge key dict
|
||||
which holds edge data keyed by edge key.
|
||||
It should require no arguments and return a dict-like object.
|
||||
|
||||
edge_attr_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the edge attribute
|
||||
dict which holds attribute values keyed by attribute name.
|
||||
It should require no arguments and return a dict-like object.
|
||||
|
||||
graph_attr_dict_factory : function, (default: dict)
|
||||
Factory function to be used to create the graph attribute
|
||||
dict which holds attribute values keyed by attribute name.
|
||||
It should require no arguments and return a dict-like object.
|
||||
|
||||
Typically, if your extension doesn't impact the data structure all
|
||||
methods will inherited without issue except: `to_directed/to_undirected`.
|
||||
By default these methods create a DiGraph/Graph class and you probably
|
||||
want them to create your extension of a DiGraph/Graph. To facilitate
|
||||
this we define two class variables that you can set in your subclass.
|
||||
|
||||
to_directed_class : callable, (default: DiGraph or MultiDiGraph)
|
||||
Class to create a new graph structure in the `to_directed` method.
|
||||
If `None`, a NetworkX class (DiGraph or MultiDiGraph) is used.
|
||||
|
||||
to_undirected_class : callable, (default: Graph or MultiGraph)
|
||||
Class to create a new graph structure in the `to_undirected` method.
|
||||
If `None`, a NetworkX class (Graph or MultiGraph) is used.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Please see :mod:`~networkx.classes.ordered` for examples of
|
||||
creating graph subclasses by overwriting the base class `dict` with
|
||||
a dictionary-like object.
|
||||
"""
|
||||
|
||||
# node_dict_factory = dict # already assigned in Graph
|
||||
# adjlist_outer_dict_factory = dict
|
||||
# adjlist_inner_dict_factory = dict
|
||||
edge_key_dict_factory = dict
|
||||
# edge_attr_dict_factory = dict
|
||||
|
||||
def __init__(self, incoming_graph_data=None, **attr):
|
||||
"""Initialize a graph with edges, name, or graph attributes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
incoming_graph_data : input graph
|
||||
Data to initialize graph. If incoming_graph_data=None (default)
|
||||
an empty graph is created. The data can be an edge list, or any
|
||||
NetworkX graph object. If the corresponding optional Python
|
||||
packages are installed the data can also be a NumPy matrix
|
||||
or 2d ndarray, a SciPy sparse matrix, or a PyGraphviz graph.
|
||||
|
||||
attr : keyword arguments, optional (default= no attributes)
|
||||
Attributes to add to graph as key=value pairs.
|
||||
|
||||
See Also
|
||||
--------
|
||||
convert
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
|
||||
>>> G = nx.Graph(name="my graph")
|
||||
>>> e = [(1, 2), (2, 3), (3, 4)] # list of edges
|
||||
>>> G = nx.Graph(e)
|
||||
|
||||
Arbitrary graph attribute pairs (key=value) may be assigned
|
||||
|
||||
>>> G = nx.Graph(e, day="Friday")
|
||||
>>> G.graph
|
||||
{'day': 'Friday'}
|
||||
|
||||
"""
|
||||
self.edge_key_dict_factory = self.edge_key_dict_factory
|
||||
DiGraph.__init__(self, incoming_graph_data, **attr)
|
||||
|
||||
@property
|
||||
def adj(self):
|
||||
"""Graph adjacency object holding the neighbors of each node.
|
||||
|
||||
This object is a read-only dict-like structure with node keys
|
||||
and neighbor-dict values. The neighbor-dict is keyed by neighbor
|
||||
to the edgekey-dict. So `G.adj[3][2][0]['color'] = 'blue'` sets
|
||||
the color of the edge `(3, 2, 0)` to `"blue"`.
|
||||
|
||||
Iterating over G.adj behaves like a dict. Useful idioms include
|
||||
`for nbr, datadict in G.adj[n].items():`.
|
||||
|
||||
The neighbor information is also provided by subscripting the graph.
|
||||
So `for nbr, foovalue in G[node].data('foo', default=1):` works.
|
||||
|
||||
For directed graphs, `G.adj` holds outgoing (successor) info.
|
||||
"""
|
||||
return MultiAdjacencyView(self._succ)
|
||||
|
||||
@property
|
||||
def succ(self):
|
||||
"""Graph adjacency object holding the successors of each node.
|
||||
|
||||
This object is a read-only dict-like structure with node keys
|
||||
and neighbor-dict values. The neighbor-dict is keyed by neighbor
|
||||
to the edgekey-dict. So `G.adj[3][2][0]['color'] = 'blue'` sets
|
||||
the color of the edge `(3, 2, 0)` to `"blue"`.
|
||||
|
||||
Iterating over G.adj behaves like a dict. Useful idioms include
|
||||
`for nbr, datadict in G.adj[n].items():`.
|
||||
|
||||
The neighbor information is also provided by subscripting the graph.
|
||||
So `for nbr, foovalue in G[node].data('foo', default=1):` works.
|
||||
|
||||
For directed graphs, `G.succ` is identical to `G.adj`.
|
||||
"""
|
||||
return MultiAdjacencyView(self._succ)
|
||||
|
||||
@property
|
||||
def pred(self):
|
||||
"""Graph adjacency object holding the predecessors of each node.
|
||||
|
||||
This object is a read-only dict-like structure with node keys
|
||||
and neighbor-dict values. The neighbor-dict is keyed by neighbor
|
||||
to the edgekey-dict. So `G.adj[3][2][0]['color'] = 'blue'` sets
|
||||
the color of the edge `(3, 2, 0)` to `"blue"`.
|
||||
|
||||
Iterating over G.adj behaves like a dict. Useful idioms include
|
||||
`for nbr, datadict in G.adj[n].items():`.
|
||||
"""
|
||||
return MultiAdjacencyView(self._pred)
|
||||
|
||||
def add_edge(self, u_for_edge, v_for_edge, key=None, **attr):
|
||||
"""Add an edge between u and v.
|
||||
|
||||
The nodes u and v will be automatically added if they are
|
||||
not already in the graph.
|
||||
|
||||
Edge attributes can be specified with keywords or by directly
|
||||
accessing the edge's attribute dictionary. See examples below.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
u_for_edge, v_for_edge : nodes
|
||||
Nodes can be, for example, strings or numbers.
|
||||
Nodes must be hashable (and not None) Python objects.
|
||||
key : hashable identifier, optional (default=lowest unused integer)
|
||||
Used to distinguish multiedges between a pair of nodes.
|
||||
attr_dict : dictionary, optional (default= no attributes)
|
||||
Dictionary of edge attributes. Key/value pairs will
|
||||
update existing data associated with the edge.
|
||||
attr : keyword arguments, optional
|
||||
Edge data (or labels or objects) can be assigned using
|
||||
keyword arguments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The edge key assigned to the edge.
|
||||
|
||||
See Also
|
||||
--------
|
||||
add_edges_from : add a collection of edges
|
||||
|
||||
Notes
|
||||
-----
|
||||
To replace/update edge data, use the optional key argument
|
||||
to identify a unique edge. Otherwise a new edge will be created.
|
||||
|
||||
NetworkX algorithms designed for weighted graphs cannot use
|
||||
multigraphs directly because it is not clear how to handle
|
||||
multiedge weights. Convert to Graph using edge attribute
|
||||
'weight' to enable weighted graph algorithms.
|
||||
|
||||
Default keys are generated using the method `new_edge_key()`.
|
||||
This method can be overridden by subclassing the base class and
|
||||
providing a custom `new_edge_key()` method.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The following all add the edge e=(1, 2) to graph G:
|
||||
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> e = (1, 2)
|
||||
>>> key = G.add_edge(1, 2) # explicit two-node form
|
||||
>>> G.add_edge(*e) # single edge as tuple of two nodes
|
||||
1
|
||||
>>> G.add_edges_from([(1, 2)]) # add edges from iterable container
|
||||
[2]
|
||||
|
||||
Associate data to edges using keywords:
|
||||
|
||||
>>> key = G.add_edge(1, 2, weight=3)
|
||||
>>> key = G.add_edge(1, 2, key=0, weight=4) # update data for key=0
|
||||
>>> key = G.add_edge(1, 3, weight=7, capacity=15, length=342.7)
|
||||
|
||||
For non-string attribute keys, use subscript notation.
|
||||
|
||||
>>> ekey = G.add_edge(1, 2)
|
||||
>>> G[1][2][0].update({0: 5})
|
||||
>>> G.edges[1, 2, 0].update({0: 5})
|
||||
"""
|
||||
u, v = u_for_edge, v_for_edge
|
||||
# add nodes
|
||||
if u not in self._succ:
|
||||
self._succ[u] = self.adjlist_inner_dict_factory()
|
||||
self._pred[u] = self.adjlist_inner_dict_factory()
|
||||
self._node[u] = self.node_attr_dict_factory()
|
||||
if v not in self._succ:
|
||||
self._succ[v] = self.adjlist_inner_dict_factory()
|
||||
self._pred[v] = self.adjlist_inner_dict_factory()
|
||||
self._node[v] = self.node_attr_dict_factory()
|
||||
if key is None:
|
||||
key = self.new_edge_key(u, v)
|
||||
if v in self._succ[u]:
|
||||
keydict = self._adj[u][v]
|
||||
datadict = keydict.get(key, self.edge_key_dict_factory())
|
||||
datadict.update(attr)
|
||||
keydict[key] = datadict
|
||||
else:
|
||||
# selfloops work this way without special treatment
|
||||
datadict = self.edge_attr_dict_factory()
|
||||
datadict.update(attr)
|
||||
keydict = self.edge_key_dict_factory()
|
||||
keydict[key] = datadict
|
||||
self._succ[u][v] = keydict
|
||||
self._pred[v][u] = keydict
|
||||
return key
|
||||
|
||||
def remove_edge(self, u, v, key=None):
|
||||
"""Remove an edge between u and v.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
u, v : nodes
|
||||
Remove an edge between nodes u and v.
|
||||
key : hashable identifier, optional (default=None)
|
||||
Used to distinguish multiple edges between a pair of nodes.
|
||||
If None remove a single (arbitrary) edge between u and v.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXError
|
||||
If there is not an edge between u and v, or
|
||||
if there is no edge with the specified key.
|
||||
|
||||
See Also
|
||||
--------
|
||||
remove_edges_from : remove a collection of edges
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> nx.add_path(G, [0, 1, 2, 3])
|
||||
>>> G.remove_edge(0, 1)
|
||||
>>> e = (1, 2)
|
||||
>>> G.remove_edge(*e) # unpacks e from an edge tuple
|
||||
|
||||
For multiple edges
|
||||
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> G.add_edges_from([(1, 2), (1, 2), (1, 2)]) # key_list returned
|
||||
[0, 1, 2]
|
||||
>>> G.remove_edge(1, 2) # remove a single (arbitrary) edge
|
||||
|
||||
For edges with keys
|
||||
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> G.add_edge(1, 2, key="first")
|
||||
'first'
|
||||
>>> G.add_edge(1, 2, key="second")
|
||||
'second'
|
||||
>>> G.remove_edge(1, 2, key="second")
|
||||
|
||||
"""
|
||||
try:
|
||||
d = self._adj[u][v]
|
||||
except KeyError as e:
|
||||
raise NetworkXError(f"The edge {u}-{v} is not in the graph.") from e
|
||||
# remove the edge with specified data
|
||||
if key is None:
|
||||
d.popitem()
|
||||
else:
|
||||
try:
|
||||
del d[key]
|
||||
except KeyError as e:
|
||||
msg = f"The edge {u}-{v} with key {key} is not in the graph."
|
||||
raise NetworkXError(msg) from e
|
||||
if len(d) == 0:
|
||||
# remove the key entries if last edge
|
||||
del self._succ[u][v]
|
||||
del self._pred[v][u]
|
||||
|
||||
@property
|
||||
def edges(self):
|
||||
"""An OutMultiEdgeView of the Graph as G.edges or G.edges().
|
||||
|
||||
edges(self, nbunch=None, data=False, keys=False, default=None)
|
||||
|
||||
The OutMultiEdgeView provides set-like operations on the edge-tuples
|
||||
as well as edge attribute lookup. When called, it also provides
|
||||
an EdgeDataView object which allows control of access to edge
|
||||
attributes (but does not provide set-like operations).
|
||||
Hence, `G.edges[u, v]['color']` provides the value of the color
|
||||
attribute for edge `(u, v)` while
|
||||
`for (u, v, c) in G.edges(data='color', default='red'):`
|
||||
iterates through all the edges yielding the color attribute
|
||||
with default `'red'` if no color attribute exists.
|
||||
|
||||
Edges are returned as tuples with optional data and keys
|
||||
in the order (node, neighbor, key, data).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nbunch : single node, container, or all nodes (default= all nodes)
|
||||
The view will only report edges incident to these nodes.
|
||||
data : string or bool, optional (default=False)
|
||||
The edge attribute returned in 3-tuple (u, v, ddict[data]).
|
||||
If True, return edge attribute dict in 3-tuple (u, v, ddict).
|
||||
If False, return 2-tuple (u, v).
|
||||
keys : bool, optional (default=False)
|
||||
If True, return edge keys with each edge.
|
||||
default : value, optional (default=None)
|
||||
Value used for edges that don't have the requested attribute.
|
||||
Only relevant if data is not True or False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges : EdgeView
|
||||
A view of edge attributes, usually it iterates over (u, v)
|
||||
(u, v, k) or (u, v, k, d) tuples of edges, but can also be
|
||||
used for attribute lookup as `edges[u, v, k]['foo']`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Nodes in nbunch that are not in the graph will be (quietly) ignored.
|
||||
For directed graphs this returns the out-edges.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> nx.add_path(G, [0, 1, 2])
|
||||
>>> key = G.add_edge(2, 3, weight=5)
|
||||
>>> [e for e in G.edges()]
|
||||
[(0, 1), (1, 2), (2, 3)]
|
||||
>>> list(G.edges(data=True)) # default data is {} (empty dict)
|
||||
[(0, 1, {}), (1, 2, {}), (2, 3, {'weight': 5})]
|
||||
>>> list(G.edges(data="weight", default=1))
|
||||
[(0, 1, 1), (1, 2, 1), (2, 3, 5)]
|
||||
>>> list(G.edges(keys=True)) # default keys are integers
|
||||
[(0, 1, 0), (1, 2, 0), (2, 3, 0)]
|
||||
>>> list(G.edges(data=True, keys=True))
|
||||
[(0, 1, 0, {}), (1, 2, 0, {}), (2, 3, 0, {'weight': 5})]
|
||||
>>> list(G.edges(data="weight", default=1, keys=True))
|
||||
[(0, 1, 0, 1), (1, 2, 0, 1), (2, 3, 0, 5)]
|
||||
>>> list(G.edges([0, 2]))
|
||||
[(0, 1), (2, 3)]
|
||||
>>> list(G.edges(0))
|
||||
[(0, 1)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
in_edges, out_edges
|
||||
"""
|
||||
return OutMultiEdgeView(self)
|
||||
|
||||
# alias out_edges to edges
|
||||
out_edges = edges
|
||||
|
||||
@property
|
||||
def in_edges(self):
|
||||
"""An InMultiEdgeView of the Graph as G.in_edges or G.in_edges().
|
||||
|
||||
in_edges(self, nbunch=None, data=False, keys=False, default=None)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nbunch : single node, container, or all nodes (default= all nodes)
|
||||
The view will only report edges incident to these nodes.
|
||||
data : string or bool, optional (default=False)
|
||||
The edge attribute returned in 3-tuple (u, v, ddict[data]).
|
||||
If True, return edge attribute dict in 3-tuple (u, v, ddict).
|
||||
If False, return 2-tuple (u, v).
|
||||
keys : bool, optional (default=False)
|
||||
If True, return edge keys with each edge.
|
||||
default : value, optional (default=None)
|
||||
Value used for edges that don't have the requested attribute.
|
||||
Only relevant if data is not True or False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
in_edges : InMultiEdgeView
|
||||
A view of edge attributes, usually it iterates over (u, v)
|
||||
or (u, v, k) or (u, v, k, d) tuples of edges, but can also be
|
||||
used for attribute lookup as `edges[u, v, k]['foo']`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
edges
|
||||
"""
|
||||
return InMultiEdgeView(self)
|
||||
|
||||
@property
|
||||
def degree(self):
|
||||
"""A DegreeView for the Graph as G.degree or G.degree().
|
||||
|
||||
The node degree is the number of edges adjacent to the node.
|
||||
The weighted node degree is the sum of the edge weights for
|
||||
edges incident to that node.
|
||||
|
||||
This object provides an iterator for (node, degree) as well as
|
||||
lookup for the degree for a single node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nbunch : single node, container, or all nodes (default= all nodes)
|
||||
The view will only report edges incident to these nodes.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
The name of an edge attribute that holds the numerical value used
|
||||
as a weight. If None, then each edge has weight 1.
|
||||
The degree is the sum of the edge weights adjacent to the node.
|
||||
|
||||
Returns
|
||||
-------
|
||||
If a single nodes is requested
|
||||
deg : int
|
||||
Degree of the node
|
||||
|
||||
OR if multiple nodes are requested
|
||||
nd_iter : iterator
|
||||
The iterator returns two-tuples of (node, degree).
|
||||
|
||||
See Also
|
||||
--------
|
||||
out_degree, in_degree
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> nx.add_path(G, [0, 1, 2, 3])
|
||||
>>> G.degree(0) # node 0 with degree 1
|
||||
1
|
||||
>>> list(G.degree([0, 1, 2]))
|
||||
[(0, 1), (1, 2), (2, 2)]
|
||||
|
||||
"""
|
||||
return DiMultiDegreeView(self)
|
||||
|
||||
@property
|
||||
def in_degree(self):
|
||||
"""A DegreeView for (node, in_degree) or in_degree for single node.
|
||||
|
||||
The node in-degree is the number of edges pointing in to the node.
|
||||
The weighted node degree is the sum of the edge weights for
|
||||
edges incident to that node.
|
||||
|
||||
This object provides an iterator for (node, degree) as well as
|
||||
lookup for the degree for a single node.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nbunch : single node, container, or all nodes (default= all nodes)
|
||||
The view will only report edges incident to these nodes.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
The edge attribute that holds the numerical value used
|
||||
as a weight. If None, then each edge has weight 1.
|
||||
The degree is the sum of the edge weights adjacent to the node.
|
||||
|
||||
Returns
|
||||
-------
|
||||
If a single node is requested
|
||||
deg : int
|
||||
Degree of the node
|
||||
|
||||
OR if multiple nodes are requested
|
||||
nd_iter : iterator
|
||||
The iterator returns two-tuples of (node, in-degree).
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree, out_degree
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> nx.add_path(G, [0, 1, 2, 3])
|
||||
>>> G.in_degree(0) # node 0 with degree 0
|
||||
0
|
||||
>>> list(G.in_degree([0, 1, 2]))
|
||||
[(0, 0), (1, 1), (2, 1)]
|
||||
|
||||
"""
|
||||
return InMultiDegreeView(self)
|
||||
|
||||
@property
|
||||
def out_degree(self):
|
||||
"""Returns an iterator for (node, out-degree) or out-degree for single node.
|
||||
|
||||
out_degree(self, nbunch=None, weight=None)
|
||||
|
||||
The node out-degree is the number of edges pointing out of the node.
|
||||
This function returns the out-degree for a single node or an iterator
|
||||
for a bunch of nodes or if nothing is passed as argument.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nbunch : single node, container, or all nodes (default= all nodes)
|
||||
The view will only report edges incident to these nodes.
|
||||
|
||||
weight : string or None, optional (default=None)
|
||||
The edge attribute that holds the numerical value used
|
||||
as a weight. If None, then each edge has weight 1.
|
||||
The degree is the sum of the edge weights.
|
||||
|
||||
Returns
|
||||
-------
|
||||
If a single node is requested
|
||||
deg : int
|
||||
Degree of the node
|
||||
|
||||
OR if multiple nodes are requested
|
||||
nd_iter : iterator
|
||||
The iterator returns two-tuples of (node, out-degree).
|
||||
|
||||
See Also
|
||||
--------
|
||||
degree, in_degree
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.MultiDiGraph()
|
||||
>>> nx.add_path(G, [0, 1, 2, 3])
|
||||
>>> G.out_degree(0) # node 0 with degree 1
|
||||
1
|
||||
>>> list(G.out_degree([0, 1, 2]))
|
||||
[(0, 1), (1, 1), (2, 1)]
|
||||
|
||||
"""
|
||||
return OutMultiDegreeView(self)
|
||||
|
||||
def is_multigraph(self):
|
||||
"""Returns True if graph is a multigraph, False otherwise."""
|
||||
return True
|
||||
|
||||
def is_directed(self):
|
||||
"""Returns True if graph is directed, False otherwise."""
|
||||
return True
|
||||
|
||||
def to_undirected(self, reciprocal=False, as_view=False):
|
||||
"""Returns an undirected representation of the digraph.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
reciprocal : bool (optional)
|
||||
If True only keep edges that appear in both directions
|
||||
in the original digraph.
|
||||
as_view : bool (optional, default=False)
|
||||
If True return an undirected view of the original directed graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
G : MultiGraph
|
||||
An undirected graph with the same name and nodes and
|
||||
with edge (u, v, data) if either (u, v, data) or (v, u, data)
|
||||
is in the digraph. If both edges exist in digraph and
|
||||
their edge data is different, only one edge is created
|
||||
with an arbitrary choice of which edge data to use.
|
||||
You must check and correct for this manually if desired.
|
||||
|
||||
See Also
|
||||
--------
|
||||
MultiGraph, copy, add_edge, add_edges_from
|
||||
|
||||
Notes
|
||||
-----
|
||||
This returns a "deepcopy" of the edge, node, and
|
||||
graph attributes which attempts to completely copy
|
||||
all of the data and references.
|
||||
|
||||
This is in contrast to the similar D=MultiiGraph(G) which
|
||||
returns a shallow copy of the data.
|
||||
|
||||
See the Python copy module for more information on shallow
|
||||
and deep copies, https://docs.python.org/3/library/copy.html.
|
||||
|
||||
Warning: If you have subclassed MultiDiGraph to use dict-like
|
||||
objects in the data structure, those changes do not transfer
|
||||
to the MultiGraph created by this method.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(2) # or MultiGraph, etc
|
||||
>>> H = G.to_directed()
|
||||
>>> list(H.edges)
|
||||
[(0, 1), (1, 0)]
|
||||
>>> G2 = H.to_undirected()
|
||||
>>> list(G2.edges)
|
||||
[(0, 1)]
|
||||
"""
|
||||
graph_class = self.to_undirected_class()
|
||||
if as_view is True:
|
||||
return nx.graphviews.generic_graph_view(self, graph_class)
|
||||
# deepcopy when not a view
|
||||
G = graph_class()
|
||||
G.graph.update(deepcopy(self.graph))
|
||||
G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
|
||||
if reciprocal is True:
|
||||
G.add_edges_from(
|
||||
(u, v, key, deepcopy(data))
|
||||
for u, nbrs in self._adj.items()
|
||||
for v, keydict in nbrs.items()
|
||||
for key, data in keydict.items()
|
||||
if v in self._pred[u] and key in self._pred[u][v]
|
||||
)
|
||||
else:
|
||||
G.add_edges_from(
|
||||
(u, v, key, deepcopy(data))
|
||||
for u, nbrs in self._adj.items()
|
||||
for v, keydict in nbrs.items()
|
||||
for key, data in keydict.items()
|
||||
)
|
||||
return G
|
||||
|
||||
def reverse(self, copy=True):
|
||||
"""Returns the reverse of the graph.
|
||||
|
||||
The reverse is a graph with the same nodes and edges
|
||||
but with the directions of the edges reversed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
copy : bool optional (default=True)
|
||||
If True, return a new DiGraph holding the reversed edges.
|
||||
If False, the reverse graph is created using a view of
|
||||
the original graph.
|
||||
"""
|
||||
if copy:
|
||||
H = self.__class__()
|
||||
H.graph.update(deepcopy(self.graph))
|
||||
H.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
|
||||
H.add_edges_from(
|
||||
(v, u, k, deepcopy(d))
|
||||
for u, v, k, d in self.edges(keys=True, data=True)
|
||||
)
|
||||
return H
|
||||
return nx.graphviews.reverse_view(self)
|
1134
venv/Lib/site-packages/networkx/classes/multigraph.py
Normal file
1134
venv/Lib/site-packages/networkx/classes/multigraph.py
Normal file
File diff suppressed because it is too large
Load diff
79
venv/Lib/site-packages/networkx/classes/ordered.py
Normal file
79
venv/Lib/site-packages/networkx/classes/ordered.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
"""
|
||||
Consistently ordered variants of the default base classes.
|
||||
Note that if you are using Python 3.6+, you shouldn't need these classes
|
||||
because the dicts in Python 3.6+ are ordered.
|
||||
Note also that there are many differing expectations for the word "ordered"
|
||||
and that these classes may not provide the order you expect.
|
||||
The intent here is to give a consistent order not a particular order.
|
||||
|
||||
The Ordered (Di/Multi/MultiDi) Graphs give a consistent order for reporting of
|
||||
nodes and edges. The order of node reporting agrees with node adding, but for
|
||||
edges, the order is not necessarily the order that the edges were added.
|
||||
|
||||
In general, you should use the default (i.e., unordered) graph classes.
|
||||
However, there are times (e.g., when testing) when you may need the
|
||||
order preserved.
|
||||
|
||||
Special care is required when using subgraphs of the Ordered classes.
|
||||
The order of nodes in the subclass is not necessarily the same order
|
||||
as the original class. In general it is probably better to avoid using
|
||||
subgraphs and replace with code similar to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# instead of SG = G.subgraph(ordered_nodes)
|
||||
SG = nx.OrderedGraph()
|
||||
SG.add_nodes_from(ordered_nodes)
|
||||
SG.add_edges_from((u, v) for (u, v) in G.edges() if u in SG if v in SG)
|
||||
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
|
||||
from .graph import Graph
|
||||
from .multigraph import MultiGraph
|
||||
from .digraph import DiGraph
|
||||
from .multidigraph import MultiDiGraph
|
||||
|
||||
__all__ = []
|
||||
|
||||
__all__.extend(
|
||||
["OrderedGraph", "OrderedDiGraph", "OrderedMultiGraph", "OrderedMultiDiGraph"]
|
||||
)
|
||||
|
||||
|
||||
class OrderedGraph(Graph):
|
||||
"""Consistently ordered variant of :class:`~networkx.Graph`."""
|
||||
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
|
||||
class OrderedDiGraph(DiGraph):
|
||||
"""Consistently ordered variant of :class:`~networkx.DiGraph`."""
|
||||
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
|
||||
class OrderedMultiGraph(MultiGraph):
|
||||
"""Consistently ordered variant of :class:`~networkx.MultiGraph`."""
|
||||
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_key_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
|
||||
class OrderedMultiDiGraph(MultiDiGraph):
|
||||
"""Consistently ordered variant of :class:`~networkx.MultiDiGraph`."""
|
||||
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_key_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
1274
venv/Lib/site-packages/networkx/classes/reportviews.py
Normal file
1274
venv/Lib/site-packages/networkx/classes/reportviews.py
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,474 @@
|
|||
"""Original NetworkX graph tests"""
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import convert_node_labels_to_integers as cnlti
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
|
||||
class HistoricalTests:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.null = nx.null_graph()
|
||||
cls.P1 = cnlti(nx.path_graph(1), first_label=1)
|
||||
cls.P3 = cnlti(nx.path_graph(3), first_label=1)
|
||||
cls.P10 = cnlti(nx.path_graph(10), first_label=1)
|
||||
cls.K1 = cnlti(nx.complete_graph(1), first_label=1)
|
||||
cls.K3 = cnlti(nx.complete_graph(3), first_label=1)
|
||||
cls.K4 = cnlti(nx.complete_graph(4), first_label=1)
|
||||
cls.K5 = cnlti(nx.complete_graph(5), first_label=1)
|
||||
cls.K10 = cnlti(nx.complete_graph(10), first_label=1)
|
||||
cls.G = nx.Graph
|
||||
|
||||
def test_name(self):
|
||||
G = self.G(name="test")
|
||||
assert str(G) == "test"
|
||||
assert G.name == "test"
|
||||
H = self.G()
|
||||
assert H.name == ""
|
||||
|
||||
# Nodes
|
||||
|
||||
def test_add_remove_node(self):
|
||||
G = self.G()
|
||||
G.add_node("A")
|
||||
assert G.has_node("A")
|
||||
G.remove_node("A")
|
||||
assert not G.has_node("A")
|
||||
|
||||
def test_nonhashable_node(self):
|
||||
# Test if a non-hashable object is in the Graph. A python dict will
|
||||
# raise a TypeError, but for a Graph class a simple False should be
|
||||
# returned (see Graph __contains__). If it cannot be a node then it is
|
||||
# not a node.
|
||||
G = self.G()
|
||||
assert not G.has_node(["A"])
|
||||
assert not G.has_node({"A": 1})
|
||||
|
||||
def test_add_nodes_from(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from(list("ABCDEFGHIJKL"))
|
||||
assert G.has_node("L")
|
||||
G.remove_nodes_from(["H", "I", "J", "K", "L"])
|
||||
G.add_nodes_from([1, 2, 3, 4])
|
||||
assert sorted(G.nodes(), key=str) == [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
]
|
||||
# test __iter__
|
||||
assert sorted(G, key=str) == [1, 2, 3, 4, "A", "B", "C", "D", "E", "F", "G"]
|
||||
|
||||
def test_contains(self):
|
||||
G = self.G()
|
||||
G.add_node("A")
|
||||
assert "A" in G
|
||||
assert not [] in G # never raise a Key or TypeError in this test
|
||||
assert not {1: 1} in G
|
||||
|
||||
def test_add_remove(self):
|
||||
# Test add_node and remove_node acting for various nbunch
|
||||
G = self.G()
|
||||
G.add_node("m")
|
||||
assert G.has_node("m")
|
||||
G.add_node("m") # no complaints
|
||||
pytest.raises(nx.NetworkXError, G.remove_node, "j")
|
||||
G.remove_node("m")
|
||||
assert list(G) == []
|
||||
|
||||
def test_nbunch_is_list(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from(list("ABCD"))
|
||||
G.add_nodes_from(self.P3) # add nbunch of nodes (nbunch=Graph)
|
||||
assert sorted(G.nodes(), key=str) == [1, 2, 3, "A", "B", "C", "D"]
|
||||
G.remove_nodes_from(self.P3) # remove nbunch of nodes (nbunch=Graph)
|
||||
assert sorted(G.nodes(), key=str) == ["A", "B", "C", "D"]
|
||||
|
||||
def test_nbunch_is_set(self):
|
||||
G = self.G()
|
||||
nbunch = set("ABCDEFGHIJKL")
|
||||
G.add_nodes_from(nbunch)
|
||||
assert G.has_node("L")
|
||||
|
||||
def test_nbunch_dict(self):
|
||||
# nbunch is a dict with nodes as keys
|
||||
G = self.G()
|
||||
nbunch = set("ABCDEFGHIJKL")
|
||||
G.add_nodes_from(nbunch)
|
||||
nbunch = {"I": "foo", "J": 2, "K": True, "L": "spam"}
|
||||
G.remove_nodes_from(nbunch)
|
||||
assert sorted(G.nodes(), key=str), ["A", "B", "C", "D", "E", "F", "G", "H"]
|
||||
|
||||
def test_nbunch_iterator(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from(["A", "B", "C", "D", "E", "F", "G", "H"])
|
||||
n_iter = self.P3.nodes()
|
||||
G.add_nodes_from(n_iter)
|
||||
assert sorted(G.nodes(), key=str) == [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
]
|
||||
n_iter = self.P3.nodes() # rebuild same iterator
|
||||
G.remove_nodes_from(n_iter) # remove nbunch of nodes (nbunch=iterator)
|
||||
assert sorted(G.nodes(), key=str) == ["A", "B", "C", "D", "E", "F", "G", "H"]
|
||||
|
||||
def test_nbunch_graph(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from(["A", "B", "C", "D", "E", "F", "G", "H"])
|
||||
nbunch = self.K3
|
||||
G.add_nodes_from(nbunch)
|
||||
assert sorted(G.nodes(), key=str), [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
]
|
||||
|
||||
# Edges
|
||||
|
||||
def test_add_edge(self):
|
||||
G = self.G()
|
||||
pytest.raises(TypeError, G.add_edge, "A")
|
||||
|
||||
G.add_edge("A", "B") # testing add_edge()
|
||||
G.add_edge("A", "B") # should fail silently
|
||||
assert G.has_edge("A", "B")
|
||||
assert not G.has_edge("A", "C")
|
||||
assert G.has_edge(*("A", "B"))
|
||||
if G.is_directed():
|
||||
assert not G.has_edge("B", "A")
|
||||
else:
|
||||
# G is undirected, so B->A is an edge
|
||||
assert G.has_edge("B", "A")
|
||||
|
||||
G.add_edge("A", "C") # test directedness
|
||||
G.add_edge("C", "A")
|
||||
G.remove_edge("C", "A")
|
||||
if G.is_directed():
|
||||
assert G.has_edge("A", "C")
|
||||
else:
|
||||
assert not G.has_edge("A", "C")
|
||||
assert not G.has_edge("C", "A")
|
||||
|
||||
def test_self_loop(self):
|
||||
G = self.G()
|
||||
G.add_edge("A", "A") # test self loops
|
||||
assert G.has_edge("A", "A")
|
||||
G.remove_edge("A", "A")
|
||||
G.add_edge("X", "X")
|
||||
assert G.has_node("X")
|
||||
G.remove_node("X")
|
||||
G.add_edge("A", "Z") # should add the node silently
|
||||
assert G.has_node("Z")
|
||||
|
||||
def test_add_edges_from(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("B", "C")]) # test add_edges_from()
|
||||
assert G.has_edge("B", "C")
|
||||
if G.is_directed():
|
||||
assert not G.has_edge("C", "B")
|
||||
else:
|
||||
assert G.has_edge("C", "B") # undirected
|
||||
|
||||
G.add_edges_from([("D", "F"), ("B", "D")])
|
||||
assert G.has_edge("D", "F")
|
||||
assert G.has_edge("B", "D")
|
||||
|
||||
if G.is_directed():
|
||||
assert not G.has_edge("D", "B")
|
||||
else:
|
||||
assert G.has_edge("D", "B") # undirected
|
||||
|
||||
def test_add_edges_from2(self):
|
||||
G = self.G()
|
||||
# after failing silently, should add 2nd edge
|
||||
G.add_edges_from([tuple("IJ"), list("KK"), tuple("JK")])
|
||||
assert G.has_edge(*("I", "J"))
|
||||
assert G.has_edge(*("K", "K"))
|
||||
assert G.has_edge(*("J", "K"))
|
||||
if G.is_directed():
|
||||
assert not G.has_edge(*("K", "J"))
|
||||
else:
|
||||
assert G.has_edge(*("K", "J"))
|
||||
|
||||
def test_add_edges_from3(self):
|
||||
G = self.G()
|
||||
G.add_edges_from(zip(list("ACD"), list("CDE")))
|
||||
assert G.has_edge("D", "E")
|
||||
assert not G.has_edge("E", "C")
|
||||
|
||||
def test_remove_edge(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from([1, 2, 3, "A", "B", "C", "D", "E", "F", "G", "H"])
|
||||
|
||||
G.add_edges_from(zip(list("MNOP"), list("NOPM")))
|
||||
assert G.has_edge("O", "P")
|
||||
assert G.has_edge("P", "M")
|
||||
G.remove_node("P") # tests remove_node()'s handling of edges.
|
||||
assert not G.has_edge("P", "M")
|
||||
pytest.raises(TypeError, G.remove_edge, "M")
|
||||
|
||||
G.add_edge("N", "M")
|
||||
assert G.has_edge("M", "N")
|
||||
G.remove_edge("M", "N")
|
||||
assert not G.has_edge("M", "N")
|
||||
|
||||
# self loop fails silently
|
||||
G.remove_edges_from([list("HI"), list("DF"), tuple("KK"), tuple("JK")])
|
||||
assert not G.has_edge("H", "I")
|
||||
assert not G.has_edge("J", "K")
|
||||
G.remove_edges_from([list("IJ"), list("KK"), list("JK")])
|
||||
assert not G.has_edge("I", "J")
|
||||
G.remove_nodes_from(set("ZEFHIMNO"))
|
||||
G.add_edge("J", "K")
|
||||
|
||||
def test_edges_nbunch(self):
|
||||
# Test G.edges(nbunch) with various forms of nbunch
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
# node not in nbunch should be quietly ignored
|
||||
pytest.raises(nx.NetworkXError, G.edges, 6)
|
||||
assert list(G.edges("Z")) == [] # iterable non-node
|
||||
# nbunch can be an empty list
|
||||
assert list(G.edges([])) == []
|
||||
if G.is_directed():
|
||||
elist = [("A", "B"), ("A", "C"), ("B", "D")]
|
||||
else:
|
||||
elist = [("A", "B"), ("A", "C"), ("B", "C"), ("B", "D")]
|
||||
# nbunch can be a list
|
||||
assert_edges_equal(list(G.edges(["A", "B"])), elist)
|
||||
# nbunch can be a set
|
||||
assert_edges_equal(G.edges({"A", "B"}), elist)
|
||||
# nbunch can be a graph
|
||||
G1 = self.G()
|
||||
G1.add_nodes_from("AB")
|
||||
assert_edges_equal(G.edges(G1), elist)
|
||||
# nbunch can be a dict with nodes as keys
|
||||
ndict = {"A": "thing1", "B": "thing2"}
|
||||
assert_edges_equal(G.edges(ndict), elist)
|
||||
# nbunch can be a single node
|
||||
assert_edges_equal(list(G.edges("A")), [("A", "B"), ("A", "C")])
|
||||
assert_nodes_equal(sorted(G), ["A", "B", "C", "D"])
|
||||
|
||||
# nbunch can be nothing (whole graph)
|
||||
assert_edges_equal(
|
||||
list(G.edges()),
|
||||
[("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")],
|
||||
)
|
||||
|
||||
def test_degree(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
assert G.degree("A") == 2
|
||||
|
||||
# degree of single node in iterable container must return dict
|
||||
assert list(G.degree(["A"])) == [("A", 2)]
|
||||
assert sorted(d for n, d in G.degree(["A", "B"])) == [2, 3]
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 3, 3]
|
||||
|
||||
def test_degree2(self):
|
||||
H = self.G()
|
||||
H.add_edges_from([(1, 24), (1, 2)])
|
||||
assert sorted(d for n, d in H.degree([1, 24])) == [1, 2]
|
||||
|
||||
def test_degree_graph(self):
|
||||
P3 = nx.path_graph(3)
|
||||
P5 = nx.path_graph(5)
|
||||
# silently ignore nodes not in P3
|
||||
assert dict(d for n, d in P3.degree(["A", "B"])) == {}
|
||||
# nbunch can be a graph
|
||||
assert sorted(d for n, d in P5.degree(P3)) == [1, 2, 2]
|
||||
# nbunch can be a graph that's way too big
|
||||
assert sorted(d for n, d in P3.degree(P5)) == [1, 1, 2]
|
||||
assert list(P5.degree([])) == []
|
||||
assert dict(P5.degree([])) == {}
|
||||
|
||||
def test_null(self):
|
||||
null = nx.null_graph()
|
||||
assert list(null.degree()) == []
|
||||
assert dict(null.degree()) == {}
|
||||
|
||||
def test_order_size(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
assert G.order() == 4
|
||||
assert G.size() == 5
|
||||
assert G.number_of_edges() == 5
|
||||
assert G.number_of_edges("A", "B") == 1
|
||||
assert G.number_of_edges("A", "D") == 0
|
||||
|
||||
def test_copy(self):
|
||||
G = self.G()
|
||||
H = G.copy() # copy
|
||||
assert H.adj == G.adj
|
||||
assert H.name == G.name
|
||||
assert H != G
|
||||
|
||||
def test_subgraph(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
SG = G.subgraph(["A", "B", "D"])
|
||||
assert_nodes_equal(list(SG), ["A", "B", "D"])
|
||||
assert_edges_equal(list(SG.edges()), [("A", "B"), ("B", "D")])
|
||||
|
||||
def test_to_directed(self):
|
||||
G = self.G()
|
||||
if not G.is_directed():
|
||||
G.add_edges_from(
|
||||
[("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")]
|
||||
)
|
||||
|
||||
DG = G.to_directed()
|
||||
assert DG != G # directed copy or copy
|
||||
|
||||
assert DG.is_directed()
|
||||
assert DG.name == G.name
|
||||
assert DG.adj == G.adj
|
||||
assert sorted(DG.out_edges(list("AB"))) == [
|
||||
("A", "B"),
|
||||
("A", "C"),
|
||||
("B", "A"),
|
||||
("B", "C"),
|
||||
("B", "D"),
|
||||
]
|
||||
DG.remove_edge("A", "B")
|
||||
assert DG.has_edge("B", "A") # this removes B-A but not A-B
|
||||
assert not DG.has_edge("A", "B")
|
||||
|
||||
def test_to_undirected(self):
|
||||
G = self.G()
|
||||
if G.is_directed():
|
||||
G.add_edges_from(
|
||||
[("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")]
|
||||
)
|
||||
UG = G.to_undirected() # to_undirected
|
||||
assert UG != G
|
||||
assert not UG.is_directed()
|
||||
assert G.is_directed()
|
||||
assert UG.name == G.name
|
||||
assert UG.adj != G.adj
|
||||
assert sorted(UG.edges(list("AB"))) == [
|
||||
("A", "B"),
|
||||
("A", "C"),
|
||||
("B", "C"),
|
||||
("B", "D"),
|
||||
]
|
||||
assert sorted(UG.edges(["A", "B"])) == [
|
||||
("A", "B"),
|
||||
("A", "C"),
|
||||
("B", "C"),
|
||||
("B", "D"),
|
||||
]
|
||||
UG.remove_edge("A", "B")
|
||||
assert not UG.has_edge("B", "A")
|
||||
assert not UG.has_edge("A", "B")
|
||||
|
||||
def test_neighbors(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
G.add_nodes_from("GJK")
|
||||
assert sorted(G["A"]) == ["B", "C"]
|
||||
assert sorted(G.neighbors("A")) == ["B", "C"]
|
||||
assert sorted(G.neighbors("A")) == ["B", "C"]
|
||||
assert sorted(G.neighbors("G")) == []
|
||||
pytest.raises(nx.NetworkXError, G.neighbors, "j")
|
||||
|
||||
def test_iterators(self):
|
||||
G = self.G()
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")])
|
||||
G.add_nodes_from("GJK")
|
||||
assert sorted(G.nodes()) == ["A", "B", "C", "D", "G", "J", "K"]
|
||||
assert_edges_equal(
|
||||
G.edges(), [("A", "B"), ("A", "C"), ("B", "D"), ("C", "B"), ("C", "D")]
|
||||
)
|
||||
|
||||
assert sorted([v for k, v in G.degree()]) == [0, 0, 0, 2, 2, 3, 3]
|
||||
assert sorted(G.degree(), key=str) == [
|
||||
("A", 2),
|
||||
("B", 3),
|
||||
("C", 3),
|
||||
("D", 2),
|
||||
("G", 0),
|
||||
("J", 0),
|
||||
("K", 0),
|
||||
]
|
||||
assert sorted(G.neighbors("A")) == ["B", "C"]
|
||||
pytest.raises(nx.NetworkXError, G.neighbors, "X")
|
||||
G.clear()
|
||||
assert nx.number_of_nodes(G) == 0
|
||||
assert nx.number_of_edges(G) == 0
|
||||
|
||||
def test_null_subgraph(self):
|
||||
# Subgraph of a null graph is a null graph
|
||||
nullgraph = nx.null_graph()
|
||||
G = nx.null_graph()
|
||||
H = G.subgraph([])
|
||||
assert nx.is_isomorphic(H, nullgraph)
|
||||
|
||||
def test_empty_subgraph(self):
|
||||
# Subgraph of an empty graph is an empty graph. test 1
|
||||
nullgraph = nx.null_graph()
|
||||
E5 = nx.empty_graph(5)
|
||||
E10 = nx.empty_graph(10)
|
||||
H = E10.subgraph([])
|
||||
assert nx.is_isomorphic(H, nullgraph)
|
||||
H = E10.subgraph([1, 2, 3, 4, 5])
|
||||
assert nx.is_isomorphic(H, E5)
|
||||
|
||||
def test_complete_subgraph(self):
|
||||
# Subgraph of a complete graph is a complete graph
|
||||
K1 = nx.complete_graph(1)
|
||||
K3 = nx.complete_graph(3)
|
||||
K5 = nx.complete_graph(5)
|
||||
H = K5.subgraph([1, 2, 3])
|
||||
assert nx.is_isomorphic(H, K3)
|
||||
|
||||
def test_subgraph_nbunch(self):
|
||||
nullgraph = nx.null_graph()
|
||||
K1 = nx.complete_graph(1)
|
||||
K3 = nx.complete_graph(3)
|
||||
K5 = nx.complete_graph(5)
|
||||
# Test G.subgraph(nbunch), where nbunch is a single node
|
||||
H = K5.subgraph(1)
|
||||
assert nx.is_isomorphic(H, K1)
|
||||
# Test G.subgraph(nbunch), where nbunch is a set
|
||||
H = K5.subgraph({1})
|
||||
assert nx.is_isomorphic(H, K1)
|
||||
# Test G.subgraph(nbunch), where nbunch is an iterator
|
||||
H = K5.subgraph(iter(K3))
|
||||
assert nx.is_isomorphic(H, K3)
|
||||
# Test G.subgraph(nbunch), where nbunch is another graph
|
||||
H = K5.subgraph(K3)
|
||||
assert nx.is_isomorphic(H, K3)
|
||||
H = K5.subgraph([9])
|
||||
assert nx.is_isomorphic(H, nullgraph)
|
||||
|
||||
def test_node_tuple_issue(self):
|
||||
H = self.G()
|
||||
# Test error handling of tuple as a node
|
||||
pytest.raises(nx.NetworkXError, H.remove_node, (1, 2))
|
||||
H.remove_nodes_from([(1, 2)]) # no error
|
||||
pytest.raises(nx.NetworkXError, H.neighbors, (1, 2))
|
356
venv/Lib/site-packages/networkx/classes/tests/test_coreviews.py
Normal file
356
venv/Lib/site-packages/networkx/classes/tests/test_coreviews.py
Normal file
|
@ -0,0 +1,356 @@
|
|||
import pytest
|
||||
import pickle
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestAtlasView:
|
||||
# node->data
|
||||
def setup(self):
|
||||
self.d = {0: {"color": "blue", "weight": 1.2}, 1: {}, 2: {"color": 1}}
|
||||
self.av = nx.classes.coreviews.AtlasView(self.d)
|
||||
|
||||
def test_pickle(self):
|
||||
view = self.av
|
||||
pview = pickle.loads(pickle.dumps(view, -1))
|
||||
assert view == pview
|
||||
assert view.__slots__ == pview.__slots__
|
||||
pview = pickle.loads(pickle.dumps(view))
|
||||
assert view == pview
|
||||
assert view.__slots__ == pview.__slots__
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.av) == len(self.d)
|
||||
|
||||
def test_iter(self):
|
||||
assert list(self.av) == list(self.d)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.av[1] is self.d[1]
|
||||
assert self.av[2]["color"] == 1
|
||||
pytest.raises(KeyError, self.av.__getitem__, 3)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.av.copy()
|
||||
assert avcopy[0] == self.av[0]
|
||||
assert avcopy == self.av
|
||||
assert avcopy[0] is not self.av[0]
|
||||
assert avcopy is not self.av
|
||||
avcopy[5] = {}
|
||||
assert avcopy != self.av
|
||||
|
||||
avcopy[0]["ht"] = 4
|
||||
assert avcopy[0] != self.av[0]
|
||||
self.av[0]["ht"] = 4
|
||||
assert avcopy[0] == self.av[0]
|
||||
del self.av[0]["ht"]
|
||||
|
||||
assert not hasattr(self.av, "__setitem__")
|
||||
|
||||
def test_items(self):
|
||||
assert sorted(self.av.items()) == sorted(self.d.items())
|
||||
|
||||
def test_str(self):
|
||||
out = str(self.d)
|
||||
assert str(self.av) == out
|
||||
|
||||
def test_repr(self):
|
||||
out = "AtlasView(" + str(self.d) + ")"
|
||||
assert repr(self.av) == out
|
||||
|
||||
|
||||
class TestAdjacencyView:
|
||||
# node->nbr->data
|
||||
def setup(self):
|
||||
dd = {"color": "blue", "weight": 1.2}
|
||||
self.nd = {0: dd, 1: {}, 2: {"color": 1}}
|
||||
self.adj = {3: self.nd, 0: {3: dd}, 1: {}, 2: {3: {"color": 1}}}
|
||||
self.adjview = nx.classes.coreviews.AdjacencyView(self.adj)
|
||||
|
||||
def test_pickle(self):
|
||||
view = self.adjview
|
||||
pview = pickle.loads(pickle.dumps(view, -1))
|
||||
assert view == pview
|
||||
assert view.__slots__ == pview.__slots__
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.adjview) == len(self.adj)
|
||||
|
||||
def test_iter(self):
|
||||
assert list(self.adjview) == list(self.adj)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.adjview[1] is not self.adj[1]
|
||||
assert self.adjview[3][0] is self.adjview[0][3]
|
||||
assert self.adjview[2][3]["color"] == 1
|
||||
pytest.raises(KeyError, self.adjview.__getitem__, 4)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.adjview.copy()
|
||||
assert avcopy[0] == self.adjview[0]
|
||||
assert avcopy[0] is not self.adjview[0]
|
||||
|
||||
avcopy[2][3]["ht"] = 4
|
||||
assert avcopy[2] != self.adjview[2]
|
||||
self.adjview[2][3]["ht"] = 4
|
||||
assert avcopy[2] == self.adjview[2]
|
||||
del self.adjview[2][3]["ht"]
|
||||
|
||||
assert not hasattr(self.adjview, "__setitem__")
|
||||
|
||||
def test_items(self):
|
||||
view_items = sorted((n, dict(d)) for n, d in self.adjview.items())
|
||||
assert view_items == sorted(self.adj.items())
|
||||
|
||||
def test_str(self):
|
||||
out = str(dict(self.adj))
|
||||
assert str(self.adjview) == out
|
||||
|
||||
def test_repr(self):
|
||||
out = self.adjview.__class__.__name__ + "(" + str(self.adj) + ")"
|
||||
assert repr(self.adjview) == out
|
||||
|
||||
|
||||
class TestMultiAdjacencyView(TestAdjacencyView):
|
||||
# node->nbr->key->data
|
||||
def setup(self):
|
||||
dd = {"color": "blue", "weight": 1.2}
|
||||
self.kd = {0: dd, 1: {}, 2: {"color": 1}}
|
||||
self.nd = {3: self.kd, 0: {3: dd}, 1: {0: {}}, 2: {3: {"color": 1}}}
|
||||
self.adj = {3: self.nd, 0: {3: {3: dd}}, 1: {}, 2: {3: {8: {}}}}
|
||||
self.adjview = nx.classes.coreviews.MultiAdjacencyView(self.adj)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.adjview[1] is not self.adj[1]
|
||||
assert self.adjview[3][0][3] is self.adjview[0][3][3]
|
||||
assert self.adjview[3][2][3]["color"] == 1
|
||||
pytest.raises(KeyError, self.adjview.__getitem__, 4)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.adjview.copy()
|
||||
assert avcopy[0] == self.adjview[0]
|
||||
assert avcopy[0] is not self.adjview[0]
|
||||
|
||||
avcopy[2][3][8]["ht"] = 4
|
||||
assert avcopy[2] != self.adjview[2]
|
||||
self.adjview[2][3][8]["ht"] = 4
|
||||
assert avcopy[2] == self.adjview[2]
|
||||
del self.adjview[2][3][8]["ht"]
|
||||
|
||||
assert not hasattr(self.adjview, "__setitem__")
|
||||
|
||||
|
||||
class TestUnionAtlas:
|
||||
# node->data
|
||||
def setup(self):
|
||||
self.s = {0: {"color": "blue", "weight": 1.2}, 1: {}, 2: {"color": 1}}
|
||||
self.p = {3: {"color": "blue", "weight": 1.2}, 4: {}, 2: {"watch": 2}}
|
||||
self.av = nx.classes.coreviews.UnionAtlas(self.s, self.p)
|
||||
|
||||
def test_pickle(self):
|
||||
view = self.av
|
||||
pview = pickle.loads(pickle.dumps(view, -1))
|
||||
assert view == pview
|
||||
assert view.__slots__ == pview.__slots__
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.av) == len(self.s) + len(self.p)
|
||||
|
||||
def test_iter(self):
|
||||
assert set(self.av) == set(self.s) | set(self.p)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.av[0] is self.s[0]
|
||||
assert self.av[4] is self.p[4]
|
||||
assert self.av[2]["color"] == 1
|
||||
pytest.raises(KeyError, self.av[2].__getitem__, "watch")
|
||||
pytest.raises(KeyError, self.av.__getitem__, 8)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.av.copy()
|
||||
assert avcopy[0] == self.av[0]
|
||||
assert avcopy[0] is not self.av[0]
|
||||
assert avcopy is not self.av
|
||||
avcopy[5] = {}
|
||||
assert avcopy != self.av
|
||||
|
||||
avcopy[0]["ht"] = 4
|
||||
assert avcopy[0] != self.av[0]
|
||||
self.av[0]["ht"] = 4
|
||||
assert avcopy[0] == self.av[0]
|
||||
del self.av[0]["ht"]
|
||||
|
||||
assert not hasattr(self.av, "__setitem__")
|
||||
|
||||
def test_items(self):
|
||||
expected = dict(self.p.items())
|
||||
expected.update(self.s)
|
||||
assert sorted(self.av.items()) == sorted(expected.items())
|
||||
|
||||
def test_str(self):
|
||||
out = str(dict(self.av))
|
||||
assert str(self.av) == out
|
||||
|
||||
def test_repr(self):
|
||||
out = f"{self.av.__class__.__name__}({self.s}, {self.p})"
|
||||
assert repr(self.av) == out
|
||||
|
||||
|
||||
class TestUnionAdjacency:
|
||||
# node->nbr->data
|
||||
def setup(self):
|
||||
dd = {"color": "blue", "weight": 1.2}
|
||||
self.nd = {0: dd, 1: {}, 2: {"color": 1}}
|
||||
self.s = {3: self.nd, 0: {}, 1: {}, 2: {3: {"color": 1}}}
|
||||
self.p = {3: {}, 0: {3: dd}, 1: {0: {}}, 2: {1: {"color": 1}}}
|
||||
self.adjview = nx.classes.coreviews.UnionAdjacency(self.s, self.p)
|
||||
|
||||
def test_pickle(self):
|
||||
view = self.adjview
|
||||
pview = pickle.loads(pickle.dumps(view, -1))
|
||||
assert view == pview
|
||||
assert view.__slots__ == pview.__slots__
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.adjview) == len(self.s)
|
||||
|
||||
def test_iter(self):
|
||||
assert sorted(self.adjview) == sorted(self.s)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.adjview[1] is not self.s[1]
|
||||
assert self.adjview[3][0] is self.adjview[0][3]
|
||||
assert self.adjview[2][3]["color"] == 1
|
||||
pytest.raises(KeyError, self.adjview.__getitem__, 4)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.adjview.copy()
|
||||
assert avcopy[0] == self.adjview[0]
|
||||
assert avcopy[0] is not self.adjview[0]
|
||||
|
||||
avcopy[2][3]["ht"] = 4
|
||||
assert avcopy[2] != self.adjview[2]
|
||||
self.adjview[2][3]["ht"] = 4
|
||||
assert avcopy[2] == self.adjview[2]
|
||||
del self.adjview[2][3]["ht"]
|
||||
|
||||
assert not hasattr(self.adjview, "__setitem__")
|
||||
|
||||
def test_str(self):
|
||||
out = str(dict(self.adjview))
|
||||
assert str(self.adjview) == out
|
||||
|
||||
def test_repr(self):
|
||||
clsname = self.adjview.__class__.__name__
|
||||
out = f"{clsname}({self.s}, {self.p})"
|
||||
assert repr(self.adjview) == out
|
||||
|
||||
|
||||
class TestUnionMultiInner(TestUnionAdjacency):
|
||||
# nbr->key->data
|
||||
def setup(self):
|
||||
dd = {"color": "blue", "weight": 1.2}
|
||||
self.kd = {7: {}, "ekey": {}, 9: {"color": 1}}
|
||||
self.s = {3: self.kd, 0: {7: dd}, 1: {}, 2: {"key": {"color": 1}}}
|
||||
self.p = {3: {}, 0: {3: dd}, 1: {}, 2: {1: {"span": 2}}}
|
||||
self.adjview = nx.classes.coreviews.UnionMultiInner(self.s, self.p)
|
||||
|
||||
def test_len(self):
|
||||
assert len(self.adjview) == len(self.s) + len(self.p)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.adjview[1] is not self.s[1]
|
||||
assert self.adjview[0][7] is self.adjview[0][3]
|
||||
assert self.adjview[2]["key"]["color"] == 1
|
||||
assert self.adjview[2][1]["span"] == 2
|
||||
pytest.raises(KeyError, self.adjview.__getitem__, 4)
|
||||
pytest.raises(KeyError, self.adjview[1].__getitem__, "key")
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.adjview.copy()
|
||||
assert avcopy[0] == self.adjview[0]
|
||||
assert avcopy[0] is not self.adjview[0]
|
||||
|
||||
avcopy[2][1]["width"] = 8
|
||||
assert avcopy[2] != self.adjview[2]
|
||||
self.adjview[2][1]["width"] = 8
|
||||
assert avcopy[2] == self.adjview[2]
|
||||
del self.adjview[2][1]["width"]
|
||||
|
||||
assert not hasattr(self.adjview, "__setitem__")
|
||||
assert hasattr(avcopy, "__setitem__")
|
||||
|
||||
|
||||
class TestUnionMultiAdjacency(TestUnionAdjacency):
|
||||
# node->nbr->key->data
|
||||
def setup(self):
|
||||
dd = {"color": "blue", "weight": 1.2}
|
||||
self.kd = {7: {}, 8: {}, 9: {"color": 1}}
|
||||
self.nd = {3: self.kd, 0: {9: dd}, 1: {8: {}}, 2: {9: {"color": 1}}}
|
||||
self.s = {3: self.nd, 0: {3: {7: dd}}, 1: {}, 2: {3: {8: {}}}}
|
||||
self.p = {3: {}, 0: {3: {9: dd}}, 1: {}, 2: {1: {8: {}}}}
|
||||
self.adjview = nx.classes.coreviews.UnionMultiAdjacency(self.s, self.p)
|
||||
|
||||
def test_getitem(self):
|
||||
assert self.adjview[1] is not self.s[1]
|
||||
assert self.adjview[3][0][9] is self.adjview[0][3][9]
|
||||
assert self.adjview[3][2][9]["color"] == 1
|
||||
pytest.raises(KeyError, self.adjview.__getitem__, 4)
|
||||
|
||||
def test_copy(self):
|
||||
avcopy = self.adjview.copy()
|
||||
assert avcopy[0] == self.adjview[0]
|
||||
assert avcopy[0] is not self.adjview[0]
|
||||
|
||||
avcopy[2][3][8]["ht"] = 4
|
||||
assert avcopy[2] != self.adjview[2]
|
||||
self.adjview[2][3][8]["ht"] = 4
|
||||
assert avcopy[2] == self.adjview[2]
|
||||
del self.adjview[2][3][8]["ht"]
|
||||
|
||||
assert not hasattr(self.adjview, "__setitem__")
|
||||
assert hasattr(avcopy, "__setitem__")
|
||||
|
||||
|
||||
class TestFilteredGraphs:
|
||||
def setup(self):
|
||||
self.Graphs = [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
|
||||
self.SubGraphs = [nx.graphviews.subgraph_view] * 4
|
||||
|
||||
def test_hide_show_nodes(self):
|
||||
for Graph, SubGraph in zip(self.Graphs, self.SubGraphs):
|
||||
G = nx.path_graph(4, Graph)
|
||||
SG = G.subgraph([2, 3])
|
||||
RG = SubGraph(G, nx.filters.hide_nodes([0, 1]))
|
||||
assert SG.nodes == RG.nodes
|
||||
assert SG.edges == RG.edges
|
||||
SGC = SG.copy()
|
||||
RGC = RG.copy()
|
||||
assert SGC.nodes == RGC.nodes
|
||||
assert SGC.edges == RGC.edges
|
||||
|
||||
def test_str_repr(self):
|
||||
for Graph, SubGraph in zip(self.Graphs, self.SubGraphs):
|
||||
G = nx.path_graph(4, Graph)
|
||||
SG = G.subgraph([2, 3])
|
||||
RG = SubGraph(G, nx.filters.hide_nodes([0, 1]))
|
||||
str(SG.adj)
|
||||
str(RG.adj)
|
||||
repr(SG.adj)
|
||||
repr(RG.adj)
|
||||
str(SG.adj[2])
|
||||
str(RG.adj[2])
|
||||
repr(SG.adj[2])
|
||||
repr(RG.adj[2])
|
||||
|
||||
def test_copy(self):
|
||||
for Graph, SubGraph in zip(self.Graphs, self.SubGraphs):
|
||||
G = nx.path_graph(4, Graph)
|
||||
SG = G.subgraph([2, 3])
|
||||
RG = SubGraph(G, nx.filters.hide_nodes([0, 1]))
|
||||
assert G.adj.copy() == G.adj
|
||||
assert G.adj[2].copy() == G.adj[2]
|
||||
assert SG.adj.copy() == SG.adj
|
||||
assert SG.adj[2].copy() == SG.adj[2]
|
||||
assert RG.adj.copy() == RG.adj
|
||||
assert RG.adj[2].copy() == RG.adj[2]
|
301
venv/Lib/site-packages/networkx/classes/tests/test_digraph.py
Normal file
301
venv/Lib/site-packages/networkx/classes/tests/test_digraph.py
Normal file
|
@ -0,0 +1,301 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_nodes_equal
|
||||
from .test_graph import BaseGraphTester, BaseAttrGraphTester
|
||||
from .test_graph import TestGraph as _TestGraph
|
||||
from .test_graph import TestEdgeSubgraph as _TestGraphEdgeSubgraph
|
||||
|
||||
|
||||
class BaseDiGraphTester(BaseGraphTester):
|
||||
def test_has_successor(self):
|
||||
G = self.K3
|
||||
assert G.has_successor(0, 1)
|
||||
assert not G.has_successor(0, -1)
|
||||
|
||||
def test_successors(self):
|
||||
G = self.K3
|
||||
assert sorted(G.successors(0)) == [1, 2]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.successors(-1)
|
||||
|
||||
def test_has_predecessor(self):
|
||||
G = self.K3
|
||||
assert G.has_predecessor(0, 1)
|
||||
assert not G.has_predecessor(0, -1)
|
||||
|
||||
def test_predecessors(self):
|
||||
G = self.K3
|
||||
assert sorted(G.predecessors(0)) == [1, 2]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.predecessors(-1)
|
||||
|
||||
def test_edges(self):
|
||||
G = self.K3
|
||||
assert sorted(G.edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.edges(0)) == [(0, 1), (0, 2)]
|
||||
assert sorted(G.edges([0, 1])) == [(0, 1), (0, 2), (1, 0), (1, 2)]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.edges(-1)
|
||||
|
||||
def test_out_edges(self):
|
||||
G = self.K3
|
||||
assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.out_edges(-1)
|
||||
|
||||
def test_out_edges_dir(self):
|
||||
G = self.P3
|
||||
assert sorted(G.out_edges()) == [(0, 1), (1, 2)]
|
||||
assert sorted(G.out_edges(0)) == [(0, 1)]
|
||||
assert sorted(G.out_edges(2)) == []
|
||||
|
||||
def test_out_edges_data(self):
|
||||
G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
|
||||
assert sorted(G.out_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
|
||||
assert sorted(G.out_edges(0, data=True)) == [(0, 1, {"data": 0})]
|
||||
assert sorted(G.out_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
|
||||
assert sorted(G.out_edges(0, data="data")) == [(0, 1, 0)]
|
||||
|
||||
def test_in_edges_dir(self):
|
||||
G = self.P3
|
||||
assert sorted(G.in_edges()) == [(0, 1), (1, 2)]
|
||||
assert sorted(G.in_edges(0)) == []
|
||||
assert sorted(G.in_edges(2)) == [(1, 2)]
|
||||
|
||||
def test_in_edges_data(self):
|
||||
G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
|
||||
assert sorted(G.in_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
|
||||
assert sorted(G.in_edges(1, data=True)) == [(0, 1, {"data": 0})]
|
||||
assert sorted(G.in_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
|
||||
assert sorted(G.in_edges(1, data="data")) == [(0, 1, 0)]
|
||||
|
||||
def test_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.degree()) == [(0, 4), (1, 4), (2, 4)]
|
||||
assert dict(G.degree()) == {0: 4, 1: 4, 2: 4}
|
||||
assert G.degree(0) == 4
|
||||
assert list(G.degree(iter([0]))) == [(0, 4)] # run through iterator
|
||||
|
||||
def test_in_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.in_degree()) == [(0, 2), (1, 2), (2, 2)]
|
||||
assert dict(G.in_degree()) == {0: 2, 1: 2, 2: 2}
|
||||
assert G.in_degree(0) == 2
|
||||
assert list(G.in_degree(iter([0]))) == [(0, 2)] # run through iterator
|
||||
|
||||
def test_out_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.out_degree()) == [(0, 2), (1, 2), (2, 2)]
|
||||
assert dict(G.out_degree()) == {0: 2, 1: 2, 2: 2}
|
||||
assert G.out_degree(0) == 2
|
||||
assert list(G.out_degree(iter([0]))) == [(0, 2)]
|
||||
|
||||
def test_size(self):
|
||||
G = self.K3
|
||||
assert G.size() == 6
|
||||
assert G.number_of_edges() == 6
|
||||
|
||||
def test_to_undirected_reciprocal(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2)
|
||||
assert G.to_undirected().has_edge(1, 2)
|
||||
assert not G.to_undirected(reciprocal=True).has_edge(1, 2)
|
||||
G.add_edge(2, 1)
|
||||
assert G.to_undirected(reciprocal=True).has_edge(1, 2)
|
||||
|
||||
def test_reverse_copy(self):
|
||||
G = nx.DiGraph([(0, 1), (1, 2)])
|
||||
R = G.reverse()
|
||||
assert sorted(R.edges()) == [(1, 0), (2, 1)]
|
||||
R.remove_edge(1, 0)
|
||||
assert sorted(R.edges()) == [(2, 1)]
|
||||
assert sorted(G.edges()) == [(0, 1), (1, 2)]
|
||||
|
||||
def test_reverse_nocopy(self):
|
||||
G = nx.DiGraph([(0, 1), (1, 2)])
|
||||
R = G.reverse(copy=False)
|
||||
assert sorted(R.edges()) == [(1, 0), (2, 1)]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
R.remove_edge(1, 0)
|
||||
|
||||
def test_reverse_hashable(self):
|
||||
class Foo:
|
||||
pass
|
||||
|
||||
x = Foo()
|
||||
y = Foo()
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(x, y)
|
||||
assert_nodes_equal(G.nodes(), G.reverse().nodes())
|
||||
assert [(y, x)] == list(G.reverse().edges())
|
||||
|
||||
|
||||
class BaseAttrDiGraphTester(BaseDiGraphTester, BaseAttrGraphTester):
|
||||
def test_edges_data(self):
|
||||
G = self.K3
|
||||
all_edges = [
|
||||
(0, 1, {}),
|
||||
(0, 2, {}),
|
||||
(1, 0, {}),
|
||||
(1, 2, {}),
|
||||
(2, 0, {}),
|
||||
(2, 1, {}),
|
||||
]
|
||||
assert sorted(G.edges(data=True)) == all_edges
|
||||
assert sorted(G.edges(0, data=True)) == all_edges[:2]
|
||||
assert sorted(G.edges([0, 1], data=True)) == all_edges[:4]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.edges(-1, True)
|
||||
|
||||
def test_in_degree_weighted(self):
|
||||
G = self.K3.copy()
|
||||
G.add_edge(0, 1, weight=0.3, other=1.2)
|
||||
assert sorted(G.in_degree(weight="weight")) == [(0, 2), (1, 1.3), (2, 2)]
|
||||
assert dict(G.in_degree(weight="weight")) == {0: 2, 1: 1.3, 2: 2}
|
||||
assert G.in_degree(1, weight="weight") == 1.3
|
||||
assert sorted(G.in_degree(weight="other")) == [(0, 2), (1, 2.2), (2, 2)]
|
||||
assert dict(G.in_degree(weight="other")) == {0: 2, 1: 2.2, 2: 2}
|
||||
assert G.in_degree(1, weight="other") == 2.2
|
||||
assert list(G.in_degree(iter([1]), weight="other")) == [(1, 2.2)]
|
||||
|
||||
def test_out_degree_weighted(self):
|
||||
G = self.K3.copy()
|
||||
G.add_edge(0, 1, weight=0.3, other=1.2)
|
||||
assert sorted(G.out_degree(weight="weight")) == [(0, 1.3), (1, 2), (2, 2)]
|
||||
assert dict(G.out_degree(weight="weight")) == {0: 1.3, 1: 2, 2: 2}
|
||||
assert G.out_degree(0, weight="weight") == 1.3
|
||||
assert sorted(G.out_degree(weight="other")) == [(0, 2.2), (1, 2), (2, 2)]
|
||||
assert dict(G.out_degree(weight="other")) == {0: 2.2, 1: 2, 2: 2}
|
||||
assert G.out_degree(0, weight="other") == 2.2
|
||||
assert list(G.out_degree(iter([0]), weight="other")) == [(0, 2.2)]
|
||||
|
||||
|
||||
class TestDiGraph(BaseAttrDiGraphTester, _TestGraph):
|
||||
"""Tests specific to dict-of-dict-of-dict digraph data structure"""
|
||||
|
||||
def setup_method(self):
|
||||
self.Graph = nx.DiGraph
|
||||
# build dict-of-dict-of-dict K3
|
||||
ed1, ed2, ed3, ed4, ed5, ed6 = ({}, {}, {}, {}, {}, {})
|
||||
self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed3, 2: ed4}, 2: {0: ed5, 1: ed6}}
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = self.K3._succ = self.k3adj
|
||||
self.K3._pred = {0: {1: ed3, 2: ed5}, 1: {0: ed1, 2: ed6}, 2: {0: ed2, 1: ed4}}
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
ed1, ed2 = ({}, {})
|
||||
self.P3 = self.Graph()
|
||||
self.P3._adj = {0: {1: ed1}, 1: {2: ed2}, 2: {}}
|
||||
self.P3._succ = self.P3._adj
|
||||
self.P3._pred = {0: {}, 1: {0: ed1}, 2: {1: ed2}}
|
||||
self.P3._node = {}
|
||||
self.P3._node[0] = {}
|
||||
self.P3._node[1] = {}
|
||||
self.P3._node[2] = {}
|
||||
|
||||
def test_data_input(self):
|
||||
G = self.Graph({1: [2], 2: [1]}, name="test")
|
||||
assert G.name == "test"
|
||||
assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
|
||||
assert sorted(G.succ.items()) == [(1, {2: {}}), (2, {1: {}})]
|
||||
assert sorted(G.pred.items()) == [(1, {2: {}}), (2, {1: {}})]
|
||||
|
||||
def test_add_edge(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(0, 1)
|
||||
assert G.adj == {0: {1: {}}, 1: {}}
|
||||
assert G.succ == {0: {1: {}}, 1: {}}
|
||||
assert G.pred == {0: {}, 1: {0: {}}}
|
||||
G = self.Graph()
|
||||
G.add_edge(*(0, 1))
|
||||
assert G.adj == {0: {1: {}}, 1: {}}
|
||||
assert G.succ == {0: {1: {}}, 1: {}}
|
||||
assert G.pred == {0: {}, 1: {0: {}}}
|
||||
|
||||
def test_add_edges_from(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2, {"data": 3})], data=2)
|
||||
assert G.adj == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
|
||||
assert G.succ == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
|
||||
assert G.pred == {0: {}, 1: {0: {"data": 2}}, 2: {0: {"data": 3}}}
|
||||
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0,)]) # too few in tuple
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
|
||||
with pytest.raises(TypeError):
|
||||
G.add_edges_from([0]) # not a tuple
|
||||
|
||||
def test_remove_edge(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_edge(0, 1)
|
||||
assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
|
||||
assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_edge(-1, 0)
|
||||
|
||||
def test_remove_edges_from(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from([(0, 1)])
|
||||
assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
|
||||
assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
|
||||
G.remove_edges_from([(0, 0)]) # silent fail
|
||||
|
||||
def test_clear(self):
|
||||
G = self.K3
|
||||
G.graph["name"] = "K3"
|
||||
G.clear()
|
||||
assert list(G.nodes) == []
|
||||
assert G.succ == {}
|
||||
assert G.pred == {}
|
||||
assert G.graph == {}
|
||||
|
||||
def test_clear_edges(self):
|
||||
G = self.K3
|
||||
G.graph["name"] = "K3"
|
||||
nodes = list(G.nodes)
|
||||
G.clear_edges()
|
||||
assert list(G.nodes) == nodes
|
||||
expected = {0: {}, 1: {}, 2: {}}
|
||||
assert G.succ == expected
|
||||
assert G.pred == expected
|
||||
assert list(G.edges) == []
|
||||
assert G.graph["name"] == "K3"
|
||||
|
||||
|
||||
class TestEdgeSubgraph(_TestGraphEdgeSubgraph):
|
||||
"""Unit tests for the :meth:`DiGraph.edge_subgraph` method."""
|
||||
|
||||
def setup_method(self):
|
||||
# Create a doubly-linked path graph on five nodes.
|
||||
G = nx.DiGraph(nx.path_graph(5))
|
||||
# Add some node, edge, and graph attributes.
|
||||
for i in range(5):
|
||||
G.nodes[i]["name"] = f"node{i}"
|
||||
G.edges[0, 1]["name"] = "edge01"
|
||||
G.edges[3, 4]["name"] = "edge34"
|
||||
G.graph["name"] = "graph"
|
||||
# Get the subgraph induced by the first and last edges.
|
||||
self.G = G
|
||||
self.H = G.edge_subgraph([(0, 1), (3, 4)])
|
||||
|
||||
def test_pred_succ(self):
|
||||
"""Test that nodes are added to predecessors and successors.
|
||||
|
||||
For more information, see GitHub issue #2370.
|
||||
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(0, 1)
|
||||
H = G.edge_subgraph([(0, 1)])
|
||||
assert list(H.predecessors(0)) == []
|
||||
assert list(H.successors(0)) == [1]
|
||||
assert list(H.predecessors(1)) == [0]
|
||||
assert list(H.successors(1)) == []
|
|
@ -0,0 +1,109 @@
|
|||
"""Original NetworkX graph tests"""
|
||||
import pytest
|
||||
import networkx
|
||||
import networkx as nx
|
||||
|
||||
from .historical_tests import HistoricalTests
|
||||
|
||||
|
||||
class TestDiGraphHistorical(HistoricalTests):
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
HistoricalTests.setup_class()
|
||||
cls.G = nx.DiGraph
|
||||
|
||||
def test_in_degree(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from("GJK")
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "C"), ("C", "D")])
|
||||
|
||||
assert sorted(d for n, d in G.in_degree()) == [0, 0, 0, 0, 1, 2, 2]
|
||||
assert dict(G.in_degree()) == {
|
||||
"A": 0,
|
||||
"C": 2,
|
||||
"B": 1,
|
||||
"D": 2,
|
||||
"G": 0,
|
||||
"K": 0,
|
||||
"J": 0,
|
||||
}
|
||||
|
||||
def test_out_degree(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from("GJK")
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "C"), ("C", "D")])
|
||||
assert sorted([v for k, v in G.in_degree()]) == [0, 0, 0, 0, 1, 2, 2]
|
||||
assert dict(G.out_degree()) == {
|
||||
"A": 2,
|
||||
"C": 1,
|
||||
"B": 2,
|
||||
"D": 0,
|
||||
"G": 0,
|
||||
"K": 0,
|
||||
"J": 0,
|
||||
}
|
||||
|
||||
def test_degree_digraph(self):
|
||||
H = nx.DiGraph()
|
||||
H.add_edges_from([(1, 24), (1, 2)])
|
||||
assert sorted(d for n, d in H.in_degree([1, 24])) == [0, 1]
|
||||
assert sorted(d for n, d in H.out_degree([1, 24])) == [0, 2]
|
||||
assert sorted(d for n, d in H.degree([1, 24])) == [1, 2]
|
||||
|
||||
def test_neighbors(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from("GJK")
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "C"), ("C", "D")])
|
||||
|
||||
assert sorted(G.neighbors("C")) == ["D"]
|
||||
assert sorted(G["C"]) == ["D"]
|
||||
assert sorted(G.neighbors("A")) == ["B", "C"]
|
||||
pytest.raises(nx.NetworkXError, G.neighbors, "j")
|
||||
pytest.raises(nx.NetworkXError, G.neighbors, "j")
|
||||
|
||||
def test_successors(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from("GJK")
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "C"), ("C", "D")])
|
||||
assert sorted(G.successors("A")) == ["B", "C"]
|
||||
assert sorted(G.successors("A")) == ["B", "C"]
|
||||
assert sorted(G.successors("G")) == []
|
||||
assert sorted(G.successors("D")) == []
|
||||
assert sorted(G.successors("G")) == []
|
||||
pytest.raises(nx.NetworkXError, G.successors, "j")
|
||||
pytest.raises(nx.NetworkXError, G.successors, "j")
|
||||
|
||||
def test_predecessors(self):
|
||||
G = self.G()
|
||||
G.add_nodes_from("GJK")
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "D"), ("B", "C"), ("C", "D")])
|
||||
assert sorted(G.predecessors("C")) == ["A", "B"]
|
||||
assert sorted(G.predecessors("C")) == ["A", "B"]
|
||||
assert sorted(G.predecessors("G")) == []
|
||||
assert sorted(G.predecessors("A")) == []
|
||||
assert sorted(G.predecessors("G")) == []
|
||||
assert sorted(G.predecessors("A")) == []
|
||||
assert sorted(G.successors("D")) == []
|
||||
|
||||
pytest.raises(nx.NetworkXError, G.predecessors, "j")
|
||||
pytest.raises(nx.NetworkXError, G.predecessors, "j")
|
||||
|
||||
def test_reverse(self):
|
||||
G = nx.complete_graph(10)
|
||||
H = G.to_directed()
|
||||
HR = H.reverse()
|
||||
assert nx.is_isomorphic(H, HR)
|
||||
assert sorted(H.edges()) == sorted(HR.edges())
|
||||
|
||||
def test_reverse2(self):
|
||||
H = nx.DiGraph()
|
||||
foo = [H.add_edge(u, u + 1) for u in range(0, 5)]
|
||||
HR = H.reverse()
|
||||
for u in range(0, 5):
|
||||
assert HR.has_edge(u + 1, u)
|
||||
|
||||
def test_reverse3(self):
|
||||
H = nx.DiGraph()
|
||||
H.add_nodes_from([1, 2, 3, 4])
|
||||
HR = H.reverse()
|
||||
assert sorted(HR.nodes()) == [1, 2, 3, 4]
|
176
venv/Lib/site-packages/networkx/classes/tests/test_filters.py
Normal file
176
venv/Lib/site-packages/networkx/classes/tests/test_filters.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestFilterFactory:
|
||||
def test_no_filter(self):
|
||||
nf = nx.filters.no_filter
|
||||
assert nf()
|
||||
assert nf(1)
|
||||
assert nf(2, 1)
|
||||
|
||||
def test_hide_nodes(self):
|
||||
f = nx.classes.filters.hide_nodes([1, 2, 3])
|
||||
assert not f(1)
|
||||
assert not f(2)
|
||||
assert not f(3)
|
||||
assert f(4)
|
||||
assert f(0)
|
||||
assert f("a")
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f)
|
||||
|
||||
def test_show_nodes(self):
|
||||
f = nx.classes.filters.show_nodes([1, 2, 3])
|
||||
assert f(1)
|
||||
assert f(2)
|
||||
assert f(3)
|
||||
assert not f(4)
|
||||
assert not f(0)
|
||||
assert not f("a")
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f)
|
||||
|
||||
def test_hide_edges(self):
|
||||
factory = nx.classes.filters.hide_edges
|
||||
f = factory([(1, 2), (3, 4)])
|
||||
assert not f(1, 2)
|
||||
assert not f(3, 4)
|
||||
assert not f(4, 3)
|
||||
assert f(2, 3)
|
||||
assert f(0, -1)
|
||||
assert f("a", "b")
|
||||
pytest.raises(TypeError, f, 1, 2, 3)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3)])
|
||||
|
||||
def test_show_edges(self):
|
||||
factory = nx.classes.filters.show_edges
|
||||
f = factory([(1, 2), (3, 4)])
|
||||
assert f(1, 2)
|
||||
assert f(3, 4)
|
||||
assert f(4, 3)
|
||||
assert not f(2, 3)
|
||||
assert not f(0, -1)
|
||||
assert not f("a", "b")
|
||||
pytest.raises(TypeError, f, 1, 2, 3)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3)])
|
||||
|
||||
def test_hide_diedges(self):
|
||||
factory = nx.classes.filters.hide_diedges
|
||||
f = factory([(1, 2), (3, 4)])
|
||||
assert not f(1, 2)
|
||||
assert not f(3, 4)
|
||||
assert f(4, 3)
|
||||
assert f(2, 3)
|
||||
assert f(0, -1)
|
||||
assert f("a", "b")
|
||||
pytest.raises(TypeError, f, 1, 2, 3)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3)])
|
||||
|
||||
def test_show_diedges(self):
|
||||
factory = nx.classes.filters.show_diedges
|
||||
f = factory([(1, 2), (3, 4)])
|
||||
assert f(1, 2)
|
||||
assert f(3, 4)
|
||||
assert not f(4, 3)
|
||||
assert not f(2, 3)
|
||||
assert not f(0, -1)
|
||||
assert not f("a", "b")
|
||||
pytest.raises(TypeError, f, 1, 2, 3)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3)])
|
||||
|
||||
def test_hide_multiedges(self):
|
||||
factory = nx.classes.filters.hide_multiedges
|
||||
f = factory([(1, 2, 0), (3, 4, 1), (1, 2, 1)])
|
||||
assert not f(1, 2, 0)
|
||||
assert not f(1, 2, 1)
|
||||
assert f(1, 2, 2)
|
||||
assert f(3, 4, 0)
|
||||
assert not f(3, 4, 1)
|
||||
assert not f(4, 3, 1)
|
||||
assert f(4, 3, 0)
|
||||
assert f(2, 3, 0)
|
||||
assert f(0, -1, 0)
|
||||
assert f("a", "b", 0)
|
||||
pytest.raises(TypeError, f, 1, 2, 3, 4)
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2)])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3, 4)])
|
||||
|
||||
def test_show_multiedges(self):
|
||||
factory = nx.classes.filters.show_multiedges
|
||||
f = factory([(1, 2, 0), (3, 4, 1), (1, 2, 1)])
|
||||
assert f(1, 2, 0)
|
||||
assert f(1, 2, 1)
|
||||
assert not f(1, 2, 2)
|
||||
assert not f(3, 4, 0)
|
||||
assert f(3, 4, 1)
|
||||
assert f(4, 3, 1)
|
||||
assert not f(4, 3, 0)
|
||||
assert not f(2, 3, 0)
|
||||
assert not f(0, -1, 0)
|
||||
assert not f("a", "b", 0)
|
||||
pytest.raises(TypeError, f, 1, 2, 3, 4)
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2)])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3, 4)])
|
||||
|
||||
def test_hide_multidiedges(self):
|
||||
factory = nx.classes.filters.hide_multidiedges
|
||||
f = factory([(1, 2, 0), (3, 4, 1), (1, 2, 1)])
|
||||
assert not f(1, 2, 0)
|
||||
assert not f(1, 2, 1)
|
||||
assert f(1, 2, 2)
|
||||
assert f(3, 4, 0)
|
||||
assert not f(3, 4, 1)
|
||||
assert f(4, 3, 1)
|
||||
assert f(4, 3, 0)
|
||||
assert f(2, 3, 0)
|
||||
assert f(0, -1, 0)
|
||||
assert f("a", "b", 0)
|
||||
pytest.raises(TypeError, f, 1, 2, 3, 4)
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2)])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3, 4)])
|
||||
|
||||
def test_show_multidiedges(self):
|
||||
factory = nx.classes.filters.show_multidiedges
|
||||
f = factory([(1, 2, 0), (3, 4, 1), (1, 2, 1)])
|
||||
assert f(1, 2, 0)
|
||||
assert f(1, 2, 1)
|
||||
assert not f(1, 2, 2)
|
||||
assert not f(3, 4, 0)
|
||||
assert f(3, 4, 1)
|
||||
assert not f(4, 3, 1)
|
||||
assert not f(4, 3, 0)
|
||||
assert not f(2, 3, 0)
|
||||
assert not f(0, -1, 0)
|
||||
assert not f("a", "b", 0)
|
||||
pytest.raises(TypeError, f, 1, 2, 3, 4)
|
||||
pytest.raises(TypeError, f, 1, 2)
|
||||
pytest.raises(TypeError, f, 1)
|
||||
pytest.raises(TypeError, f)
|
||||
pytest.raises(TypeError, factory, [1, 2, 3])
|
||||
pytest.raises(ValueError, factory, [(1, 2)])
|
||||
pytest.raises(ValueError, factory, [(1, 2, 3, 4)])
|
718
venv/Lib/site-packages/networkx/classes/tests/test_function.py
Normal file
718
venv/Lib/site-packages/networkx/classes/tests/test_function.py
Normal file
|
@ -0,0 +1,718 @@
|
|||
import random
|
||||
import pytest
|
||||
import networkx as nx
|
||||
from networkx.testing.utils import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
|
||||
class TestFunction:
|
||||
def setup_method(self):
|
||||
self.G = nx.Graph({0: [1, 2, 3], 1: [1, 2, 0], 4: []}, name="Test")
|
||||
self.Gdegree = {0: 3, 1: 2, 2: 2, 3: 1, 4: 0}
|
||||
self.Gnodes = list(range(5))
|
||||
self.Gedges = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2)]
|
||||
self.DG = nx.DiGraph({0: [1, 2, 3], 1: [1, 2, 0], 4: []})
|
||||
self.DGin_degree = {0: 1, 1: 2, 2: 2, 3: 1, 4: 0}
|
||||
self.DGout_degree = {0: 3, 1: 3, 2: 0, 3: 0, 4: 0}
|
||||
self.DGnodes = list(range(5))
|
||||
self.DGedges = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2)]
|
||||
|
||||
def test_nodes(self):
|
||||
assert_nodes_equal(self.G.nodes(), list(nx.nodes(self.G)))
|
||||
assert_nodes_equal(self.DG.nodes(), list(nx.nodes(self.DG)))
|
||||
|
||||
def test_edges(self):
|
||||
assert_edges_equal(self.G.edges(), list(nx.edges(self.G)))
|
||||
assert sorted(self.DG.edges()) == sorted(nx.edges(self.DG))
|
||||
assert_edges_equal(
|
||||
self.G.edges(nbunch=[0, 1, 3]), list(nx.edges(self.G, nbunch=[0, 1, 3]))
|
||||
)
|
||||
assert sorted(self.DG.edges(nbunch=[0, 1, 3])) == sorted(
|
||||
nx.edges(self.DG, nbunch=[0, 1, 3])
|
||||
)
|
||||
|
||||
def test_degree(self):
|
||||
assert_edges_equal(self.G.degree(), list(nx.degree(self.G)))
|
||||
assert sorted(self.DG.degree()) == sorted(nx.degree(self.DG))
|
||||
assert_edges_equal(
|
||||
self.G.degree(nbunch=[0, 1]), list(nx.degree(self.G, nbunch=[0, 1]))
|
||||
)
|
||||
assert sorted(self.DG.degree(nbunch=[0, 1])) == sorted(
|
||||
nx.degree(self.DG, nbunch=[0, 1])
|
||||
)
|
||||
assert_edges_equal(
|
||||
self.G.degree(weight="weight"), list(nx.degree(self.G, weight="weight"))
|
||||
)
|
||||
assert sorted(self.DG.degree(weight="weight")) == sorted(
|
||||
nx.degree(self.DG, weight="weight")
|
||||
)
|
||||
|
||||
def test_neighbors(self):
|
||||
assert list(self.G.neighbors(1)) == list(nx.neighbors(self.G, 1))
|
||||
assert list(self.DG.neighbors(1)) == list(nx.neighbors(self.DG, 1))
|
||||
|
||||
def test_number_of_nodes(self):
|
||||
assert self.G.number_of_nodes() == nx.number_of_nodes(self.G)
|
||||
assert self.DG.number_of_nodes() == nx.number_of_nodes(self.DG)
|
||||
|
||||
def test_number_of_edges(self):
|
||||
assert self.G.number_of_edges() == nx.number_of_edges(self.G)
|
||||
assert self.DG.number_of_edges() == nx.number_of_edges(self.DG)
|
||||
|
||||
def test_is_directed(self):
|
||||
assert self.G.is_directed() == nx.is_directed(self.G)
|
||||
assert self.DG.is_directed() == nx.is_directed(self.DG)
|
||||
|
||||
def test_add_star(self):
|
||||
G = self.G.copy()
|
||||
nlist = [12, 13, 14, 15]
|
||||
nx.add_star(G, nlist)
|
||||
assert_edges_equal(G.edges(nlist), [(12, 13), (12, 14), (12, 15)])
|
||||
|
||||
G = self.G.copy()
|
||||
nx.add_star(G, nlist, weight=2.0)
|
||||
assert_edges_equal(
|
||||
G.edges(nlist, data=True),
|
||||
[
|
||||
(12, 13, {"weight": 2.0}),
|
||||
(12, 14, {"weight": 2.0}),
|
||||
(12, 15, {"weight": 2.0}),
|
||||
],
|
||||
)
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = [12]
|
||||
nx.add_star(G, nlist)
|
||||
assert_nodes_equal(G, list(self.G) + nlist)
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = []
|
||||
nx.add_star(G, nlist)
|
||||
assert_nodes_equal(G.nodes, self.Gnodes)
|
||||
assert_edges_equal(G.edges, self.G.edges)
|
||||
|
||||
def test_add_path(self):
|
||||
G = self.G.copy()
|
||||
nlist = [12, 13, 14, 15]
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges(nlist), [(12, 13), (13, 14), (14, 15)])
|
||||
G = self.G.copy()
|
||||
nx.add_path(G, nlist, weight=2.0)
|
||||
assert_edges_equal(
|
||||
G.edges(nlist, data=True),
|
||||
[
|
||||
(12, 13, {"weight": 2.0}),
|
||||
(13, 14, {"weight": 2.0}),
|
||||
(14, 15, {"weight": 2.0}),
|
||||
],
|
||||
)
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = [None]
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges(nlist), [])
|
||||
assert_nodes_equal(G, list(self.G) + [None])
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = iter([None])
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges([None]), [])
|
||||
assert_nodes_equal(G, list(self.G) + [None])
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = [12]
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges(nlist), [])
|
||||
assert_nodes_equal(G, list(self.G) + [12])
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = iter([12])
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges([12]), [])
|
||||
assert_nodes_equal(G, list(self.G) + [12])
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = []
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges, self.G.edges)
|
||||
assert_nodes_equal(G, list(self.G))
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = iter([])
|
||||
nx.add_path(G, nlist)
|
||||
assert_edges_equal(G.edges, self.G.edges)
|
||||
assert_nodes_equal(G, list(self.G))
|
||||
|
||||
def test_add_cycle(self):
|
||||
G = self.G.copy()
|
||||
nlist = [12, 13, 14, 15]
|
||||
oklists = [
|
||||
[(12, 13), (12, 15), (13, 14), (14, 15)],
|
||||
[(12, 13), (13, 14), (14, 15), (15, 12)],
|
||||
]
|
||||
nx.add_cycle(G, nlist)
|
||||
assert sorted(G.edges(nlist)) in oklists
|
||||
G = self.G.copy()
|
||||
oklists = [
|
||||
[
|
||||
(12, 13, {"weight": 1.0}),
|
||||
(12, 15, {"weight": 1.0}),
|
||||
(13, 14, {"weight": 1.0}),
|
||||
(14, 15, {"weight": 1.0}),
|
||||
],
|
||||
[
|
||||
(12, 13, {"weight": 1.0}),
|
||||
(13, 14, {"weight": 1.0}),
|
||||
(14, 15, {"weight": 1.0}),
|
||||
(15, 12, {"weight": 1.0}),
|
||||
],
|
||||
]
|
||||
nx.add_cycle(G, nlist, weight=1.0)
|
||||
assert sorted(G.edges(nlist, data=True)) in oklists
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = [12]
|
||||
nx.add_cycle(G, nlist)
|
||||
assert_nodes_equal(G, list(self.G) + nlist)
|
||||
|
||||
G = self.G.copy()
|
||||
nlist = []
|
||||
nx.add_cycle(G, nlist)
|
||||
assert_nodes_equal(G.nodes, self.Gnodes)
|
||||
assert_edges_equal(G.edges, self.G.edges)
|
||||
|
||||
def test_subgraph(self):
|
||||
assert (
|
||||
self.G.subgraph([0, 1, 2, 4]).adj == nx.subgraph(self.G, [0, 1, 2, 4]).adj
|
||||
)
|
||||
assert (
|
||||
self.DG.subgraph([0, 1, 2, 4]).adj == nx.subgraph(self.DG, [0, 1, 2, 4]).adj
|
||||
)
|
||||
assert (
|
||||
self.G.subgraph([0, 1, 2, 4]).adj
|
||||
== nx.induced_subgraph(self.G, [0, 1, 2, 4]).adj
|
||||
)
|
||||
assert (
|
||||
self.DG.subgraph([0, 1, 2, 4]).adj
|
||||
== nx.induced_subgraph(self.DG, [0, 1, 2, 4]).adj
|
||||
)
|
||||
# subgraph-subgraph chain is allowed in function interface
|
||||
H = nx.induced_subgraph(self.G.subgraph([0, 1, 2, 4]), [0, 1, 4])
|
||||
assert H._graph is not self.G
|
||||
assert H.adj == self.G.subgraph([0, 1, 4]).adj
|
||||
|
||||
def test_edge_subgraph(self):
|
||||
assert (
|
||||
self.G.edge_subgraph([(1, 2), (0, 3)]).adj
|
||||
== nx.edge_subgraph(self.G, [(1, 2), (0, 3)]).adj
|
||||
)
|
||||
assert (
|
||||
self.DG.edge_subgraph([(1, 2), (0, 3)]).adj
|
||||
== nx.edge_subgraph(self.DG, [(1, 2), (0, 3)]).adj
|
||||
)
|
||||
|
||||
def test_restricted_view(self):
|
||||
H = nx.restricted_view(self.G, [0, 2, 5], [(1, 2), (3, 4)])
|
||||
assert set(H.nodes) == {1, 3, 4}
|
||||
assert set(H.edges) == {(1, 1)}
|
||||
|
||||
def test_create_empty_copy(self):
|
||||
G = nx.create_empty_copy(self.G, with_data=False)
|
||||
assert_nodes_equal(G, list(self.G))
|
||||
assert G.graph == {}
|
||||
assert G._node == {}.fromkeys(self.G.nodes(), {})
|
||||
assert G._adj == {}.fromkeys(self.G.nodes(), {})
|
||||
G = nx.create_empty_copy(self.G)
|
||||
assert_nodes_equal(G, list(self.G))
|
||||
assert G.graph == self.G.graph
|
||||
assert G._node == self.G._node
|
||||
assert G._adj == {}.fromkeys(self.G.nodes(), {})
|
||||
|
||||
def test_degree_histogram(self):
|
||||
assert nx.degree_histogram(self.G) == [1, 1, 1, 1, 1]
|
||||
|
||||
def test_density(self):
|
||||
assert nx.density(self.G) == 0.5
|
||||
assert nx.density(self.DG) == 0.3
|
||||
G = nx.Graph()
|
||||
G.add_node(1)
|
||||
assert nx.density(G) == 0.0
|
||||
|
||||
def test_density_selfloop(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 1)
|
||||
assert nx.density(G) == 0.0
|
||||
G.add_edge(1, 2)
|
||||
assert nx.density(G) == 2.0
|
||||
|
||||
def test_freeze(self):
|
||||
G = nx.freeze(self.G)
|
||||
assert G.frozen
|
||||
pytest.raises(nx.NetworkXError, G.add_node, 1)
|
||||
pytest.raises(nx.NetworkXError, G.add_nodes_from, [1])
|
||||
pytest.raises(nx.NetworkXError, G.remove_node, 1)
|
||||
pytest.raises(nx.NetworkXError, G.remove_nodes_from, [1])
|
||||
pytest.raises(nx.NetworkXError, G.add_edge, 1, 2)
|
||||
pytest.raises(nx.NetworkXError, G.add_edges_from, [(1, 2)])
|
||||
pytest.raises(nx.NetworkXError, G.remove_edge, 1, 2)
|
||||
pytest.raises(nx.NetworkXError, G.remove_edges_from, [(1, 2)])
|
||||
pytest.raises(nx.NetworkXError, G.clear)
|
||||
|
||||
def test_is_frozen(self):
|
||||
assert not nx.is_frozen(self.G)
|
||||
G = nx.freeze(self.G)
|
||||
assert G.frozen == nx.is_frozen(self.G)
|
||||
assert G.frozen
|
||||
|
||||
def test_info(self):
|
||||
G = nx.path_graph(5)
|
||||
G.name = "path_graph(5)"
|
||||
info = nx.info(G)
|
||||
expected_graph_info = "\n".join(
|
||||
[
|
||||
"Name: path_graph(5)",
|
||||
"Type: Graph",
|
||||
"Number of nodes: 5",
|
||||
"Number of edges: 4",
|
||||
"Average degree: 1.6000",
|
||||
]
|
||||
)
|
||||
assert info == expected_graph_info
|
||||
|
||||
info = nx.info(G, n=1)
|
||||
assert type(info) == str
|
||||
expected_node_info = "\n".join(
|
||||
["Node 1 has the following properties:", "Degree: 2", "Neighbors: 0 2"]
|
||||
)
|
||||
assert info == expected_node_info
|
||||
|
||||
# must raise an error for a non-existent node
|
||||
pytest.raises(nx.NetworkXError, nx.info, G, 1248)
|
||||
|
||||
def test_info_digraph(self):
|
||||
G = nx.DiGraph(name="path_graph(5)")
|
||||
nx.add_path(G, [0, 1, 2, 3, 4])
|
||||
info = nx.info(G)
|
||||
expected_graph_info = "\n".join(
|
||||
[
|
||||
"Name: path_graph(5)",
|
||||
"Type: DiGraph",
|
||||
"Number of nodes: 5",
|
||||
"Number of edges: 4",
|
||||
"Average in degree: 0.8000",
|
||||
"Average out degree: 0.8000",
|
||||
]
|
||||
)
|
||||
assert info == expected_graph_info
|
||||
|
||||
info = nx.info(G, n=1)
|
||||
expected_node_info = "\n".join(
|
||||
["Node 1 has the following properties:", "Degree: 2", "Neighbors: 2"]
|
||||
)
|
||||
assert info == expected_node_info
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.info, G, n=-1)
|
||||
|
||||
def test_neighbors_complete_graph(self):
|
||||
graph = nx.complete_graph(100)
|
||||
pop = random.sample(list(graph), 1)
|
||||
nbors = list(nx.neighbors(graph, pop[0]))
|
||||
# should be all the other vertices in the graph
|
||||
assert len(nbors) == len(graph) - 1
|
||||
|
||||
graph = nx.path_graph(100)
|
||||
node = random.sample(list(graph), 1)[0]
|
||||
nbors = list(nx.neighbors(graph, node))
|
||||
# should be all the other vertices in the graph
|
||||
if node != 0 and node != 99:
|
||||
assert len(nbors) == 2
|
||||
else:
|
||||
assert len(nbors) == 1
|
||||
|
||||
# create a star graph with 99 outer nodes
|
||||
graph = nx.star_graph(99)
|
||||
nbors = list(nx.neighbors(graph, 0))
|
||||
assert len(nbors) == 99
|
||||
|
||||
def test_non_neighbors(self):
|
||||
graph = nx.complete_graph(100)
|
||||
pop = random.sample(list(graph), 1)
|
||||
nbors = list(nx.non_neighbors(graph, pop[0]))
|
||||
# should be all the other vertices in the graph
|
||||
assert len(nbors) == 0
|
||||
|
||||
graph = nx.path_graph(100)
|
||||
node = random.sample(list(graph), 1)[0]
|
||||
nbors = list(nx.non_neighbors(graph, node))
|
||||
# should be all the other vertices in the graph
|
||||
if node != 0 and node != 99:
|
||||
assert len(nbors) == 97
|
||||
else:
|
||||
assert len(nbors) == 98
|
||||
|
||||
# create a star graph with 99 outer nodes
|
||||
graph = nx.star_graph(99)
|
||||
nbors = list(nx.non_neighbors(graph, 0))
|
||||
assert len(nbors) == 0
|
||||
|
||||
# disconnected graph
|
||||
graph = nx.Graph()
|
||||
graph.add_nodes_from(range(10))
|
||||
nbors = list(nx.non_neighbors(graph, 0))
|
||||
assert len(nbors) == 9
|
||||
|
||||
def test_non_edges(self):
|
||||
# All possible edges exist
|
||||
graph = nx.complete_graph(5)
|
||||
nedges = list(nx.non_edges(graph))
|
||||
assert len(nedges) == 0
|
||||
|
||||
graph = nx.path_graph(4)
|
||||
expected = [(0, 2), (0, 3), (1, 3)]
|
||||
nedges = list(nx.non_edges(graph))
|
||||
for (u, v) in expected:
|
||||
assert (u, v) in nedges or (v, u) in nedges
|
||||
|
||||
graph = nx.star_graph(4)
|
||||
expected = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
|
||||
nedges = list(nx.non_edges(graph))
|
||||
for (u, v) in expected:
|
||||
assert (u, v) in nedges or (v, u) in nedges
|
||||
|
||||
# Directed graphs
|
||||
graph = nx.DiGraph()
|
||||
graph.add_edges_from([(0, 2), (2, 0), (2, 1)])
|
||||
expected = [(0, 1), (1, 0), (1, 2)]
|
||||
nedges = list(nx.non_edges(graph))
|
||||
for e in expected:
|
||||
assert e in nedges
|
||||
|
||||
def test_is_weighted(self):
|
||||
G = nx.Graph()
|
||||
assert not nx.is_weighted(G)
|
||||
|
||||
G = nx.path_graph(4)
|
||||
assert not nx.is_weighted(G)
|
||||
assert not nx.is_weighted(G, (2, 3))
|
||||
|
||||
G.add_node(4)
|
||||
G.add_edge(3, 4, weight=4)
|
||||
assert not nx.is_weighted(G)
|
||||
assert nx.is_weighted(G, (3, 4))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from(
|
||||
[
|
||||
("0", "3", 3),
|
||||
("0", "1", -5),
|
||||
("1", "0", -5),
|
||||
("0", "2", 2),
|
||||
("1", "2", 4),
|
||||
("2", "3", 1),
|
||||
]
|
||||
)
|
||||
assert nx.is_weighted(G)
|
||||
assert nx.is_weighted(G, ("1", "0"))
|
||||
|
||||
G = G.to_undirected()
|
||||
assert nx.is_weighted(G)
|
||||
assert nx.is_weighted(G, ("1", "0"))
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.is_weighted, G, (1, 2))
|
||||
|
||||
def test_is_negatively_weighted(self):
|
||||
G = nx.Graph()
|
||||
assert not nx.is_negatively_weighted(G)
|
||||
|
||||
G.add_node(1)
|
||||
G.add_nodes_from([2, 3, 4, 5])
|
||||
assert not nx.is_negatively_weighted(G)
|
||||
|
||||
G.add_edge(1, 2, weight=4)
|
||||
assert not nx.is_negatively_weighted(G, (1, 2))
|
||||
|
||||
G.add_edges_from([(1, 3), (2, 4), (2, 6)])
|
||||
G[1][3]["color"] = "blue"
|
||||
assert not nx.is_negatively_weighted(G)
|
||||
assert not nx.is_negatively_weighted(G, (1, 3))
|
||||
|
||||
G[2][4]["weight"] = -2
|
||||
assert nx.is_negatively_weighted(G, (2, 4))
|
||||
assert nx.is_negatively_weighted(G)
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_weighted_edges_from(
|
||||
[
|
||||
("0", "3", 3),
|
||||
("0", "1", -5),
|
||||
("1", "0", -2),
|
||||
("0", "2", 2),
|
||||
("1", "2", -3),
|
||||
("2", "3", 1),
|
||||
]
|
||||
)
|
||||
assert nx.is_negatively_weighted(G)
|
||||
assert not nx.is_negatively_weighted(G, ("0", "3"))
|
||||
assert nx.is_negatively_weighted(G, ("1", "0"))
|
||||
|
||||
pytest.raises(nx.NetworkXError, nx.is_negatively_weighted, G, (1, 4))
|
||||
|
||||
|
||||
class TestCommonNeighbors:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.func = staticmethod(nx.common_neighbors)
|
||||
|
||||
def test_func(G, u, v, expected):
|
||||
result = sorted(cls.func(G, u, v))
|
||||
assert result == expected
|
||||
|
||||
cls.test = staticmethod(test_func)
|
||||
|
||||
def test_K5(self):
|
||||
G = nx.complete_graph(5)
|
||||
self.test(G, 0, 1, [2, 3, 4])
|
||||
|
||||
def test_P3(self):
|
||||
G = nx.path_graph(3)
|
||||
self.test(G, 0, 2, [1])
|
||||
|
||||
def test_S4(self):
|
||||
G = nx.star_graph(4)
|
||||
self.test(G, 1, 2, [0])
|
||||
|
||||
def test_digraph(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(0, 1), (1, 2)])
|
||||
self.func(G, 0, 2)
|
||||
|
||||
def test_nonexistent_nodes(self):
|
||||
G = nx.complete_graph(5)
|
||||
pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 5, 4)
|
||||
pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 4, 5)
|
||||
pytest.raises(nx.NetworkXError, nx.common_neighbors, G, 5, 6)
|
||||
|
||||
def test_custom1(self):
|
||||
"""Case of no common neighbors."""
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([0, 1])
|
||||
self.test(G, 0, 1, [])
|
||||
|
||||
def test_custom2(self):
|
||||
"""Case of equal nodes."""
|
||||
G = nx.complete_graph(4)
|
||||
self.test(G, 0, 0, [1, 2, 3])
|
||||
|
||||
|
||||
def test_set_node_attributes():
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for G in graphs:
|
||||
# Test single value
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
vals = 100
|
||||
attr = "hello"
|
||||
nx.set_node_attributes(G, vals, attr)
|
||||
assert G.nodes[0][attr] == vals
|
||||
assert G.nodes[1][attr] == vals
|
||||
assert G.nodes[2][attr] == vals
|
||||
|
||||
# Test dictionary
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
vals = dict(zip(sorted(G.nodes()), range(len(G))))
|
||||
attr = "hi"
|
||||
nx.set_node_attributes(G, vals, attr)
|
||||
assert G.nodes[0][attr] == 0
|
||||
assert G.nodes[1][attr] == 1
|
||||
assert G.nodes[2][attr] == 2
|
||||
|
||||
# Test dictionary of dictionaries
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
d = {"hi": 0, "hello": 200}
|
||||
vals = dict.fromkeys(G.nodes(), d)
|
||||
vals.pop(0)
|
||||
nx.set_node_attributes(G, vals)
|
||||
assert G.nodes[0] == {}
|
||||
assert G.nodes[1]["hi"] == 0
|
||||
assert G.nodes[2]["hello"] == 200
|
||||
|
||||
|
||||
def test_set_edge_attributes():
|
||||
graphs = [nx.Graph(), nx.DiGraph()]
|
||||
for G in graphs:
|
||||
# Test single value
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hello"
|
||||
vals = 3
|
||||
nx.set_edge_attributes(G, vals, attr)
|
||||
assert G[0][1][attr] == vals
|
||||
assert G[1][2][attr] == vals
|
||||
|
||||
# Test multiple values
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hi"
|
||||
edges = [(0, 1), (1, 2)]
|
||||
vals = dict(zip(edges, range(len(edges))))
|
||||
nx.set_edge_attributes(G, vals, attr)
|
||||
assert G[0][1][attr] == 0
|
||||
assert G[1][2][attr] == 1
|
||||
|
||||
# Test dictionary of dictionaries
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
d = {"hi": 0, "hello": 200}
|
||||
edges = [(0, 1)]
|
||||
vals = dict.fromkeys(edges, d)
|
||||
nx.set_edge_attributes(G, vals)
|
||||
assert G[0][1]["hi"] == 0
|
||||
assert G[0][1]["hello"] == 200
|
||||
assert G[1][2] == {}
|
||||
|
||||
|
||||
def test_set_edge_attributes_multi():
|
||||
graphs = [nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for G in graphs:
|
||||
# Test single value
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hello"
|
||||
vals = 3
|
||||
nx.set_edge_attributes(G, vals, attr)
|
||||
assert G[0][1][0][attr] == vals
|
||||
assert G[1][2][0][attr] == vals
|
||||
|
||||
# Test multiple values
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hi"
|
||||
edges = [(0, 1, 0), (1, 2, 0)]
|
||||
vals = dict(zip(edges, range(len(edges))))
|
||||
nx.set_edge_attributes(G, vals, attr)
|
||||
assert G[0][1][0][attr] == 0
|
||||
assert G[1][2][0][attr] == 1
|
||||
|
||||
# Test dictionary of dictionaries
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
d = {"hi": 0, "hello": 200}
|
||||
edges = [(0, 1, 0)]
|
||||
vals = dict.fromkeys(edges, d)
|
||||
nx.set_edge_attributes(G, vals)
|
||||
assert G[0][1][0]["hi"] == 0
|
||||
assert G[0][1][0]["hello"] == 200
|
||||
assert G[1][2][0] == {}
|
||||
|
||||
|
||||
def test_get_node_attributes():
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for G in graphs:
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hello"
|
||||
vals = 100
|
||||
nx.set_node_attributes(G, vals, attr)
|
||||
attrs = nx.get_node_attributes(G, attr)
|
||||
assert attrs[0] == vals
|
||||
assert attrs[1] == vals
|
||||
assert attrs[2] == vals
|
||||
|
||||
|
||||
def test_get_edge_attributes():
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for G in graphs:
|
||||
G = nx.path_graph(3, create_using=G)
|
||||
attr = "hello"
|
||||
vals = 100
|
||||
nx.set_edge_attributes(G, vals, attr)
|
||||
attrs = nx.get_edge_attributes(G, attr)
|
||||
|
||||
assert len(attrs) == 2
|
||||
if G.is_multigraph():
|
||||
keys = [(0, 1, 0), (1, 2, 0)]
|
||||
for u, v, k in keys:
|
||||
try:
|
||||
assert attrs[(u, v, k)] == 100
|
||||
except KeyError:
|
||||
assert attrs[(v, u, k)] == 100
|
||||
else:
|
||||
keys = [(0, 1), (1, 2)]
|
||||
for u, v in keys:
|
||||
try:
|
||||
assert attrs[(u, v)] == 100
|
||||
except KeyError:
|
||||
assert attrs[(v, u)] == 100
|
||||
|
||||
|
||||
def test_is_empty():
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for G in graphs:
|
||||
assert nx.is_empty(G)
|
||||
G.add_nodes_from(range(5))
|
||||
assert nx.is_empty(G)
|
||||
G.add_edges_from([(1, 2), (3, 4)])
|
||||
assert not nx.is_empty(G)
|
||||
|
||||
|
||||
def test_selfloops():
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
for graph in graphs:
|
||||
G = nx.complete_graph(3, create_using=graph)
|
||||
G.add_edge(0, 0)
|
||||
assert_nodes_equal(nx.nodes_with_selfloops(G), [0])
|
||||
assert_edges_equal(nx.selfloop_edges(G), [(0, 0)])
|
||||
assert_edges_equal(nx.selfloop_edges(G, data=True), [(0, 0, {})])
|
||||
assert nx.number_of_selfloops(G) == 1
|
||||
# test selfloop attr
|
||||
G.add_edge(1, 1, weight=2)
|
||||
assert_edges_equal(
|
||||
nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
|
||||
)
|
||||
assert_edges_equal(
|
||||
nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)]
|
||||
)
|
||||
# test removing selfloops behavior vis-a-vis altering a dict while iterating
|
||||
G.add_edge(0, 0)
|
||||
G.remove_edges_from(nx.selfloop_edges(G))
|
||||
if G.is_multigraph():
|
||||
G.add_edge(0, 0)
|
||||
pytest.raises(
|
||||
RuntimeError, G.remove_edges_from, nx.selfloop_edges(G, keys=True)
|
||||
)
|
||||
G.add_edge(0, 0)
|
||||
pytest.raises(
|
||||
TypeError, G.remove_edges_from, nx.selfloop_edges(G, data=True)
|
||||
)
|
||||
G.add_edge(0, 0)
|
||||
pytest.raises(
|
||||
RuntimeError,
|
||||
G.remove_edges_from,
|
||||
nx.selfloop_edges(G, data=True, keys=True),
|
||||
)
|
||||
else:
|
||||
G.add_edge(0, 0)
|
||||
G.remove_edges_from(nx.selfloop_edges(G, keys=True))
|
||||
G.add_edge(0, 0)
|
||||
G.remove_edges_from(nx.selfloop_edges(G, data=True))
|
||||
G.add_edge(0, 0)
|
||||
G.remove_edges_from(nx.selfloop_edges(G, keys=True, data=True))
|
||||
|
||||
|
||||
def test_pathweight():
|
||||
valid_path = [1, 2, 3]
|
||||
invalid_path = [1, 3, 2]
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
edges = [
|
||||
(1, 2, dict(cost=5, dist=6)),
|
||||
(2, 3, dict(cost=3, dist=4)),
|
||||
(1, 2, dict(cost=1, dist=2)),
|
||||
]
|
||||
for graph in graphs:
|
||||
graph.add_edges_from(edges)
|
||||
assert nx.path_weight(graph, valid_path, "cost") == 4
|
||||
assert nx.path_weight(graph, valid_path, "dist") == 6
|
||||
pytest.raises(nx.NetworkXNoPath, nx.path_weight, graph, invalid_path, "cost")
|
||||
|
||||
|
||||
def test_ispath():
|
||||
valid_path = [1, 2, 3, 4]
|
||||
invalid_path = [1, 2, 4, 3]
|
||||
graphs = [nx.Graph(), nx.DiGraph(), nx.MultiGraph(), nx.MultiDiGraph()]
|
||||
edges = [(1, 2), (2, 3), (1, 2), (3, 4)]
|
||||
for graph in graphs:
|
||||
graph.add_edges_from(edges)
|
||||
assert nx.is_path(graph, valid_path)
|
||||
assert not nx.is_path(graph, invalid_path)
|
803
venv/Lib/site-packages/networkx/classes/tests/test_graph.py
Normal file
803
venv/Lib/site-packages/networkx/classes/tests/test_graph.py
Normal file
|
@ -0,0 +1,803 @@
|
|||
import pickle
|
||||
import gc
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing.utils import (
|
||||
assert_graphs_equal,
|
||||
assert_edges_equal,
|
||||
assert_nodes_equal,
|
||||
)
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class BaseGraphTester:
|
||||
""" Tests for data-structure independent graph class features."""
|
||||
|
||||
def test_contains(self):
|
||||
G = self.K3
|
||||
assert 1 in G
|
||||
assert 4 not in G
|
||||
assert "b" not in G
|
||||
assert [] not in G # no exception for nonhashable
|
||||
assert {1: 1} not in G # no exception for nonhashable
|
||||
|
||||
def test_order(self):
|
||||
G = self.K3
|
||||
assert len(G) == 3
|
||||
assert G.order() == 3
|
||||
assert G.number_of_nodes() == 3
|
||||
|
||||
def test_nodes(self):
|
||||
G = self.K3
|
||||
assert sorted(G.nodes()) == self.k3nodes
|
||||
assert sorted(G.nodes(data=True)) == [(0, {}), (1, {}), (2, {})]
|
||||
|
||||
def test_has_node(self):
|
||||
G = self.K3
|
||||
assert G.has_node(1)
|
||||
assert not G.has_node(4)
|
||||
assert not G.has_node([]) # no exception for nonhashable
|
||||
assert not G.has_node({1: 1}) # no exception for nonhashable
|
||||
|
||||
def test_has_edge(self):
|
||||
G = self.K3
|
||||
assert G.has_edge(0, 1)
|
||||
assert not G.has_edge(0, -1)
|
||||
|
||||
def test_neighbors(self):
|
||||
G = self.K3
|
||||
assert sorted(G.neighbors(0)) == [1, 2]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.neighbors(-1)
|
||||
|
||||
def test_memory_leak(self):
|
||||
G = self.Graph()
|
||||
|
||||
def count_objects_of_type(_type):
|
||||
return sum(1 for obj in gc.get_objects() if isinstance(obj, _type))
|
||||
|
||||
gc.collect()
|
||||
before = count_objects_of_type(self.Graph)
|
||||
G.copy()
|
||||
gc.collect()
|
||||
after = count_objects_of_type(self.Graph)
|
||||
assert before == after
|
||||
|
||||
# test a subgraph of the base class
|
||||
class MyGraph(self.Graph):
|
||||
pass
|
||||
|
||||
gc.collect()
|
||||
G = MyGraph()
|
||||
before = count_objects_of_type(MyGraph)
|
||||
G.copy()
|
||||
gc.collect()
|
||||
after = count_objects_of_type(MyGraph)
|
||||
assert before == after
|
||||
|
||||
def test_edges(self):
|
||||
G = self.K3
|
||||
assert_edges_equal(G.edges(), [(0, 1), (0, 2), (1, 2)])
|
||||
assert_edges_equal(G.edges(0), [(0, 1), (0, 2)])
|
||||
assert_edges_equal(G.edges([0, 1]), [(0, 1), (0, 2), (1, 2)])
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.edges(-1)
|
||||
|
||||
def test_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.degree()) == [(0, 2), (1, 2), (2, 2)]
|
||||
assert dict(G.degree()) == {0: 2, 1: 2, 2: 2}
|
||||
assert G.degree(0) == 2
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.degree(-1) # node not in graph
|
||||
|
||||
def test_size(self):
|
||||
G = self.K3
|
||||
assert G.size() == 3
|
||||
assert G.number_of_edges() == 3
|
||||
|
||||
def test_nbunch_iter(self):
|
||||
G = self.K3
|
||||
assert_nodes_equal(G.nbunch_iter(), self.k3nodes) # all nodes
|
||||
assert_nodes_equal(G.nbunch_iter(0), [0]) # single node
|
||||
assert_nodes_equal(G.nbunch_iter([0, 1]), [0, 1]) # sequence
|
||||
# sequence with none in graph
|
||||
assert_nodes_equal(G.nbunch_iter([-1]), [])
|
||||
# string sequence with none in graph
|
||||
assert_nodes_equal(G.nbunch_iter("foo"), [])
|
||||
# node not in graph doesn't get caught upon creation of iterator
|
||||
bunch = G.nbunch_iter(-1)
|
||||
# but gets caught when iterator used
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
list(bunch)
|
||||
# unhashable doesn't get caught upon creation of iterator
|
||||
bunch = G.nbunch_iter([0, 1, 2, {}])
|
||||
# but gets caught when iterator hits the unhashable
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
list(bunch)
|
||||
|
||||
def test_nbunch_iter_node_format_raise(self):
|
||||
# Tests that a node that would have failed string formatting
|
||||
# doesn't cause an error when attempting to raise a
|
||||
# :exc:`nx.NetworkXError`.
|
||||
|
||||
# For more information, see pull request #1813.
|
||||
G = self.Graph()
|
||||
nbunch = [("x", set())]
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
list(G.nbunch_iter(nbunch))
|
||||
|
||||
def test_selfloop_degree(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 1)
|
||||
assert sorted(G.degree()) == [(1, 2)]
|
||||
assert dict(G.degree()) == {1: 2}
|
||||
assert G.degree(1) == 2
|
||||
assert sorted(G.degree([1])) == [(1, 2)]
|
||||
assert G.degree(1, weight="weight") == 2
|
||||
|
||||
def test_selfloops(self):
|
||||
G = self.K3.copy()
|
||||
G.add_edge(0, 0)
|
||||
assert_nodes_equal(nx.nodes_with_selfloops(G), [0])
|
||||
assert_edges_equal(nx.selfloop_edges(G), [(0, 0)])
|
||||
assert nx.number_of_selfloops(G) == 1
|
||||
G.remove_edge(0, 0)
|
||||
G.add_edge(0, 0)
|
||||
G.remove_edges_from([(0, 0)])
|
||||
G.add_edge(1, 1)
|
||||
G.remove_node(1)
|
||||
G.add_edge(0, 0)
|
||||
G.add_edge(1, 1)
|
||||
G.remove_nodes_from([0, 1])
|
||||
|
||||
|
||||
class BaseAttrGraphTester(BaseGraphTester):
|
||||
""" Tests of graph class attribute features."""
|
||||
|
||||
def test_weighted_degree(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, weight=2, other=3)
|
||||
G.add_edge(2, 3, weight=3, other=4)
|
||||
assert sorted(d for n, d in G.degree(weight="weight")) == [2, 3, 5]
|
||||
assert dict(G.degree(weight="weight")) == {1: 2, 2: 5, 3: 3}
|
||||
assert G.degree(1, weight="weight") == 2
|
||||
assert_nodes_equal((G.degree([1], weight="weight")), [(1, 2)])
|
||||
|
||||
assert_nodes_equal((d for n, d in G.degree(weight="other")), [3, 7, 4])
|
||||
assert dict(G.degree(weight="other")) == {1: 3, 2: 7, 3: 4}
|
||||
assert G.degree(1, weight="other") == 3
|
||||
assert_edges_equal((G.degree([1], weight="other")), [(1, 3)])
|
||||
|
||||
def add_attributes(self, G):
|
||||
G.graph["foo"] = []
|
||||
G.nodes[0]["foo"] = []
|
||||
G.remove_edge(1, 2)
|
||||
ll = []
|
||||
G.add_edge(1, 2, foo=ll)
|
||||
G.add_edge(2, 1, foo=ll)
|
||||
|
||||
def test_name(self):
|
||||
G = self.Graph(name="")
|
||||
assert G.name == ""
|
||||
G = self.Graph(name="test")
|
||||
assert G.__str__() == "test"
|
||||
assert G.name == "test"
|
||||
|
||||
def test_graph_chain(self):
|
||||
G = self.Graph([(0, 1), (1, 2)])
|
||||
DG = G.to_directed(as_view=True)
|
||||
SDG = DG.subgraph([0, 1])
|
||||
RSDG = SDG.reverse(copy=False)
|
||||
assert G is DG._graph
|
||||
assert DG is SDG._graph
|
||||
assert SDG is RSDG._graph
|
||||
|
||||
def test_copy(self):
|
||||
G = self.Graph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
self.add_attributes(G)
|
||||
# copy edge datadict but any container attr are same
|
||||
H = G.copy()
|
||||
self.graphs_equal(H, G)
|
||||
self.different_attrdict(H, G)
|
||||
self.shallow_copy_attrdict(H, G)
|
||||
|
||||
def test_class_copy(self):
|
||||
G = self.Graph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
self.add_attributes(G)
|
||||
# copy edge datadict but any container attr are same
|
||||
H = G.__class__(G)
|
||||
self.graphs_equal(H, G)
|
||||
self.different_attrdict(H, G)
|
||||
self.shallow_copy_attrdict(H, G)
|
||||
|
||||
def test_fresh_copy(self):
|
||||
G = self.Graph()
|
||||
G.add_node(0)
|
||||
G.add_edge(1, 2)
|
||||
self.add_attributes(G)
|
||||
# copy graph structure but use fresh datadict
|
||||
H = G.__class__()
|
||||
H.add_nodes_from(G)
|
||||
H.add_edges_from(G.edges())
|
||||
assert len(G.nodes[0]) == 1
|
||||
ddict = G.adj[1][2][0] if G.is_multigraph() else G.adj[1][2]
|
||||
assert len(ddict) == 1
|
||||
assert len(H.nodes[0]) == 0
|
||||
ddict = H.adj[1][2][0] if H.is_multigraph() else H.adj[1][2]
|
||||
assert len(ddict) == 0
|
||||
|
||||
def is_deepcopy(self, H, G):
|
||||
self.graphs_equal(H, G)
|
||||
self.different_attrdict(H, G)
|
||||
self.deep_copy_attrdict(H, G)
|
||||
|
||||
def deep_copy_attrdict(self, H, G):
|
||||
self.deepcopy_graph_attr(H, G)
|
||||
self.deepcopy_node_attr(H, G)
|
||||
self.deepcopy_edge_attr(H, G)
|
||||
|
||||
def deepcopy_graph_attr(self, H, G):
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
G.graph["foo"].append(1)
|
||||
assert G.graph["foo"] != H.graph["foo"]
|
||||
|
||||
def deepcopy_node_attr(self, H, G):
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
G.nodes[0]["foo"].append(1)
|
||||
assert G.nodes[0]["foo"] != H.nodes[0]["foo"]
|
||||
|
||||
def deepcopy_edge_attr(self, H, G):
|
||||
assert G[1][2]["foo"] == H[1][2]["foo"]
|
||||
G[1][2]["foo"].append(1)
|
||||
assert G[1][2]["foo"] != H[1][2]["foo"]
|
||||
|
||||
def is_shallow_copy(self, H, G):
|
||||
self.graphs_equal(H, G)
|
||||
self.shallow_copy_attrdict(H, G)
|
||||
|
||||
def shallow_copy_attrdict(self, H, G):
|
||||
self.shallow_copy_graph_attr(H, G)
|
||||
self.shallow_copy_node_attr(H, G)
|
||||
self.shallow_copy_edge_attr(H, G)
|
||||
|
||||
def shallow_copy_graph_attr(self, H, G):
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
G.graph["foo"].append(1)
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
|
||||
def shallow_copy_node_attr(self, H, G):
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
G.nodes[0]["foo"].append(1)
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
|
||||
def shallow_copy_edge_attr(self, H, G):
|
||||
assert G[1][2]["foo"] == H[1][2]["foo"]
|
||||
G[1][2]["foo"].append(1)
|
||||
assert G[1][2]["foo"] == H[1][2]["foo"]
|
||||
|
||||
def same_attrdict(self, H, G):
|
||||
old_foo = H[1][2]["foo"]
|
||||
H.adj[1][2]["foo"] = "baz"
|
||||
assert G.edges == H.edges
|
||||
H.adj[1][2]["foo"] = old_foo
|
||||
assert G.edges == H.edges
|
||||
|
||||
old_foo = H.nodes[0]["foo"]
|
||||
H.nodes[0]["foo"] = "baz"
|
||||
assert G.nodes == H.nodes
|
||||
H.nodes[0]["foo"] = old_foo
|
||||
assert G.nodes == H.nodes
|
||||
|
||||
def different_attrdict(self, H, G):
|
||||
old_foo = H[1][2]["foo"]
|
||||
H.adj[1][2]["foo"] = "baz"
|
||||
assert G._adj != H._adj
|
||||
H.adj[1][2]["foo"] = old_foo
|
||||
assert G._adj == H._adj
|
||||
|
||||
old_foo = H.nodes[0]["foo"]
|
||||
H.nodes[0]["foo"] = "baz"
|
||||
assert G._node != H._node
|
||||
H.nodes[0]["foo"] = old_foo
|
||||
assert G._node == H._node
|
||||
|
||||
def graphs_equal(self, H, G):
|
||||
assert G._adj == H._adj
|
||||
assert G._node == H._node
|
||||
assert G.graph == H.graph
|
||||
assert G.name == H.name
|
||||
if not G.is_directed() and not H.is_directed():
|
||||
assert H._adj[1][2] is H._adj[2][1]
|
||||
assert G._adj[1][2] is G._adj[2][1]
|
||||
else: # at least one is directed
|
||||
if not G.is_directed():
|
||||
G._pred = G._adj
|
||||
G._succ = G._adj
|
||||
if not H.is_directed():
|
||||
H._pred = H._adj
|
||||
H._succ = H._adj
|
||||
assert G._pred == H._pred
|
||||
assert G._succ == H._succ
|
||||
assert H._succ[1][2] is H._pred[2][1]
|
||||
assert G._succ[1][2] is G._pred[2][1]
|
||||
|
||||
def test_graph_attr(self):
|
||||
G = self.K3.copy()
|
||||
G.graph["foo"] = "bar"
|
||||
assert G.graph["foo"] == "bar"
|
||||
del G.graph["foo"]
|
||||
assert G.graph == {}
|
||||
H = self.Graph(foo="bar")
|
||||
assert H.graph["foo"] == "bar"
|
||||
|
||||
def test_node_attr(self):
|
||||
G = self.K3.copy()
|
||||
G.add_node(1, foo="bar")
|
||||
assert_nodes_equal(G.nodes(), [0, 1, 2])
|
||||
assert_nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "bar"}), (2, {})])
|
||||
G.nodes[1]["foo"] = "baz"
|
||||
assert_nodes_equal(G.nodes(data=True), [(0, {}), (1, {"foo": "baz"}), (2, {})])
|
||||
assert_nodes_equal(G.nodes(data="foo"), [(0, None), (1, "baz"), (2, None)])
|
||||
assert_nodes_equal(
|
||||
G.nodes(data="foo", default="bar"), [(0, "bar"), (1, "baz"), (2, "bar")]
|
||||
)
|
||||
|
||||
def test_node_attr2(self):
|
||||
G = self.K3.copy()
|
||||
a = {"foo": "bar"}
|
||||
G.add_node(3, **a)
|
||||
assert_nodes_equal(G.nodes(), [0, 1, 2, 3])
|
||||
assert_nodes_equal(
|
||||
G.nodes(data=True), [(0, {}), (1, {}), (2, {}), (3, {"foo": "bar"})]
|
||||
)
|
||||
|
||||
def test_edge_lookup(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, foo="bar")
|
||||
assert_edges_equal(G.edges[1, 2], {"foo": "bar"})
|
||||
|
||||
def test_edge_attr(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, foo="bar")
|
||||
assert_edges_equal(G.edges(data=True), [(1, 2, {"foo": "bar"})])
|
||||
assert_edges_equal(G.edges(data="foo"), [(1, 2, "bar")])
|
||||
|
||||
def test_edge_attr2(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(1, 2), (3, 4)], foo="foo")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"foo": "foo"}), (3, 4, {"foo": "foo"})]
|
||||
)
|
||||
assert_edges_equal(G.edges(data="foo"), [(1, 2, "foo"), (3, 4, "foo")])
|
||||
|
||||
def test_edge_attr3(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(1, 2, {"weight": 32}), (3, 4, {"weight": 64})], foo="foo")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True),
|
||||
[
|
||||
(1, 2, {"foo": "foo", "weight": 32}),
|
||||
(3, 4, {"foo": "foo", "weight": 64}),
|
||||
],
|
||||
)
|
||||
|
||||
G.remove_edges_from([(1, 2), (3, 4)])
|
||||
G.add_edge(1, 2, data=7, spam="bar", bar="foo")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
|
||||
def test_edge_attr4(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, data=7, spam="bar", bar="foo")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G[1][2]["data"] = 10 # OK to set data like this
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 10, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
|
||||
G.adj[1][2]["data"] = 20
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G.edges[1, 2]["data"] = 21 # another spelling, "edge"
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G.adj[1][2]["listdata"] = [20, 200]
|
||||
G.adj[1][2]["weight"] = 20
|
||||
dd = {
|
||||
"data": 21,
|
||||
"spam": "bar",
|
||||
"bar": "foo",
|
||||
"listdata": [20, 200],
|
||||
"weight": 20,
|
||||
}
|
||||
assert_edges_equal(G.edges(data=True), [(1, 2, dd)])
|
||||
|
||||
def test_to_undirected(self):
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = nx.Graph(G)
|
||||
self.is_shallow_copy(H, G)
|
||||
self.different_attrdict(H, G)
|
||||
H = G.to_undirected()
|
||||
self.is_deepcopy(H, G)
|
||||
|
||||
def test_to_directed(self):
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = nx.DiGraph(G)
|
||||
self.is_shallow_copy(H, G)
|
||||
self.different_attrdict(H, G)
|
||||
H = G.to_directed()
|
||||
self.is_deepcopy(H, G)
|
||||
|
||||
def test_subgraph(self):
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = G.subgraph([0, 1, 2, 5])
|
||||
self.graphs_equal(H, G)
|
||||
self.same_attrdict(H, G)
|
||||
self.shallow_copy_attrdict(H, G)
|
||||
|
||||
H = G.subgraph(0)
|
||||
assert H.adj == {0: {}}
|
||||
H = G.subgraph([])
|
||||
assert H.adj == {}
|
||||
assert G.adj != {}
|
||||
|
||||
def test_selfloops_attr(self):
|
||||
G = self.K3.copy()
|
||||
G.add_edge(0, 0)
|
||||
G.add_edge(1, 1, weight=2)
|
||||
assert_edges_equal(
|
||||
nx.selfloop_edges(G, data=True), [(0, 0, {}), (1, 1, {"weight": 2})]
|
||||
)
|
||||
assert_edges_equal(
|
||||
nx.selfloop_edges(G, data="weight"), [(0, 0, None), (1, 1, 2)]
|
||||
)
|
||||
|
||||
|
||||
class TestGraph(BaseAttrGraphTester):
|
||||
"""Tests specific to dict-of-dict-of-dict graph data structure"""
|
||||
|
||||
def setup_method(self):
|
||||
self.Graph = nx.Graph
|
||||
# build dict-of-dict-of-dict K3
|
||||
ed1, ed2, ed3 = ({}, {}, {})
|
||||
self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = self.k3adj
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
def test_pickle(self):
|
||||
G = self.K3
|
||||
pg = pickle.loads(pickle.dumps(G, -1))
|
||||
self.graphs_equal(pg, G)
|
||||
pg = pickle.loads(pickle.dumps(G))
|
||||
self.graphs_equal(pg, G)
|
||||
|
||||
def test_data_input(self):
|
||||
G = self.Graph({1: [2], 2: [1]}, name="test")
|
||||
assert G.name == "test"
|
||||
assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
|
||||
G = self.Graph({1: [2], 2: [1]}, name="test")
|
||||
assert G.name == "test"
|
||||
assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
|
||||
|
||||
def test_adjacency(self):
|
||||
G = self.K3
|
||||
assert dict(G.adjacency()) == {
|
||||
0: {1: {}, 2: {}},
|
||||
1: {0: {}, 2: {}},
|
||||
2: {0: {}, 1: {}},
|
||||
}
|
||||
|
||||
def test_getitem(self):
|
||||
G = self.K3
|
||||
assert G[0] == {1: {}, 2: {}}
|
||||
with pytest.raises(KeyError):
|
||||
G.__getitem__("j")
|
||||
with pytest.raises(TypeError):
|
||||
G.__getitem__(["A"])
|
||||
|
||||
def test_add_node(self):
|
||||
G = self.Graph()
|
||||
G.add_node(0)
|
||||
assert G.adj == {0: {}}
|
||||
# test add attributes
|
||||
G.add_node(1, c="red")
|
||||
G.add_node(2, c="blue")
|
||||
G.add_node(3, c="red")
|
||||
assert G.nodes[1]["c"] == "red"
|
||||
assert G.nodes[2]["c"] == "blue"
|
||||
assert G.nodes[3]["c"] == "red"
|
||||
# test updating attributes
|
||||
G.add_node(1, c="blue")
|
||||
G.add_node(2, c="red")
|
||||
G.add_node(3, c="blue")
|
||||
assert G.nodes[1]["c"] == "blue"
|
||||
assert G.nodes[2]["c"] == "red"
|
||||
assert G.nodes[3]["c"] == "blue"
|
||||
|
||||
def test_add_nodes_from(self):
|
||||
G = self.Graph()
|
||||
G.add_nodes_from([0, 1, 2])
|
||||
assert G.adj == {0: {}, 1: {}, 2: {}}
|
||||
# test add attributes
|
||||
G.add_nodes_from([0, 1, 2], c="red")
|
||||
assert G.nodes[0]["c"] == "red"
|
||||
assert G.nodes[2]["c"] == "red"
|
||||
# test that attribute dicts are not the same
|
||||
assert G.nodes[0] is not G.nodes[1]
|
||||
# test updating attributes
|
||||
G.add_nodes_from([0, 1, 2], c="blue")
|
||||
assert G.nodes[0]["c"] == "blue"
|
||||
assert G.nodes[2]["c"] == "blue"
|
||||
assert G.nodes[0] is not G.nodes[1]
|
||||
# test tuple input
|
||||
H = self.Graph()
|
||||
H.add_nodes_from(G.nodes(data=True))
|
||||
assert H.nodes[0]["c"] == "blue"
|
||||
assert H.nodes[2]["c"] == "blue"
|
||||
assert H.nodes[0] is not H.nodes[1]
|
||||
# specific overrides general
|
||||
H.add_nodes_from([0, (1, {"c": "green"}), (3, {"c": "cyan"})], c="red")
|
||||
assert H.nodes[0]["c"] == "red"
|
||||
assert H.nodes[1]["c"] == "green"
|
||||
assert H.nodes[2]["c"] == "blue"
|
||||
assert H.nodes[3]["c"] == "cyan"
|
||||
|
||||
def test_remove_node(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_node(0)
|
||||
assert G.adj == {1: {2: {}}, 2: {1: {}}}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_node(-1)
|
||||
|
||||
# generator here to implement list,set,string...
|
||||
|
||||
def test_remove_nodes_from(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_nodes_from([0, 1])
|
||||
assert G.adj == {2: {}}
|
||||
G.remove_nodes_from([-1]) # silent fail
|
||||
|
||||
def test_add_edge(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(0, 1)
|
||||
assert G.adj == {0: {1: {}}, 1: {0: {}}}
|
||||
G = self.Graph()
|
||||
G.add_edge(*(0, 1))
|
||||
assert G.adj == {0: {1: {}}, 1: {0: {}}}
|
||||
|
||||
def test_add_edges_from(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2, {"weight": 3})])
|
||||
assert G.adj == {
|
||||
0: {1: {}, 2: {"weight": 3}},
|
||||
1: {0: {}},
|
||||
2: {0: {"weight": 3}},
|
||||
}
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 2, {"weight": 3}), (1, 2, {"data": 4})], data=2)
|
||||
assert G.adj == {
|
||||
0: {1: {"data": 2}, 2: {"weight": 3, "data": 2}},
|
||||
1: {0: {"data": 2}, 2: {"data": 4}},
|
||||
2: {0: {"weight": 3, "data": 2}, 1: {"data": 4}},
|
||||
}
|
||||
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0,)]) # too few in tuple
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
|
||||
with pytest.raises(TypeError):
|
||||
G.add_edges_from([0]) # not a tuple
|
||||
|
||||
def test_remove_edge(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_edge(0, 1)
|
||||
assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_edge(-1, 0)
|
||||
|
||||
def test_remove_edges_from(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from([(0, 1)])
|
||||
assert G.adj == {0: {2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
|
||||
G.remove_edges_from([(0, 0)]) # silent fail
|
||||
|
||||
def test_clear(self):
|
||||
G = self.K3.copy()
|
||||
G.graph["name"] = "K3"
|
||||
G.clear()
|
||||
assert list(G.nodes) == []
|
||||
assert G.adj == {}
|
||||
assert G.graph == {}
|
||||
|
||||
def test_clear_edges(self):
|
||||
G = self.K3.copy()
|
||||
G.graph["name"] = "K3"
|
||||
nodes = list(G.nodes)
|
||||
G.clear_edges()
|
||||
assert list(G.nodes) == nodes
|
||||
assert G.adj == {0: {}, 1: {}, 2: {}}
|
||||
assert list(G.edges) == []
|
||||
assert G.graph["name"] == "K3"
|
||||
|
||||
def test_edges_data(self):
|
||||
G = self.K3
|
||||
all_edges = [(0, 1, {}), (0, 2, {}), (1, 2, {})]
|
||||
assert_edges_equal(G.edges(data=True), all_edges)
|
||||
assert_edges_equal(G.edges(0, data=True), [(0, 1, {}), (0, 2, {})])
|
||||
assert_edges_equal(G.edges([0, 1], data=True), all_edges)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.edges(-1, True)
|
||||
|
||||
def test_get_edge_data(self):
|
||||
G = self.K3.copy()
|
||||
assert G.get_edge_data(0, 1) == {}
|
||||
assert G[0][1] == {}
|
||||
assert G.get_edge_data(10, 20) is None
|
||||
assert G.get_edge_data(-1, 0) is None
|
||||
assert G.get_edge_data(-1, 0, default=1) == 1
|
||||
|
||||
def test_update(self):
|
||||
# specify both edgees and nodes
|
||||
G = self.K3.copy()
|
||||
G.update(nodes=[3, (4, {"size": 2})], edges=[(4, 5), (6, 7, {"weight": 2})])
|
||||
nlist = [
|
||||
(0, {}),
|
||||
(1, {}),
|
||||
(2, {}),
|
||||
(3, {}),
|
||||
(4, {"size": 2}),
|
||||
(5, {}),
|
||||
(6, {}),
|
||||
(7, {}),
|
||||
]
|
||||
assert sorted(G.nodes.data()) == nlist
|
||||
if G.is_directed():
|
||||
elist = [
|
||||
(0, 1, {}),
|
||||
(0, 2, {}),
|
||||
(1, 0, {}),
|
||||
(1, 2, {}),
|
||||
(2, 0, {}),
|
||||
(2, 1, {}),
|
||||
(4, 5, {}),
|
||||
(6, 7, {"weight": 2}),
|
||||
]
|
||||
else:
|
||||
elist = [
|
||||
(0, 1, {}),
|
||||
(0, 2, {}),
|
||||
(1, 2, {}),
|
||||
(4, 5, {}),
|
||||
(6, 7, {"weight": 2}),
|
||||
]
|
||||
assert sorted(G.edges.data()) == elist
|
||||
assert G.graph == {}
|
||||
|
||||
# no keywords -- order is edges, nodes
|
||||
G = self.K3.copy()
|
||||
G.update([(4, 5), (6, 7, {"weight": 2})], [3, (4, {"size": 2})])
|
||||
assert sorted(G.nodes.data()) == nlist
|
||||
assert sorted(G.edges.data()) == elist
|
||||
assert G.graph == {}
|
||||
|
||||
# update using only a graph
|
||||
G = self.Graph()
|
||||
G.graph["foo"] = "bar"
|
||||
G.add_node(2, data=4)
|
||||
G.add_edge(0, 1, weight=0.5)
|
||||
GG = G.copy()
|
||||
H = self.Graph()
|
||||
GG.update(H)
|
||||
assert_graphs_equal(G, GG)
|
||||
H.update(G)
|
||||
assert_graphs_equal(H, G)
|
||||
|
||||
# update nodes only
|
||||
H = self.Graph()
|
||||
H.update(nodes=[3, 4])
|
||||
assert H.nodes ^ {3, 4} == set()
|
||||
assert H.size() == 0
|
||||
|
||||
# update edges only
|
||||
H = self.Graph()
|
||||
H.update(edges=[(3, 4)])
|
||||
assert sorted(H.edges.data()) == [(3, 4, {})]
|
||||
assert H.size() == 1
|
||||
|
||||
# No inputs -> exception
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
nx.Graph().update()
|
||||
|
||||
|
||||
class TestEdgeSubgraph:
|
||||
"""Unit tests for the :meth:`Graph.edge_subgraph` method."""
|
||||
|
||||
def setup_method(self):
|
||||
# Create a path graph on five nodes.
|
||||
G = nx.path_graph(5)
|
||||
# Add some node, edge, and graph attributes.
|
||||
for i in range(5):
|
||||
G.nodes[i]["name"] = f"node{i}"
|
||||
G.edges[0, 1]["name"] = "edge01"
|
||||
G.edges[3, 4]["name"] = "edge34"
|
||||
G.graph["name"] = "graph"
|
||||
# Get the subgraph induced by the first and last edges.
|
||||
self.G = G
|
||||
self.H = G.edge_subgraph([(0, 1), (3, 4)])
|
||||
|
||||
def test_correct_nodes(self):
|
||||
"""Tests that the subgraph has the correct nodes."""
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_correct_edges(self):
|
||||
"""Tests that the subgraph has the correct edges."""
|
||||
assert [(0, 1, "edge01"), (3, 4, "edge34")] == sorted(self.H.edges(data="name"))
|
||||
|
||||
def test_add_node(self):
|
||||
"""Tests that adding a node to the original graph does not
|
||||
affect the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.add_node(5)
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_remove_node(self):
|
||||
"""Tests that removing a node in the original graph does
|
||||
affect the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.remove_node(0)
|
||||
assert [1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_node_attr_dict(self):
|
||||
"""Tests that the node attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for v in self.H:
|
||||
assert self.G.nodes[v] == self.H.nodes[v]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G.nodes[0]["name"] = "foo"
|
||||
assert self.G.nodes[0] == self.H.nodes[0]
|
||||
self.H.nodes[1]["name"] = "bar"
|
||||
assert self.G.nodes[1] == self.H.nodes[1]
|
||||
|
||||
def test_edge_attr_dict(self):
|
||||
"""Tests that the edge attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for u, v in self.H.edges():
|
||||
assert self.G.edges[u, v] == self.H.edges[u, v]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G.edges[0, 1]["name"] = "foo"
|
||||
assert self.G.edges[0, 1]["name"] == self.H.edges[0, 1]["name"]
|
||||
self.H.edges[3, 4]["name"] = "bar"
|
||||
assert self.G.edges[3, 4]["name"] == self.H.edges[3, 4]["name"]
|
||||
|
||||
def test_graph_attr_dict(self):
|
||||
"""Tests that the graph attribute dictionary of the two graphs
|
||||
is the same object.
|
||||
|
||||
"""
|
||||
assert self.G.graph is self.H.graph
|
|
@ -0,0 +1,12 @@
|
|||
"""Original NetworkX graph tests"""
|
||||
import networkx
|
||||
import networkx as nx
|
||||
|
||||
from .historical_tests import HistoricalTests
|
||||
|
||||
|
||||
class TestGraphHistorical(HistoricalTests):
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
HistoricalTests.setup_class()
|
||||
cls.G = nx.Graph
|
341
venv/Lib/site-packages/networkx/classes/tests/test_graphviews.py
Normal file
341
venv/Lib/site-packages/networkx/classes/tests/test_graphviews.py
Normal file
|
@ -0,0 +1,341 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
# Note: SubGraph views are not tested here. They have their own testing file
|
||||
|
||||
|
||||
class TestReverseView:
|
||||
def setup(self):
|
||||
self.G = nx.path_graph(9, create_using=nx.DiGraph())
|
||||
self.rv = nx.reverse_view(self.G)
|
||||
|
||||
def test_pickle(self):
|
||||
import pickle
|
||||
|
||||
rv = self.rv
|
||||
prv = pickle.loads(pickle.dumps(rv, -1))
|
||||
assert rv._node == prv._node
|
||||
assert rv._adj == prv._adj
|
||||
assert rv.graph == prv.graph
|
||||
|
||||
def test_contains(self):
|
||||
assert (2, 3) in self.G.edges
|
||||
assert (3, 2) not in self.G.edges
|
||||
assert (2, 3) not in self.rv.edges
|
||||
assert (3, 2) in self.rv.edges
|
||||
|
||||
def test_iter(self):
|
||||
expected = sorted(tuple(reversed(e)) for e in self.G.edges)
|
||||
assert sorted(self.rv.edges) == expected
|
||||
|
||||
def test_exceptions(self):
|
||||
nxg = nx.graphviews
|
||||
pytest.raises(nx.NetworkXNotImplemented, nxg.reverse_view, nx.Graph())
|
||||
|
||||
def test_subclass(self):
|
||||
class MyGraph(nx.DiGraph):
|
||||
def my_method(self):
|
||||
return "me"
|
||||
|
||||
def to_directed_class(self):
|
||||
return MyGraph()
|
||||
|
||||
M = MyGraph()
|
||||
M.add_edge(1, 2)
|
||||
RM = nx.reverse_view(M)
|
||||
print("RM class", RM.__class__)
|
||||
RMC = RM.copy()
|
||||
print("RMC class", RMC.__class__)
|
||||
print(RMC.edges)
|
||||
assert RMC.has_edge(2, 1)
|
||||
assert RMC.my_method() == "me"
|
||||
|
||||
|
||||
class TestMultiReverseView:
|
||||
def setup(self):
|
||||
self.G = nx.path_graph(9, create_using=nx.MultiDiGraph())
|
||||
self.G.add_edge(4, 5)
|
||||
self.rv = nx.reverse_view(self.G)
|
||||
|
||||
def test_pickle(self):
|
||||
import pickle
|
||||
|
||||
rv = self.rv
|
||||
prv = pickle.loads(pickle.dumps(rv, -1))
|
||||
assert rv._node == prv._node
|
||||
assert rv._adj == prv._adj
|
||||
assert rv.graph == prv.graph
|
||||
|
||||
def test_contains(self):
|
||||
assert (2, 3, 0) in self.G.edges
|
||||
assert (3, 2, 0) not in self.G.edges
|
||||
assert (2, 3, 0) not in self.rv.edges
|
||||
assert (3, 2, 0) in self.rv.edges
|
||||
assert (5, 4, 1) in self.rv.edges
|
||||
assert (4, 5, 1) not in self.rv.edges
|
||||
|
||||
def test_iter(self):
|
||||
expected = sorted((v, u, k) for u, v, k in self.G.edges)
|
||||
assert sorted(self.rv.edges) == expected
|
||||
|
||||
def test_exceptions(self):
|
||||
nxg = nx.graphviews
|
||||
MG = nx.MultiGraph(self.G)
|
||||
pytest.raises(nx.NetworkXNotImplemented, nxg.reverse_view, MG)
|
||||
|
||||
|
||||
class TestToDirected:
|
||||
def setup(self):
|
||||
self.G = nx.path_graph(9)
|
||||
self.dv = nx.to_directed(self.G)
|
||||
self.MG = nx.path_graph(9, create_using=nx.MultiGraph())
|
||||
self.Mdv = nx.to_directed(self.MG)
|
||||
|
||||
def test_directed(self):
|
||||
assert not self.G.is_directed()
|
||||
assert self.dv.is_directed()
|
||||
|
||||
def test_already_directed(self):
|
||||
dd = nx.to_directed(self.dv)
|
||||
Mdd = nx.to_directed(self.Mdv)
|
||||
assert_edges_equal(dd.edges, self.dv.edges)
|
||||
assert_edges_equal(Mdd.edges, self.Mdv.edges)
|
||||
|
||||
def test_pickle(self):
|
||||
import pickle
|
||||
|
||||
dv = self.dv
|
||||
pdv = pickle.loads(pickle.dumps(dv, -1))
|
||||
assert dv._node == pdv._node
|
||||
assert dv._succ == pdv._succ
|
||||
assert dv._pred == pdv._pred
|
||||
assert dv.graph == pdv.graph
|
||||
|
||||
def test_contains(self):
|
||||
assert (2, 3) in self.G.edges
|
||||
assert (3, 2) in self.G.edges
|
||||
assert (2, 3) in self.dv.edges
|
||||
assert (3, 2) in self.dv.edges
|
||||
|
||||
def test_iter(self):
|
||||
revd = [tuple(reversed(e)) for e in self.G.edges]
|
||||
expected = sorted(list(self.G.edges) + revd)
|
||||
assert sorted(self.dv.edges) == expected
|
||||
|
||||
|
||||
class TestToUndirected:
|
||||
def setup(self):
|
||||
self.DG = nx.path_graph(9, create_using=nx.DiGraph())
|
||||
self.uv = nx.to_undirected(self.DG)
|
||||
self.MDG = nx.path_graph(9, create_using=nx.MultiDiGraph())
|
||||
self.Muv = nx.to_undirected(self.MDG)
|
||||
|
||||
def test_directed(self):
|
||||
assert self.DG.is_directed()
|
||||
assert not self.uv.is_directed()
|
||||
|
||||
def test_already_directed(self):
|
||||
uu = nx.to_undirected(self.uv)
|
||||
Muu = nx.to_undirected(self.Muv)
|
||||
assert_edges_equal(uu.edges, self.uv.edges)
|
||||
assert_edges_equal(Muu.edges, self.Muv.edges)
|
||||
|
||||
def test_pickle(self):
|
||||
import pickle
|
||||
|
||||
uv = self.uv
|
||||
puv = pickle.loads(pickle.dumps(uv, -1))
|
||||
assert uv._node == puv._node
|
||||
assert uv._adj == puv._adj
|
||||
assert uv.graph == puv.graph
|
||||
assert hasattr(uv, "_graph")
|
||||
|
||||
def test_contains(self):
|
||||
assert (2, 3) in self.DG.edges
|
||||
assert (3, 2) not in self.DG.edges
|
||||
assert (2, 3) in self.uv.edges
|
||||
assert (3, 2) in self.uv.edges
|
||||
|
||||
def test_iter(self):
|
||||
expected = sorted(self.DG.edges)
|
||||
assert sorted(self.uv.edges) == expected
|
||||
|
||||
|
||||
class TestChainsOfViews:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.path_graph(9)
|
||||
cls.DG = nx.path_graph(9, create_using=nx.DiGraph())
|
||||
cls.MG = nx.path_graph(9, create_using=nx.MultiGraph())
|
||||
cls.MDG = nx.path_graph(9, create_using=nx.MultiDiGraph())
|
||||
cls.Gv = nx.to_undirected(cls.DG)
|
||||
cls.DGv = nx.to_directed(cls.G)
|
||||
cls.MGv = nx.to_undirected(cls.MDG)
|
||||
cls.MDGv = nx.to_directed(cls.MG)
|
||||
cls.Rv = cls.DG.reverse()
|
||||
cls.MRv = cls.MDG.reverse()
|
||||
cls.graphs = [
|
||||
cls.G,
|
||||
cls.DG,
|
||||
cls.MG,
|
||||
cls.MDG,
|
||||
cls.Gv,
|
||||
cls.DGv,
|
||||
cls.MGv,
|
||||
cls.MDGv,
|
||||
cls.Rv,
|
||||
cls.MRv,
|
||||
]
|
||||
for G in cls.graphs:
|
||||
G.edges, G.nodes, G.degree
|
||||
|
||||
def test_pickle(self):
|
||||
import pickle
|
||||
|
||||
for G in self.graphs:
|
||||
H = pickle.loads(pickle.dumps(G, -1))
|
||||
assert_edges_equal(H.edges, G.edges)
|
||||
assert_nodes_equal(H.nodes, G.nodes)
|
||||
|
||||
def test_subgraph_of_subgraph(self):
|
||||
SGv = nx.subgraph(self.G, range(3, 7))
|
||||
SDGv = nx.subgraph(self.DG, range(3, 7))
|
||||
SMGv = nx.subgraph(self.MG, range(3, 7))
|
||||
SMDGv = nx.subgraph(self.MDG, range(3, 7))
|
||||
for G in self.graphs + [SGv, SDGv, SMGv, SMDGv]:
|
||||
SG = nx.induced_subgraph(G, [4, 5, 6])
|
||||
assert list(SG) == [4, 5, 6]
|
||||
SSG = SG.subgraph([6, 7])
|
||||
assert list(SSG) == [6]
|
||||
# subgraph-subgraph chain is short-cut in base class method
|
||||
assert SSG._graph is G
|
||||
|
||||
def test_restricted_induced_subgraph_chains(self):
|
||||
""" Test subgraph chains that both restrict and show nodes/edges.
|
||||
|
||||
A restricted_view subgraph should allow induced subgraphs using
|
||||
G.subgraph that automagically without a chain (meaning the result
|
||||
is a subgraph view of the original graph not a subgraph-of-subgraph.
|
||||
"""
|
||||
hide_nodes = [3, 4, 5]
|
||||
hide_edges = [(6, 7)]
|
||||
RG = nx.restricted_view(self.G, hide_nodes, hide_edges)
|
||||
nodes = [4, 5, 6, 7, 8]
|
||||
SG = nx.induced_subgraph(RG, nodes)
|
||||
SSG = RG.subgraph(nodes)
|
||||
assert RG._graph is self.G
|
||||
assert SSG._graph is self.G
|
||||
assert SG._graph is RG
|
||||
assert_edges_equal(SG.edges, SSG.edges)
|
||||
# should be same as morphing the graph
|
||||
CG = self.G.copy()
|
||||
CG.remove_nodes_from(hide_nodes)
|
||||
CG.remove_edges_from(hide_edges)
|
||||
assert_edges_equal(CG.edges(nodes), SSG.edges)
|
||||
CG.remove_nodes_from([0, 1, 2, 3])
|
||||
assert_edges_equal(CG.edges, SSG.edges)
|
||||
# switch order: subgraph first, then restricted view
|
||||
SSSG = self.G.subgraph(nodes)
|
||||
RSG = nx.restricted_view(SSSG, hide_nodes, hide_edges)
|
||||
assert RSG._graph is not self.G
|
||||
assert_edges_equal(RSG.edges, CG.edges)
|
||||
|
||||
def test_subgraph_copy(self):
|
||||
for origG in self.graphs:
|
||||
G = nx.OrderedGraph(origG)
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
H = SG.copy()
|
||||
assert type(G) == type(H)
|
||||
|
||||
def test_subgraph_todirected(self):
|
||||
SG = nx.induced_subgraph(self.G, [4, 5, 6])
|
||||
SSG = SG.to_directed()
|
||||
assert sorted(SSG) == [4, 5, 6]
|
||||
assert sorted(SSG.edges) == [(4, 5), (5, 4), (5, 6), (6, 5)]
|
||||
|
||||
def test_subgraph_toundirected(self):
|
||||
SG = nx.induced_subgraph(self.G, [4, 5, 6])
|
||||
SSG = SG.to_undirected()
|
||||
assert list(SSG) == [4, 5, 6]
|
||||
assert sorted(SSG.edges) == [(4, 5), (5, 6)]
|
||||
|
||||
def test_reverse_subgraph_toundirected(self):
|
||||
G = self.DG.reverse(copy=False)
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
SSG = SG.to_undirected()
|
||||
assert list(SSG) == [4, 5, 6]
|
||||
assert sorted(SSG.edges) == [(4, 5), (5, 6)]
|
||||
|
||||
def test_reverse_reverse_copy(self):
|
||||
G = self.DG.reverse(copy=False)
|
||||
H = G.reverse(copy=True)
|
||||
assert H.nodes == self.DG.nodes
|
||||
assert H.edges == self.DG.edges
|
||||
G = self.MDG.reverse(copy=False)
|
||||
H = G.reverse(copy=True)
|
||||
assert H.nodes == self.MDG.nodes
|
||||
assert H.edges == self.MDG.edges
|
||||
|
||||
def test_subgraph_edgesubgraph_toundirected(self):
|
||||
G = self.G.copy()
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
SSG = SG.edge_subgraph([(4, 5), (5, 4)])
|
||||
USSG = SSG.to_undirected()
|
||||
assert list(USSG) == [4, 5]
|
||||
assert sorted(USSG.edges) == [(4, 5)]
|
||||
|
||||
def test_copy_subgraph(self):
|
||||
G = self.G.copy()
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
CSG = SG.copy(as_view=True)
|
||||
DCSG = SG.copy(as_view=False)
|
||||
assert hasattr(CSG, "_graph") # is a view
|
||||
assert not hasattr(DCSG, "_graph") # not a view
|
||||
|
||||
def test_copy_disubgraph(self):
|
||||
G = self.DG.copy()
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
CSG = SG.copy(as_view=True)
|
||||
DCSG = SG.copy(as_view=False)
|
||||
assert hasattr(CSG, "_graph") # is a view
|
||||
assert not hasattr(DCSG, "_graph") # not a view
|
||||
|
||||
def test_copy_multidisubgraph(self):
|
||||
G = self.MDG.copy()
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
CSG = SG.copy(as_view=True)
|
||||
DCSG = SG.copy(as_view=False)
|
||||
assert hasattr(CSG, "_graph") # is a view
|
||||
assert not hasattr(DCSG, "_graph") # not a view
|
||||
|
||||
def test_copy_multisubgraph(self):
|
||||
G = self.MG.copy()
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
CSG = SG.copy(as_view=True)
|
||||
DCSG = SG.copy(as_view=False)
|
||||
assert hasattr(CSG, "_graph") # is a view
|
||||
assert not hasattr(DCSG, "_graph") # not a view
|
||||
|
||||
def test_copy_of_view(self):
|
||||
G = nx.OrderedMultiGraph(self.MGv)
|
||||
assert G.__class__.__name__ == "OrderedMultiGraph"
|
||||
G = G.copy(as_view=True)
|
||||
assert G.__class__.__name__ == "OrderedMultiGraph"
|
||||
|
||||
def test_subclass(self):
|
||||
class MyGraph(nx.DiGraph):
|
||||
def my_method(self):
|
||||
return "me"
|
||||
|
||||
def to_directed_class(self):
|
||||
return MyGraph()
|
||||
|
||||
for origG in self.graphs:
|
||||
G = MyGraph(origG)
|
||||
SG = G.subgraph([4, 5, 6])
|
||||
H = SG.copy()
|
||||
assert SG.my_method() == "me"
|
||||
assert H.my_method() == "me"
|
||||
assert not 3 in H or 3 in SG
|
|
@ -0,0 +1,399 @@
|
|||
import pytest
|
||||
from networkx.testing import assert_edges_equal
|
||||
import networkx as nx
|
||||
from .test_multigraph import BaseMultiGraphTester
|
||||
from .test_multigraph import TestMultiGraph as _TestMultiGraph
|
||||
from .test_multigraph import TestEdgeSubgraph as _TestMultiGraphEdgeSubgraph
|
||||
|
||||
|
||||
class BaseMultiDiGraphTester(BaseMultiGraphTester):
|
||||
def test_edges(self):
|
||||
G = self.K3
|
||||
edges = [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.edges()) == edges
|
||||
assert sorted(G.edges(0)) == [(0, 1), (0, 2)]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.edges, -1)
|
||||
|
||||
def test_edges_data(self):
|
||||
G = self.K3
|
||||
edges = [(0, 1, {}), (0, 2, {}), (1, 0, {}), (1, 2, {}), (2, 0, {}), (2, 1, {})]
|
||||
assert sorted(G.edges(data=True)) == edges
|
||||
assert sorted(G.edges(0, data=True)) == [(0, 1, {}), (0, 2, {})]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.neighbors, -1)
|
||||
|
||||
def test_edges_multi(self):
|
||||
G = self.K3
|
||||
assert sorted(G.edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.edges(0)) == [(0, 1), (0, 2)]
|
||||
G.add_edge(0, 1)
|
||||
assert sorted(G.edges()) == [
|
||||
(0, 1),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
]
|
||||
|
||||
def test_out_edges(self):
|
||||
G = self.K3
|
||||
assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.out_edges, -1)
|
||||
assert sorted(G.out_edges(0, keys=True)) == [(0, 1, 0), (0, 2, 0)]
|
||||
|
||||
def test_out_edges_multi(self):
|
||||
G = self.K3
|
||||
assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)]
|
||||
G.add_edge(0, 1, 2)
|
||||
assert sorted(G.out_edges()) == [
|
||||
(0, 1),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
]
|
||||
|
||||
def test_out_edges_data(self):
|
||||
G = self.K3
|
||||
assert sorted(G.edges(0, data=True)) == [(0, 1, {}), (0, 2, {})]
|
||||
G.remove_edge(0, 1)
|
||||
G.add_edge(0, 1, data=1)
|
||||
assert sorted(G.edges(0, data=True)) == [(0, 1, {"data": 1}), (0, 2, {})]
|
||||
assert sorted(G.edges(0, data="data")) == [(0, 1, 1), (0, 2, None)]
|
||||
assert sorted(G.edges(0, data="data", default=-1)) == [(0, 1, 1), (0, 2, -1)]
|
||||
|
||||
def test_in_edges(self):
|
||||
G = self.K3
|
||||
assert sorted(G.in_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.in_edges(0)) == [(1, 0), (2, 0)]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.in_edges, -1)
|
||||
G.add_edge(0, 1, 2)
|
||||
assert sorted(G.in_edges()) == [
|
||||
(0, 1),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
]
|
||||
assert sorted(G.in_edges(0, keys=True)) == [(1, 0, 0), (2, 0, 0)]
|
||||
|
||||
def test_in_edges_no_keys(self):
|
||||
G = self.K3
|
||||
assert sorted(G.in_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
assert sorted(G.in_edges(0)) == [(1, 0), (2, 0)]
|
||||
G.add_edge(0, 1, 2)
|
||||
assert sorted(G.in_edges()) == [
|
||||
(0, 1),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(2, 1),
|
||||
]
|
||||
|
||||
assert sorted(G.in_edges(data=True, keys=False)) == [
|
||||
(0, 1, {}),
|
||||
(0, 1, {}),
|
||||
(0, 2, {}),
|
||||
(1, 0, {}),
|
||||
(1, 2, {}),
|
||||
(2, 0, {}),
|
||||
(2, 1, {}),
|
||||
]
|
||||
|
||||
def test_in_edges_data(self):
|
||||
G = self.K3
|
||||
assert sorted(G.in_edges(0, data=True)) == [(1, 0, {}), (2, 0, {})]
|
||||
G.remove_edge(1, 0)
|
||||
G.add_edge(1, 0, data=1)
|
||||
assert sorted(G.in_edges(0, data=True)) == [(1, 0, {"data": 1}), (2, 0, {})]
|
||||
assert sorted(G.in_edges(0, data="data")) == [(1, 0, 1), (2, 0, None)]
|
||||
assert sorted(G.in_edges(0, data="data", default=-1)) == [(1, 0, 1), (2, 0, -1)]
|
||||
|
||||
def is_shallow(self, H, G):
|
||||
# graph
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
G.graph["foo"].append(1)
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
# node
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
G.nodes[0]["foo"].append(1)
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
# edge
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
G[1][2][0]["foo"].append(1)
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
|
||||
def is_deep(self, H, G):
|
||||
# graph
|
||||
assert G.graph["foo"] == H.graph["foo"]
|
||||
G.graph["foo"].append(1)
|
||||
assert G.graph["foo"] != H.graph["foo"]
|
||||
# node
|
||||
assert G.nodes[0]["foo"] == H.nodes[0]["foo"]
|
||||
G.nodes[0]["foo"].append(1)
|
||||
assert G.nodes[0]["foo"] != H.nodes[0]["foo"]
|
||||
# edge
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
G[1][2][0]["foo"].append(1)
|
||||
assert G[1][2][0]["foo"] != H[1][2][0]["foo"]
|
||||
|
||||
def test_to_undirected(self):
|
||||
# MultiDiGraph -> MultiGraph changes number of edges so it is
|
||||
# not a copy operation... use is_shallow, not is_shallow_copy
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = nx.MultiGraph(G)
|
||||
# self.is_shallow(H,G)
|
||||
# the result is traversal order dependent so we
|
||||
# can't use the is_shallow() test here.
|
||||
try:
|
||||
assert_edges_equal(H.edges(), [(0, 1), (1, 2), (2, 0)])
|
||||
except AssertionError:
|
||||
assert_edges_equal(H.edges(), [(0, 1), (1, 2), (1, 2), (2, 0)])
|
||||
H = G.to_undirected()
|
||||
self.is_deep(H, G)
|
||||
|
||||
def test_has_successor(self):
|
||||
G = self.K3
|
||||
assert G.has_successor(0, 1)
|
||||
assert not G.has_successor(0, -1)
|
||||
|
||||
def test_successors(self):
|
||||
G = self.K3
|
||||
assert sorted(G.successors(0)) == [1, 2]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.successors, -1)
|
||||
|
||||
def test_has_predecessor(self):
|
||||
G = self.K3
|
||||
assert G.has_predecessor(0, 1)
|
||||
assert not G.has_predecessor(0, -1)
|
||||
|
||||
def test_predecessors(self):
|
||||
G = self.K3
|
||||
assert sorted(G.predecessors(0)) == [1, 2]
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.predecessors, -1)
|
||||
|
||||
def test_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.degree()) == [(0, 4), (1, 4), (2, 4)]
|
||||
assert dict(G.degree()) == {0: 4, 1: 4, 2: 4}
|
||||
assert G.degree(0) == 4
|
||||
assert list(G.degree(iter([0]))) == [(0, 4)]
|
||||
G.add_edge(0, 1, weight=0.3, other=1.2)
|
||||
assert sorted(G.degree(weight="weight")) == [(0, 4.3), (1, 4.3), (2, 4)]
|
||||
assert sorted(G.degree(weight="other")) == [(0, 5.2), (1, 5.2), (2, 4)]
|
||||
|
||||
def test_in_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.in_degree()) == [(0, 2), (1, 2), (2, 2)]
|
||||
assert dict(G.in_degree()) == {0: 2, 1: 2, 2: 2}
|
||||
assert G.in_degree(0) == 2
|
||||
assert list(G.in_degree(iter([0]))) == [(0, 2)]
|
||||
assert G.in_degree(0, weight="weight") == 2
|
||||
|
||||
def test_out_degree(self):
|
||||
G = self.K3
|
||||
assert sorted(G.out_degree()) == [(0, 2), (1, 2), (2, 2)]
|
||||
assert dict(G.out_degree()) == {0: 2, 1: 2, 2: 2}
|
||||
assert G.out_degree(0) == 2
|
||||
assert list(G.out_degree(iter([0]))) == [(0, 2)]
|
||||
assert G.out_degree(0, weight="weight") == 2
|
||||
|
||||
def test_size(self):
|
||||
G = self.K3
|
||||
assert G.size() == 6
|
||||
assert G.number_of_edges() == 6
|
||||
G.add_edge(0, 1, weight=0.3, other=1.2)
|
||||
assert round(G.size(weight="weight"), 2) == 6.3
|
||||
assert round(G.size(weight="other"), 2) == 7.2
|
||||
|
||||
def test_to_undirected_reciprocal(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2)
|
||||
assert G.to_undirected().has_edge(1, 2)
|
||||
assert not G.to_undirected(reciprocal=True).has_edge(1, 2)
|
||||
G.add_edge(2, 1)
|
||||
assert G.to_undirected(reciprocal=True).has_edge(1, 2)
|
||||
|
||||
def test_reverse_copy(self):
|
||||
G = nx.MultiDiGraph([(0, 1), (0, 1)])
|
||||
R = G.reverse()
|
||||
assert sorted(R.edges()) == [(1, 0), (1, 0)]
|
||||
R.remove_edge(1, 0)
|
||||
assert sorted(R.edges()) == [(1, 0)]
|
||||
assert sorted(G.edges()) == [(0, 1), (0, 1)]
|
||||
|
||||
def test_reverse_nocopy(self):
|
||||
G = nx.MultiDiGraph([(0, 1), (0, 1)])
|
||||
R = G.reverse(copy=False)
|
||||
assert sorted(R.edges()) == [(1, 0), (1, 0)]
|
||||
pytest.raises(nx.NetworkXError, R.remove_edge, 1, 0)
|
||||
|
||||
|
||||
class TestMultiDiGraph(BaseMultiDiGraphTester, _TestMultiGraph):
|
||||
def setup_method(self):
|
||||
self.Graph = nx.MultiDiGraph
|
||||
# build K3
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = {0: {}, 1: {}, 2: {}}
|
||||
self.K3._succ = self.K3._adj
|
||||
self.K3._pred = {0: {}, 1: {}, 2: {}}
|
||||
for u in self.k3nodes:
|
||||
for v in self.k3nodes:
|
||||
if u == v:
|
||||
continue
|
||||
d = {0: {}}
|
||||
self.K3._succ[u][v] = d
|
||||
self.K3._pred[v][u] = d
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
def test_add_edge(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(0, 1)
|
||||
assert G._adj == {0: {1: {0: {}}}, 1: {}}
|
||||
assert G._succ == {0: {1: {0: {}}}, 1: {}}
|
||||
assert G._pred == {0: {}, 1: {0: {0: {}}}}
|
||||
G = self.Graph()
|
||||
G.add_edge(*(0, 1))
|
||||
assert G._adj == {0: {1: {0: {}}}, 1: {}}
|
||||
assert G._succ == {0: {1: {0: {}}}, 1: {}}
|
||||
assert G._pred == {0: {}, 1: {0: {0: {}}}}
|
||||
|
||||
def test_add_edges_from(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 1, {"weight": 3})])
|
||||
assert G._adj == {0: {1: {0: {}, 1: {"weight": 3}}}, 1: {}}
|
||||
assert G._succ == {0: {1: {0: {}, 1: {"weight": 3}}}, 1: {}}
|
||||
assert G._pred == {0: {}, 1: {0: {0: {}, 1: {"weight": 3}}}}
|
||||
|
||||
G.add_edges_from([(0, 1), (0, 1, {"weight": 3})], weight=2)
|
||||
assert G._succ == {
|
||||
0: {1: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}},
|
||||
1: {},
|
||||
}
|
||||
assert G._pred == {
|
||||
0: {},
|
||||
1: {0: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}},
|
||||
}
|
||||
|
||||
G = self.Graph()
|
||||
edges = [
|
||||
(0, 1, {"weight": 3}),
|
||||
(0, 1, (("weight", 2),)),
|
||||
(0, 1, 5),
|
||||
(0, 1, "s"),
|
||||
]
|
||||
G.add_edges_from(edges)
|
||||
keydict = {0: {"weight": 3}, 1: {"weight": 2}, 5: {}, "s": {}}
|
||||
assert G._succ == {0: {1: keydict}, 1: {}}
|
||||
assert G._pred == {1: {0: keydict}, 0: {}}
|
||||
|
||||
# too few in tuple
|
||||
pytest.raises(nx.NetworkXError, G.add_edges_from, [(0,)])
|
||||
# too many in tuple
|
||||
pytest.raises(nx.NetworkXError, G.add_edges_from, [(0, 1, 2, 3, 4)])
|
||||
# not a tuple
|
||||
pytest.raises(TypeError, G.add_edges_from, [0])
|
||||
|
||||
def test_remove_edge(self):
|
||||
G = self.K3
|
||||
G.remove_edge(0, 1)
|
||||
assert G._succ == {
|
||||
0: {2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
assert G._pred == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.remove_edge, -1, 0)
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.remove_edge, 0, 2, key=1)
|
||||
|
||||
def test_remove_multiedge(self):
|
||||
G = self.K3
|
||||
G.add_edge(0, 1, key="parallel edge")
|
||||
G.remove_edge(0, 1, key="parallel edge")
|
||||
assert G._adj == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
|
||||
assert G._succ == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
|
||||
assert G._pred == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
G.remove_edge(0, 1)
|
||||
assert G._succ == {
|
||||
0: {2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
assert G._pred == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
pytest.raises((KeyError, nx.NetworkXError), G.remove_edge, -1, 0)
|
||||
|
||||
def test_remove_edges_from(self):
|
||||
G = self.K3
|
||||
G.remove_edges_from([(0, 1)])
|
||||
assert G._succ == {
|
||||
0: {2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
assert G._pred == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
G.remove_edges_from([(0, 0)]) # silent fail
|
||||
|
||||
|
||||
class TestEdgeSubgraph(_TestMultiGraphEdgeSubgraph):
|
||||
"""Unit tests for the :meth:`MultiDiGraph.edge_subgraph` method."""
|
||||
|
||||
def setup_method(self):
|
||||
# Create a quadruply-linked path graph on five nodes.
|
||||
G = nx.MultiDiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
nx.add_path(G, range(5))
|
||||
nx.add_path(G, reversed(range(5)))
|
||||
nx.add_path(G, reversed(range(5)))
|
||||
# Add some node, edge, and graph attributes.
|
||||
for i in range(5):
|
||||
G.nodes[i]["name"] = f"node{i}"
|
||||
G.adj[0][1][0]["name"] = "edge010"
|
||||
G.adj[0][1][1]["name"] = "edge011"
|
||||
G.adj[3][4][0]["name"] = "edge340"
|
||||
G.adj[3][4][1]["name"] = "edge341"
|
||||
G.graph["name"] = "graph"
|
||||
# Get the subgraph induced by one of the first edges and one of
|
||||
# the last edges.
|
||||
self.G = G
|
||||
self.H = G.edge_subgraph([(0, 1, 0), (3, 4, 1)])
|
375
venv/Lib/site-packages/networkx/classes/tests/test_multigraph.py
Normal file
375
venv/Lib/site-packages/networkx/classes/tests/test_multigraph.py
Normal file
|
@ -0,0 +1,375 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing.utils import assert_edges_equal
|
||||
|
||||
from .test_graph import BaseAttrGraphTester
|
||||
from .test_graph import TestGraph as _TestGraph
|
||||
|
||||
|
||||
class BaseMultiGraphTester(BaseAttrGraphTester):
|
||||
def test_has_edge(self):
|
||||
G = self.K3
|
||||
assert G.has_edge(0, 1)
|
||||
assert not G.has_edge(0, -1)
|
||||
assert G.has_edge(0, 1, 0)
|
||||
assert not G.has_edge(0, 1, 1)
|
||||
|
||||
def test_get_edge_data(self):
|
||||
G = self.K3
|
||||
assert G.get_edge_data(0, 1) == {0: {}}
|
||||
assert G[0][1] == {0: {}}
|
||||
assert G[0][1][0] == {}
|
||||
assert G.get_edge_data(10, 20) is None
|
||||
assert G.get_edge_data(0, 1, 0) == {}
|
||||
|
||||
def test_adjacency(self):
|
||||
G = self.K3
|
||||
assert dict(G.adjacency()) == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
|
||||
def deepcopy_edge_attr(self, H, G):
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
G[1][2][0]["foo"].append(1)
|
||||
assert G[1][2][0]["foo"] != H[1][2][0]["foo"]
|
||||
|
||||
def shallow_copy_edge_attr(self, H, G):
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
G[1][2][0]["foo"].append(1)
|
||||
assert G[1][2][0]["foo"] == H[1][2][0]["foo"]
|
||||
|
||||
def graphs_equal(self, H, G):
|
||||
assert G._adj == H._adj
|
||||
assert G._node == H._node
|
||||
assert G.graph == H.graph
|
||||
assert G.name == H.name
|
||||
if not G.is_directed() and not H.is_directed():
|
||||
assert H._adj[1][2][0] is H._adj[2][1][0]
|
||||
assert G._adj[1][2][0] is G._adj[2][1][0]
|
||||
else: # at least one is directed
|
||||
if not G.is_directed():
|
||||
G._pred = G._adj
|
||||
G._succ = G._adj
|
||||
if not H.is_directed():
|
||||
H._pred = H._adj
|
||||
H._succ = H._adj
|
||||
assert G._pred == H._pred
|
||||
assert G._succ == H._succ
|
||||
assert H._succ[1][2][0] is H._pred[2][1][0]
|
||||
assert G._succ[1][2][0] is G._pred[2][1][0]
|
||||
|
||||
def same_attrdict(self, H, G):
|
||||
# same attrdict in the edgedata
|
||||
old_foo = H[1][2][0]["foo"]
|
||||
H.adj[1][2][0]["foo"] = "baz"
|
||||
assert G._adj == H._adj
|
||||
H.adj[1][2][0]["foo"] = old_foo
|
||||
assert G._adj == H._adj
|
||||
|
||||
old_foo = H.nodes[0]["foo"]
|
||||
H.nodes[0]["foo"] = "baz"
|
||||
assert G._node == H._node
|
||||
H.nodes[0]["foo"] = old_foo
|
||||
assert G._node == H._node
|
||||
|
||||
def different_attrdict(self, H, G):
|
||||
# used by graph_equal_but_different
|
||||
old_foo = H[1][2][0]["foo"]
|
||||
H.adj[1][2][0]["foo"] = "baz"
|
||||
assert G._adj != H._adj
|
||||
H.adj[1][2][0]["foo"] = old_foo
|
||||
assert G._adj == H._adj
|
||||
|
||||
old_foo = H.nodes[0]["foo"]
|
||||
H.nodes[0]["foo"] = "baz"
|
||||
assert G._node != H._node
|
||||
H.nodes[0]["foo"] = old_foo
|
||||
assert G._node == H._node
|
||||
|
||||
def test_to_undirected(self):
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = nx.MultiGraph(G)
|
||||
self.is_shallow_copy(H, G)
|
||||
H = G.to_undirected()
|
||||
self.is_deepcopy(H, G)
|
||||
|
||||
def test_to_directed(self):
|
||||
G = self.K3
|
||||
self.add_attributes(G)
|
||||
H = nx.MultiDiGraph(G)
|
||||
self.is_shallow_copy(H, G)
|
||||
H = G.to_directed()
|
||||
self.is_deepcopy(H, G)
|
||||
|
||||
def test_number_of_edges_selfloops(self):
|
||||
G = self.K3
|
||||
G.add_edge(0, 0)
|
||||
G.add_edge(0, 0)
|
||||
G.add_edge(0, 0, key="parallel edge")
|
||||
G.remove_edge(0, 0, key="parallel edge")
|
||||
assert G.number_of_edges(0, 0) == 2
|
||||
G.remove_edge(0, 0)
|
||||
assert G.number_of_edges(0, 0) == 1
|
||||
|
||||
def test_edge_lookup(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, foo="bar")
|
||||
G.add_edge(1, 2, "key", foo="biz")
|
||||
assert_edges_equal(G.edges[1, 2, 0], {"foo": "bar"})
|
||||
assert_edges_equal(G.edges[1, 2, "key"], {"foo": "biz"})
|
||||
|
||||
def test_edge_attr4(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(1, 2, key=0, data=7, spam="bar", bar="foo")
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 7, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G[1][2][0]["data"] = 10 # OK to set data like this
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 10, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
|
||||
G.adj[1][2][0]["data"] = 20
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 20, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G.edges[1, 2, 0]["data"] = 21 # another spelling, "edge"
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"data": 21, "spam": "bar", "bar": "foo"})]
|
||||
)
|
||||
G.adj[1][2][0]["listdata"] = [20, 200]
|
||||
G.adj[1][2][0]["weight"] = 20
|
||||
assert_edges_equal(
|
||||
G.edges(data=True),
|
||||
[
|
||||
(
|
||||
1,
|
||||
2,
|
||||
{
|
||||
"data": 21,
|
||||
"spam": "bar",
|
||||
"bar": "foo",
|
||||
"listdata": [20, 200],
|
||||
"weight": 20,
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class TestMultiGraph(BaseMultiGraphTester, _TestGraph):
|
||||
def setup_method(self):
|
||||
self.Graph = nx.MultiGraph
|
||||
# build K3
|
||||
ed1, ed2, ed3 = ({0: {}}, {0: {}}, {0: {}})
|
||||
self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = self.k3adj
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
def test_data_input(self):
|
||||
G = self.Graph({1: [2], 2: [1]}, name="test")
|
||||
assert G.name == "test"
|
||||
expected = [(1, {2: {0: {}}}), (2, {1: {0: {}}})]
|
||||
assert sorted(G.adj.items()) == expected
|
||||
|
||||
def test_getitem(self):
|
||||
G = self.K3
|
||||
assert G[0] == {1: {0: {}}, 2: {0: {}}}
|
||||
with pytest.raises(KeyError):
|
||||
G.__getitem__("j")
|
||||
with pytest.raises(TypeError):
|
||||
G.__getitem__(["A"])
|
||||
|
||||
def test_remove_node(self):
|
||||
G = self.K3
|
||||
G.remove_node(0)
|
||||
assert G.adj == {1: {2: {0: {}}}, 2: {1: {0: {}}}}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_node(-1)
|
||||
|
||||
def test_add_edge(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(0, 1)
|
||||
assert G.adj == {0: {1: {0: {}}}, 1: {0: {0: {}}}}
|
||||
G = self.Graph()
|
||||
G.add_edge(*(0, 1))
|
||||
assert G.adj == {0: {1: {0: {}}}, 1: {0: {0: {}}}}
|
||||
|
||||
def test_add_edge_conflicting_key(self):
|
||||
G = self.Graph()
|
||||
G.add_edge(0, 1, key=1)
|
||||
G.add_edge(0, 1)
|
||||
assert G.number_of_edges() == 2
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1, 1, {})])
|
||||
G.add_edges_from([(0, 1)])
|
||||
assert G.number_of_edges() == 2
|
||||
|
||||
def test_add_edges_from(self):
|
||||
G = self.Graph()
|
||||
G.add_edges_from([(0, 1), (0, 1, {"weight": 3})])
|
||||
assert G.adj == {
|
||||
0: {1: {0: {}, 1: {"weight": 3}}},
|
||||
1: {0: {0: {}, 1: {"weight": 3}}},
|
||||
}
|
||||
G.add_edges_from([(0, 1), (0, 1, {"weight": 3})], weight=2)
|
||||
assert G.adj == {
|
||||
0: {1: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}},
|
||||
1: {0: {0: {}, 1: {"weight": 3}, 2: {"weight": 2}, 3: {"weight": 3}}},
|
||||
}
|
||||
G = self.Graph()
|
||||
edges = [
|
||||
(0, 1, {"weight": 3}),
|
||||
(0, 1, (("weight", 2),)),
|
||||
(0, 1, 5),
|
||||
(0, 1, "s"),
|
||||
]
|
||||
G.add_edges_from(edges)
|
||||
keydict = {0: {"weight": 3}, 1: {"weight": 2}, 5: {}, "s": {}}
|
||||
assert G._adj == {0: {1: keydict}, 1: {0: keydict}}
|
||||
|
||||
# too few in tuple
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0,)])
|
||||
# too many in tuple
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.add_edges_from([(0, 1, 2, 3, 4)])
|
||||
# not a tuple
|
||||
with pytest.raises(TypeError):
|
||||
G.add_edges_from([0])
|
||||
|
||||
def test_remove_edge(self):
|
||||
G = self.K3
|
||||
G.remove_edge(0, 1)
|
||||
assert G.adj == {0: {2: {0: {}}}, 1: {2: {0: {}}}, 2: {0: {0: {}}, 1: {0: {}}}}
|
||||
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_edge(-1, 0)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_edge(0, 2, key=1)
|
||||
|
||||
def test_remove_edges_from(self):
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from([(0, 1)])
|
||||
kd = {0: {}}
|
||||
assert G.adj == {0: {2: kd}, 1: {2: kd}, 2: {0: kd, 1: kd}}
|
||||
G.remove_edges_from([(0, 0)]) # silent fail
|
||||
self.K3.add_edge(0, 1)
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from(list(G.edges(data=True, keys=True)))
|
||||
assert G.adj == {0: {}, 1: {}, 2: {}}
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from(list(G.edges(data=False, keys=True)))
|
||||
assert G.adj == {0: {}, 1: {}, 2: {}}
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from(list(G.edges(data=False, keys=False)))
|
||||
assert G.adj == {0: {}, 1: {}, 2: {}}
|
||||
G = self.K3.copy()
|
||||
G.remove_edges_from([(0, 1, 0), (0, 2, 0, {}), (1, 2)])
|
||||
assert G.adj == {0: {1: {1: {}}}, 1: {0: {1: {}}}, 2: {}}
|
||||
|
||||
def test_remove_multiedge(self):
|
||||
G = self.K3
|
||||
G.add_edge(0, 1, key="parallel edge")
|
||||
G.remove_edge(0, 1, key="parallel edge")
|
||||
assert G.adj == {
|
||||
0: {1: {0: {}}, 2: {0: {}}},
|
||||
1: {0: {0: {}}, 2: {0: {}}},
|
||||
2: {0: {0: {}}, 1: {0: {}}},
|
||||
}
|
||||
G.remove_edge(0, 1)
|
||||
kd = {0: {}}
|
||||
assert G.adj == {0: {2: kd}, 1: {2: kd}, 2: {0: kd, 1: kd}}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G.remove_edge(-1, 0)
|
||||
|
||||
|
||||
class TestEdgeSubgraph:
|
||||
"""Unit tests for the :meth:`MultiGraph.edge_subgraph` method."""
|
||||
|
||||
def setup_method(self):
|
||||
# Create a doubly-linked path graph on five nodes.
|
||||
G = nx.MultiGraph()
|
||||
nx.add_path(G, range(5))
|
||||
nx.add_path(G, range(5))
|
||||
# Add some node, edge, and graph attributes.
|
||||
for i in range(5):
|
||||
G.nodes[i]["name"] = f"node{i}"
|
||||
G.adj[0][1][0]["name"] = "edge010"
|
||||
G.adj[0][1][1]["name"] = "edge011"
|
||||
G.adj[3][4][0]["name"] = "edge340"
|
||||
G.adj[3][4][1]["name"] = "edge341"
|
||||
G.graph["name"] = "graph"
|
||||
# Get the subgraph induced by one of the first edges and one of
|
||||
# the last edges.
|
||||
self.G = G
|
||||
self.H = G.edge_subgraph([(0, 1, 0), (3, 4, 1)])
|
||||
|
||||
def test_correct_nodes(self):
|
||||
"""Tests that the subgraph has the correct nodes."""
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_correct_edges(self):
|
||||
"""Tests that the subgraph has the correct edges."""
|
||||
assert [(0, 1, 0, "edge010"), (3, 4, 1, "edge341")] == sorted(
|
||||
self.H.edges(keys=True, data="name")
|
||||
)
|
||||
|
||||
def test_add_node(self):
|
||||
"""Tests that adding a node to the original graph does not
|
||||
affect the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.add_node(5)
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_remove_node(self):
|
||||
"""Tests that removing a node in the original graph does
|
||||
affect the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.remove_node(0)
|
||||
assert [1, 3, 4] == sorted(self.H.nodes())
|
||||
|
||||
def test_node_attr_dict(self):
|
||||
"""Tests that the node attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for v in self.H:
|
||||
assert self.G.nodes[v] == self.H.nodes[v]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G.nodes[0]["name"] = "foo"
|
||||
assert self.G.nodes[0] == self.H.nodes[0]
|
||||
self.H.nodes[1]["name"] = "bar"
|
||||
assert self.G.nodes[1] == self.H.nodes[1]
|
||||
|
||||
def test_edge_attr_dict(self):
|
||||
"""Tests that the edge attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for u, v, k in self.H.edges(keys=True):
|
||||
assert self.G._adj[u][v][k] == self.H._adj[u][v][k]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G._adj[0][1][0]["name"] = "foo"
|
||||
assert self.G._adj[0][1][0]["name"] == self.H._adj[0][1][0]["name"]
|
||||
self.H._adj[3][4][1]["name"] = "bar"
|
||||
assert self.G._adj[3][4][1]["name"] == self.H._adj[3][4][1]["name"]
|
||||
|
||||
def test_graph_attr_dict(self):
|
||||
"""Tests that the graph attribute dictionary of the two graphs
|
||||
is the same object.
|
||||
|
||||
"""
|
||||
assert self.G.graph is self.H.graph
|
|
@ -0,0 +1,40 @@
|
|||
import networkx as nx
|
||||
|
||||
|
||||
class TestOrdered:
|
||||
# Just test instantiation.
|
||||
def test_graph(self):
|
||||
G = nx.OrderedGraph()
|
||||
|
||||
def test_digraph(self):
|
||||
G = nx.OrderedDiGraph()
|
||||
|
||||
def test_multigraph(self):
|
||||
G = nx.OrderedMultiGraph()
|
||||
|
||||
def test_multidigraph(self):
|
||||
G = nx.OrderedMultiDiGraph()
|
||||
|
||||
|
||||
class TestOrderedFeatures:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.OrderedDiGraph()
|
||||
cls.G.add_nodes_from([1, 2, 3])
|
||||
cls.G.add_edges_from([(2, 3), (1, 3)])
|
||||
|
||||
def test_subgraph_order(self):
|
||||
G = self.G
|
||||
G_sub = G.subgraph([1, 2, 3])
|
||||
assert list(G.nodes) == list(G_sub.nodes)
|
||||
assert list(G.edges) == list(G_sub.edges)
|
||||
assert list(G.pred[3]) == list(G_sub.pred[3])
|
||||
assert [2, 1] == list(G_sub.pred[3])
|
||||
assert [] == list(G_sub.succ[3])
|
||||
|
||||
G_sub = nx.induced_subgraph(G, [1, 2, 3])
|
||||
assert list(G.nodes) == list(G_sub.nodes)
|
||||
assert list(G.edges) == list(G_sub.edges)
|
||||
assert list(G.pred[3]) == list(G_sub.pred[3])
|
||||
assert [2, 1] == list(G_sub.pred[3])
|
||||
assert [] == list(G_sub.succ[3])
|
1338
venv/Lib/site-packages/networkx/classes/tests/test_reportviews.py
Normal file
1338
venv/Lib/site-packages/networkx/classes/tests/test_reportviews.py
Normal file
File diff suppressed because it is too large
Load diff
184
venv/Lib/site-packages/networkx/classes/tests/test_special.py
Normal file
184
venv/Lib/site-packages/networkx/classes/tests/test_special.py
Normal file
|
@ -0,0 +1,184 @@
|
|||
from collections import OrderedDict
|
||||
import networkx as nx
|
||||
from .test_graph import TestGraph as _TestGraph
|
||||
from .test_graph import BaseGraphTester
|
||||
from .test_digraph import TestDiGraph as _TestDiGraph
|
||||
from .test_digraph import BaseDiGraphTester
|
||||
from .test_multigraph import TestMultiGraph as _TestMultiGraph
|
||||
from .test_multidigraph import TestMultiDiGraph as _TestMultiDiGraph
|
||||
|
||||
|
||||
def test_factories():
|
||||
class mydict1(dict):
|
||||
pass
|
||||
|
||||
class mydict2(dict):
|
||||
pass
|
||||
|
||||
class mydict3(dict):
|
||||
pass
|
||||
|
||||
class mydict4(dict):
|
||||
pass
|
||||
|
||||
class mydict5(dict):
|
||||
pass
|
||||
|
||||
for Graph in (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph):
|
||||
# print("testing class: ", Graph.__name__)
|
||||
class MyGraph(Graph):
|
||||
node_dict_factory = mydict1
|
||||
adjlist_outer_dict_factory = mydict2
|
||||
adjlist_inner_dict_factory = mydict3
|
||||
edge_key_dict_factory = mydict4
|
||||
edge_attr_dict_factory = mydict5
|
||||
|
||||
G = MyGraph()
|
||||
assert isinstance(G._node, mydict1)
|
||||
assert isinstance(G._adj, mydict2)
|
||||
G.add_node(1)
|
||||
assert isinstance(G._adj[1], mydict3)
|
||||
if G.is_directed():
|
||||
assert isinstance(G._pred, mydict2)
|
||||
assert isinstance(G._succ, mydict2)
|
||||
assert isinstance(G._pred[1], mydict3)
|
||||
G.add_edge(1, 2)
|
||||
if G.is_multigraph():
|
||||
assert isinstance(G._adj[1][2], mydict4)
|
||||
assert isinstance(G._adj[1][2][0], mydict5)
|
||||
else:
|
||||
assert isinstance(G._adj[1][2], mydict5)
|
||||
|
||||
|
||||
class TestSpecialGraph(_TestGraph):
|
||||
def setup_method(self):
|
||||
_TestGraph.setup_method(self)
|
||||
self.Graph = nx.Graph
|
||||
|
||||
|
||||
class TestOrderedGraph(_TestGraph):
|
||||
def setup_method(self):
|
||||
_TestGraph.setup_method(self)
|
||||
|
||||
class MyGraph(nx.Graph):
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
self.Graph = MyGraph
|
||||
|
||||
|
||||
class TestThinGraph(BaseGraphTester):
|
||||
def setup_method(self):
|
||||
all_edge_dict = {"weight": 1}
|
||||
|
||||
class MyGraph(nx.Graph):
|
||||
def edge_attr_dict_factory(self):
|
||||
return all_edge_dict
|
||||
|
||||
self.Graph = MyGraph
|
||||
# build dict-of-dict-of-dict K3
|
||||
ed1, ed2, ed3 = (all_edge_dict, all_edge_dict, all_edge_dict)
|
||||
self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed1, 2: ed3}, 2: {0: ed2, 1: ed3}}
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = self.k3adj
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
|
||||
class TestSpecialDiGraph(_TestDiGraph):
|
||||
def setup_method(self):
|
||||
_TestDiGraph.setup_method(self)
|
||||
self.Graph = nx.DiGraph
|
||||
|
||||
|
||||
class TestOrderedDiGraph(_TestDiGraph):
|
||||
def setup_method(self):
|
||||
_TestDiGraph.setup_method(self)
|
||||
|
||||
class MyGraph(nx.DiGraph):
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
self.Graph = MyGraph
|
||||
|
||||
|
||||
class TestThinDiGraph(BaseDiGraphTester):
|
||||
def setup_method(self):
|
||||
all_edge_dict = {"weight": 1}
|
||||
|
||||
class MyGraph(nx.DiGraph):
|
||||
def edge_attr_dict_factory(self):
|
||||
return all_edge_dict
|
||||
|
||||
self.Graph = MyGraph
|
||||
# build dict-of-dict-of-dict K3
|
||||
ed1, ed2, ed3 = (all_edge_dict, all_edge_dict, all_edge_dict)
|
||||
ed4, ed5, ed6 = (all_edge_dict, all_edge_dict, all_edge_dict)
|
||||
self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed3, 2: ed4}, 2: {0: ed5, 1: ed6}}
|
||||
self.k3edges = [(0, 1), (0, 2), (1, 2)]
|
||||
self.k3nodes = [0, 1, 2]
|
||||
self.K3 = self.Graph()
|
||||
self.K3._adj = self.K3._succ = self.k3adj
|
||||
self.K3._pred = {0: {1: ed3, 2: ed5}, 1: {0: ed1, 2: ed6}, 2: {0: ed2, 1: ed4}}
|
||||
self.K3._node = {}
|
||||
self.K3._node[0] = {}
|
||||
self.K3._node[1] = {}
|
||||
self.K3._node[2] = {}
|
||||
|
||||
ed1, ed2 = (all_edge_dict, all_edge_dict)
|
||||
self.P3 = self.Graph()
|
||||
self.P3._adj = {0: {1: ed1}, 1: {2: ed2}, 2: {}}
|
||||
self.P3._succ = self.P3._adj
|
||||
self.P3._pred = {0: {}, 1: {0: ed1}, 2: {1: ed2}}
|
||||
self.P3._node = {}
|
||||
self.P3._node[0] = {}
|
||||
self.P3._node[1] = {}
|
||||
self.P3._node[2] = {}
|
||||
|
||||
|
||||
class TestSpecialMultiGraph(_TestMultiGraph):
|
||||
def setup_method(self):
|
||||
_TestMultiGraph.setup_method(self)
|
||||
self.Graph = nx.MultiGraph
|
||||
|
||||
|
||||
class TestOrderedMultiGraph(_TestMultiGraph):
|
||||
def setup_method(self):
|
||||
_TestMultiGraph.setup_method(self)
|
||||
|
||||
class MyGraph(nx.MultiGraph):
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_key_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
self.Graph = MyGraph
|
||||
|
||||
|
||||
class TestSpecialMultiDiGraph(_TestMultiDiGraph):
|
||||
def setup_method(self):
|
||||
_TestMultiDiGraph.setup_method(self)
|
||||
self.Graph = nx.MultiDiGraph
|
||||
|
||||
|
||||
class TestOrderedMultiDiGraph(_TestMultiDiGraph):
|
||||
def setup_method(self):
|
||||
_TestMultiDiGraph.setup_method(self)
|
||||
|
||||
class MyGraph(nx.MultiDiGraph):
|
||||
node_dict_factory = OrderedDict
|
||||
adjlist_outer_dict_factory = OrderedDict
|
||||
adjlist_inner_dict_factory = OrderedDict
|
||||
edge_key_dict_factory = OrderedDict
|
||||
edge_attr_dict_factory = OrderedDict
|
||||
|
||||
self.Graph = MyGraph
|
|
@ -0,0 +1,351 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class TestSubGraphView:
|
||||
gview = staticmethod(nx.graphviews.subgraph_view)
|
||||
graph = nx.Graph
|
||||
hide_edges_filter = staticmethod(nx.filters.hide_edges)
|
||||
show_edges_filter = staticmethod(nx.filters.show_edges)
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.path_graph(9, create_using=cls.graph())
|
||||
cls.hide_edges_w_hide_nodes = {(3, 4), (4, 5), (5, 6)}
|
||||
|
||||
def test_hidden_nodes(self):
|
||||
hide_nodes = [4, 5, 111]
|
||||
nodes_gone = nx.filters.hide_nodes(hide_nodes)
|
||||
gview = self.gview
|
||||
print(gview)
|
||||
G = gview(self.G, filter_node=nodes_gone)
|
||||
assert self.G.nodes - G.nodes == {4, 5}
|
||||
assert self.G.edges - G.edges == self.hide_edges_w_hide_nodes
|
||||
if G.is_directed():
|
||||
assert list(G[3]) == []
|
||||
assert list(G[2]) == [3]
|
||||
else:
|
||||
assert list(G[3]) == [2]
|
||||
assert set(G[2]) == {1, 3}
|
||||
pytest.raises(KeyError, G.__getitem__, 4)
|
||||
pytest.raises(KeyError, G.__getitem__, 112)
|
||||
pytest.raises(KeyError, G.__getitem__, 111)
|
||||
assert G.degree(3) == (3 if G.is_multigraph() else 1)
|
||||
assert G.size() == (7 if G.is_multigraph() else 5)
|
||||
|
||||
def test_hidden_edges(self):
|
||||
hide_edges = [(2, 3), (8, 7), (222, 223)]
|
||||
edges_gone = self.hide_edges_filter(hide_edges)
|
||||
gview = self.gview
|
||||
G = gview(self.G, filter_edge=edges_gone)
|
||||
assert self.G.nodes == G.nodes
|
||||
if G.is_directed():
|
||||
assert self.G.edges - G.edges == {(2, 3)}
|
||||
assert list(G[2]) == []
|
||||
assert list(G.pred[3]) == []
|
||||
assert list(G.pred[2]) == [1]
|
||||
assert G.size() == 7
|
||||
else:
|
||||
assert self.G.edges - G.edges == {(2, 3), (7, 8)}
|
||||
assert list(G[2]) == [1]
|
||||
assert G.size() == 6
|
||||
assert list(G[3]) == [4]
|
||||
pytest.raises(KeyError, G.__getitem__, 221)
|
||||
pytest.raises(KeyError, G.__getitem__, 222)
|
||||
assert G.degree(3) == 1
|
||||
|
||||
def test_shown_node(self):
|
||||
induced_subgraph = nx.filters.show_nodes([2, 3, 111])
|
||||
gview = self.gview
|
||||
G = gview(self.G, filter_node=induced_subgraph)
|
||||
assert set(G.nodes) == {2, 3}
|
||||
if G.is_directed():
|
||||
assert list(G[3]) == []
|
||||
else:
|
||||
assert list(G[3]) == [2]
|
||||
assert list(G[2]) == [3]
|
||||
pytest.raises(KeyError, G.__getitem__, 4)
|
||||
pytest.raises(KeyError, G.__getitem__, 112)
|
||||
pytest.raises(KeyError, G.__getitem__, 111)
|
||||
assert G.degree(3) == (3 if G.is_multigraph() else 1)
|
||||
assert G.size() == (3 if G.is_multigraph() else 1)
|
||||
|
||||
def test_shown_edges(self):
|
||||
show_edges = [(2, 3), (8, 7), (222, 223)]
|
||||
edge_subgraph = self.show_edges_filter(show_edges)
|
||||
G = self.gview(self.G, filter_edge=edge_subgraph)
|
||||
assert self.G.nodes == G.nodes
|
||||
if G.is_directed():
|
||||
assert G.edges == {(2, 3)}
|
||||
assert list(G[3]) == []
|
||||
assert list(G[2]) == [3]
|
||||
assert list(G.pred[3]) == [2]
|
||||
assert list(G.pred[2]) == []
|
||||
assert G.size() == 1
|
||||
else:
|
||||
assert G.edges == {(2, 3), (7, 8)}
|
||||
assert list(G[3]) == [2]
|
||||
assert list(G[2]) == [3]
|
||||
assert G.size() == 2
|
||||
pytest.raises(KeyError, G.__getitem__, 221)
|
||||
pytest.raises(KeyError, G.__getitem__, 222)
|
||||
assert G.degree(3) == 1
|
||||
|
||||
|
||||
class TestSubDiGraphView(TestSubGraphView):
|
||||
gview = staticmethod(nx.graphviews.subgraph_view)
|
||||
graph = nx.DiGraph
|
||||
hide_edges_filter = staticmethod(nx.filters.hide_diedges)
|
||||
show_edges_filter = staticmethod(nx.filters.show_diedges)
|
||||
hide_edges = [(2, 3), (8, 7), (222, 223)]
|
||||
excluded = {(2, 3), (3, 4), (4, 5), (5, 6)}
|
||||
|
||||
def test_inoutedges(self):
|
||||
edges_gone = self.hide_edges_filter(self.hide_edges)
|
||||
hide_nodes = [4, 5, 111]
|
||||
nodes_gone = nx.filters.hide_nodes(hide_nodes)
|
||||
G = self.gview(self.G, nodes_gone, edges_gone)
|
||||
|
||||
assert self.G.in_edges - G.in_edges == self.excluded
|
||||
assert self.G.out_edges - G.out_edges == self.excluded
|
||||
|
||||
def test_pred(self):
|
||||
edges_gone = self.hide_edges_filter(self.hide_edges)
|
||||
hide_nodes = [4, 5, 111]
|
||||
nodes_gone = nx.filters.hide_nodes(hide_nodes)
|
||||
G = self.gview(self.G, nodes_gone, edges_gone)
|
||||
|
||||
assert list(G.pred[2]) == [1]
|
||||
assert list(G.pred[6]) == []
|
||||
|
||||
def test_inout_degree(self):
|
||||
edges_gone = self.hide_edges_filter(self.hide_edges)
|
||||
hide_nodes = [4, 5, 111]
|
||||
nodes_gone = nx.filters.hide_nodes(hide_nodes)
|
||||
G = self.gview(self.G, nodes_gone, edges_gone)
|
||||
|
||||
assert G.degree(2) == 1
|
||||
assert G.out_degree(2) == 0
|
||||
assert G.in_degree(2) == 1
|
||||
assert G.size() == 4
|
||||
|
||||
|
||||
# multigraph
|
||||
class TestMultiGraphView(TestSubGraphView):
|
||||
gview = staticmethod(nx.graphviews.subgraph_view)
|
||||
graph = nx.MultiGraph
|
||||
hide_edges_filter = staticmethod(nx.filters.hide_multiedges)
|
||||
show_edges_filter = staticmethod(nx.filters.show_multiedges)
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.path_graph(9, create_using=cls.graph())
|
||||
multiedges = {(2, 3, 4), (2, 3, 5)}
|
||||
cls.G.add_edges_from(multiedges)
|
||||
cls.hide_edges_w_hide_nodes = {(3, 4, 0), (4, 5, 0), (5, 6, 0)}
|
||||
|
||||
def test_hidden_edges(self):
|
||||
hide_edges = [(2, 3, 4), (2, 3, 3), (8, 7, 0), (222, 223, 0)]
|
||||
edges_gone = self.hide_edges_filter(hide_edges)
|
||||
G = self.gview(self.G, filter_edge=edges_gone)
|
||||
assert self.G.nodes == G.nodes
|
||||
if G.is_directed():
|
||||
assert self.G.edges - G.edges == {(2, 3, 4)}
|
||||
assert list(G[3]) == [4]
|
||||
assert list(G[2]) == [3]
|
||||
assert list(G.pred[3]) == [2] # only one 2 but two edges
|
||||
assert list(G.pred[2]) == [1]
|
||||
assert G.size() == 9
|
||||
else:
|
||||
assert self.G.edges - G.edges == {(2, 3, 4), (7, 8, 0)}
|
||||
assert list(G[3]) == [2, 4]
|
||||
assert list(G[2]) == [1, 3]
|
||||
assert G.size() == 8
|
||||
assert G.degree(3) == 3
|
||||
pytest.raises(KeyError, G.__getitem__, 221)
|
||||
pytest.raises(KeyError, G.__getitem__, 222)
|
||||
|
||||
def test_shown_edges(self):
|
||||
show_edges = [(2, 3, 4), (2, 3, 3), (8, 7, 0), (222, 223, 0)]
|
||||
edge_subgraph = self.show_edges_filter(show_edges)
|
||||
G = self.gview(self.G, filter_edge=edge_subgraph)
|
||||
assert self.G.nodes == G.nodes
|
||||
if G.is_directed():
|
||||
assert G.edges == {(2, 3, 4)}
|
||||
assert list(G[3]) == []
|
||||
assert list(G.pred[3]) == [2]
|
||||
assert list(G.pred[2]) == []
|
||||
assert G.size() == 1
|
||||
else:
|
||||
assert G.edges == {(2, 3, 4), (7, 8, 0)}
|
||||
assert G.size() == 2
|
||||
assert list(G[3]) == [2]
|
||||
assert G.degree(3) == 1
|
||||
assert list(G[2]) == [3]
|
||||
pytest.raises(KeyError, G.__getitem__, 221)
|
||||
pytest.raises(KeyError, G.__getitem__, 222)
|
||||
|
||||
|
||||
# multidigraph
|
||||
class TestMultiDiGraphView(TestMultiGraphView, TestSubDiGraphView):
|
||||
gview = staticmethod(nx.graphviews.subgraph_view)
|
||||
graph = nx.MultiDiGraph
|
||||
hide_edges_filter = staticmethod(nx.filters.hide_multidiedges)
|
||||
show_edges_filter = staticmethod(nx.filters.show_multidiedges)
|
||||
hide_edges = [(2, 3, 0), (8, 7, 0), (222, 223, 0)]
|
||||
excluded = {(2, 3, 0), (3, 4, 0), (4, 5, 0), (5, 6, 0)}
|
||||
|
||||
def test_inout_degree(self):
|
||||
edges_gone = self.hide_edges_filter(self.hide_edges)
|
||||
hide_nodes = [4, 5, 111]
|
||||
nodes_gone = nx.filters.hide_nodes(hide_nodes)
|
||||
G = self.gview(self.G, nodes_gone, edges_gone)
|
||||
|
||||
assert G.degree(2) == 3
|
||||
assert G.out_degree(2) == 2
|
||||
assert G.in_degree(2) == 1
|
||||
assert G.size() == 6
|
||||
|
||||
|
||||
# induced_subgraph
|
||||
class TestInducedSubGraph:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.K3 = G = nx.complete_graph(3)
|
||||
G.graph["foo"] = []
|
||||
G.nodes[0]["foo"] = []
|
||||
G.remove_edge(1, 2)
|
||||
ll = []
|
||||
G.add_edge(1, 2, foo=ll)
|
||||
G.add_edge(2, 1, foo=ll)
|
||||
|
||||
def test_full_graph(self):
|
||||
G = self.K3
|
||||
H = nx.induced_subgraph(G, [0, 1, 2, 5])
|
||||
assert H.name == G.name
|
||||
self.graphs_equal(H, G)
|
||||
self.same_attrdict(H, G)
|
||||
|
||||
def test_partial_subgraph(self):
|
||||
G = self.K3
|
||||
H = nx.induced_subgraph(G, 0)
|
||||
assert dict(H.adj) == {0: {}}
|
||||
assert dict(G.adj) != {0: {}}
|
||||
|
||||
H = nx.induced_subgraph(G, [0, 1])
|
||||
assert dict(H.adj) == {0: {1: {}}, 1: {0: {}}}
|
||||
|
||||
def same_attrdict(self, H, G):
|
||||
old_foo = H[1][2]["foo"]
|
||||
H.edges[1, 2]["foo"] = "baz"
|
||||
assert G.edges == H.edges
|
||||
H.edges[1, 2]["foo"] = old_foo
|
||||
assert G.edges == H.edges
|
||||
old_foo = H.nodes[0]["foo"]
|
||||
H.nodes[0]["foo"] = "baz"
|
||||
assert G.nodes == H.nodes
|
||||
H.nodes[0]["foo"] = old_foo
|
||||
assert G.nodes == H.nodes
|
||||
|
||||
def graphs_equal(self, H, G):
|
||||
assert G._adj == H._adj
|
||||
assert G._node == H._node
|
||||
assert G.graph == H.graph
|
||||
assert G.name == H.name
|
||||
if not G.is_directed() and not H.is_directed():
|
||||
assert H._adj[1][2] is H._adj[2][1]
|
||||
assert G._adj[1][2] is G._adj[2][1]
|
||||
else: # at least one is directed
|
||||
if not G.is_directed():
|
||||
G._pred = G._adj
|
||||
G._succ = G._adj
|
||||
if not H.is_directed():
|
||||
H._pred = H._adj
|
||||
H._succ = H._adj
|
||||
assert G._pred == H._pred
|
||||
assert G._succ == H._succ
|
||||
assert H._succ[1][2] is H._pred[2][1]
|
||||
assert G._succ[1][2] is G._pred[2][1]
|
||||
|
||||
|
||||
# edge_subgraph
|
||||
class TestEdgeSubGraph:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
# Create a path graph on five nodes.
|
||||
cls.G = G = nx.path_graph(5)
|
||||
# Add some node, edge, and graph attributes.
|
||||
for i in range(5):
|
||||
G.nodes[i]["name"] = f"node{i}"
|
||||
G.edges[0, 1]["name"] = "edge01"
|
||||
G.edges[3, 4]["name"] = "edge34"
|
||||
G.graph["name"] = "graph"
|
||||
# Get the subgraph induced by the first and last edges.
|
||||
cls.H = nx.edge_subgraph(G, [(0, 1), (3, 4)])
|
||||
|
||||
def test_correct_nodes(self):
|
||||
"""Tests that the subgraph has the correct nodes."""
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes)
|
||||
|
||||
def test_correct_edges(self):
|
||||
"""Tests that the subgraph has the correct edges."""
|
||||
assert [(0, 1, "edge01"), (3, 4, "edge34")] == sorted(self.H.edges(data="name"))
|
||||
|
||||
def test_add_node(self):
|
||||
"""Tests that adding a node to the original graph does not
|
||||
affect the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.add_node(5)
|
||||
assert [0, 1, 3, 4] == sorted(self.H.nodes)
|
||||
self.G.remove_node(5)
|
||||
|
||||
def test_remove_node(self):
|
||||
"""Tests that removing a node in the original graph
|
||||
removes the nodes of the subgraph.
|
||||
|
||||
"""
|
||||
self.G.remove_node(0)
|
||||
assert [1, 3, 4] == sorted(self.H.nodes)
|
||||
self.G.add_edge(0, 1)
|
||||
|
||||
def test_node_attr_dict(self):
|
||||
"""Tests that the node attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for v in self.H:
|
||||
assert self.G.nodes[v] == self.H.nodes[v]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G.nodes[0]["name"] = "foo"
|
||||
assert self.G.nodes[0] == self.H.nodes[0]
|
||||
self.H.nodes[1]["name"] = "bar"
|
||||
assert self.G.nodes[1] == self.H.nodes[1]
|
||||
|
||||
def test_edge_attr_dict(self):
|
||||
"""Tests that the edge attribute dictionary of the two graphs is
|
||||
the same object.
|
||||
|
||||
"""
|
||||
for u, v in self.H.edges():
|
||||
assert self.G.edges[u, v] == self.H.edges[u, v]
|
||||
# Making a change to G should make a change in H and vice versa.
|
||||
self.G.edges[0, 1]["name"] = "foo"
|
||||
assert self.G.edges[0, 1]["name"] == self.H.edges[0, 1]["name"]
|
||||
self.H.edges[3, 4]["name"] = "bar"
|
||||
assert self.G.edges[3, 4]["name"] == self.H.edges[3, 4]["name"]
|
||||
|
||||
def test_graph_attr_dict(self):
|
||||
"""Tests that the graph attribute dictionary of the two graphs
|
||||
is the same object.
|
||||
|
||||
"""
|
||||
assert self.G.graph is self.H.graph
|
||||
|
||||
def test_readonly(self):
|
||||
"""Tests that the subgraph cannot change the graph structure"""
|
||||
pytest.raises(nx.NetworkXError, self.H.add_node, 5)
|
||||
pytest.raises(nx.NetworkXError, self.H.remove_node, 0)
|
||||
pytest.raises(nx.NetworkXError, self.H.add_edge, 5, 6)
|
||||
pytest.raises(nx.NetworkXError, self.H.remove_edge, 0, 1)
|
Loading…
Add table
Add a link
Reference in a new issue