Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/classes/graphviews.py: 21%
53 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1"""View of Graphs as SubGraph, Reverse, Directed, Undirected.
3In some algorithms it is convenient to temporarily morph
4a graph to exclude some nodes or edges. It should be better
5to do that via a view than to remove and then re-add.
6In other algorithms it is convenient to temporarily morph
7a graph to reverse directed edges, or treat a directed graph
8as undirected, etc. This module provides those graph views.
10The resulting views are essentially read-only graphs that
11report data from the original graph object. We provide an
12attribute G._graph which points to the underlying graph object.
14Note: Since graphviews look like graphs, one can end up with
15view-of-view-of-view chains. Be careful with chains because
16they become very slow with about 15 nested views.
17For the common simple case of node induced subgraphs created
18from the graph class, we short-cut the chain by returning a
19subgraph of the original graph directly rather than a subgraph
20of a subgraph. We are careful not to disrupt any edge filter in
21the middle subgraph. In general, determining how to short-cut
22the chain is tricky and much harder with restricted_views than
23with induced subgraphs.
24Often it is easiest to use .copy() to avoid chains.
25"""
26import networkx as nx
27from networkx.classes.coreviews import (
28 FilterAdjacency,
29 FilterAtlas,
30 FilterMultiAdjacency,
31 UnionAdjacency,
32 UnionMultiAdjacency,
33)
34from networkx.classes.filters import no_filter
35from networkx.exception import NetworkXError
36from networkx.utils import deprecate_positional_args, not_implemented_for
38__all__ = ["generic_graph_view", "subgraph_view", "reverse_view"]
41def generic_graph_view(G, create_using=None):
42 """Returns a read-only view of `G`.
44 The graph `G` and its attributes are not copied but viewed through the new graph object
45 of the same class as `G` (or of the class specified in `create_using`).
47 Parameters
48 ----------
49 G : graph
50 A directed/undirected graph/multigraph.
52 create_using : NetworkX graph constructor, optional (default=None)
53 Graph type to create. If graph instance, then cleared before populated.
54 If `None`, then the appropriate Graph type is inferred from `G`.
56 Returns
57 -------
58 newG : graph
59 A view of the input graph `G` and its attributes as viewed through
60 the `create_using` class.
62 Raises
63 ------
64 NetworkXError
65 If `G` is a multigraph (or multidigraph) but `create_using` is not, or vice versa.
67 Notes
68 -----
69 The returned graph view is read-only (cannot modify the graph).
70 Yet the view reflects any changes in `G`. The intent is to mimic dict views.
72 Examples
73 --------
74 >>> G = nx.Graph()
75 >>> G.add_edge(1, 2, weight=0.3)
76 >>> G.add_edge(2, 3, weight=0.5)
77 >>> G.edges(data=True)
78 EdgeDataView([(1, 2, {'weight': 0.3}), (2, 3, {'weight': 0.5})])
80 The view exposes the attributes from the original graph.
82 >>> viewG = nx.graphviews.generic_graph_view(G)
83 >>> viewG.edges(data=True)
84 EdgeDataView([(1, 2, {'weight': 0.3}), (2, 3, {'weight': 0.5})])
86 Changes to `G` are reflected in `viewG`.
88 >>> G.remove_edge(2, 3)
89 >>> G.edges(data=True)
90 EdgeDataView([(1, 2, {'weight': 0.3})])
92 >>> viewG.edges(data=True)
93 EdgeDataView([(1, 2, {'weight': 0.3})])
95 We can change the graph type with the `create_using` parameter.
97 >>> type(G)
98 <class 'networkx.classes.graph.Graph'>
99 >>> viewDG = nx.graphviews.generic_graph_view(G, create_using=nx.DiGraph)
100 >>> type(viewDG)
101 <class 'networkx.classes.digraph.DiGraph'>
102 """
103 if create_using is None:
104 newG = G.__class__()
105 else:
106 newG = nx.empty_graph(0, create_using)
107 if G.is_multigraph() != newG.is_multigraph():
108 raise NetworkXError("Multigraph for G must agree with create_using")
109 newG = nx.freeze(newG)
111 # create view by assigning attributes from G
112 newG._graph = G
113 newG.graph = G.graph
115 newG._node = G._node
116 if newG.is_directed():
117 if G.is_directed():
118 newG._succ = G._succ
119 newG._pred = G._pred
120 # newG._adj is synced with _succ
121 else:
122 newG._succ = G._adj
123 newG._pred = G._adj
124 # newG._adj is synced with _succ
125 elif G.is_directed():
126 if G.is_multigraph():
127 newG._adj = UnionMultiAdjacency(G._succ, G._pred)
128 else:
129 newG._adj = UnionAdjacency(G._succ, G._pred)
130 else:
131 newG._adj = G._adj
132 return newG
135@deprecate_positional_args(version="3.4")
136def subgraph_view(G, *, filter_node=no_filter, filter_edge=no_filter):
137 """View of `G` applying a filter on nodes and edges.
139 `subgraph_view` provides a read-only view of the input graph that excludes
140 nodes and edges based on the outcome of two filter functions `filter_node`
141 and `filter_edge`.
143 The `filter_node` function takes one argument --- the node --- and returns
144 `True` if the node should be included in the subgraph, and `False` if it
145 should not be included.
147 The `filter_edge` function takes two (or three arguments if `G` is a
148 multi-graph) --- the nodes describing an edge, plus the edge-key if
149 parallel edges are possible --- and returns `True` if the edge should be
150 included in the subgraph, and `False` if it should not be included.
152 Both node and edge filter functions are called on graph elements as they
153 are queried, meaning there is no up-front cost to creating the view.
155 Parameters
156 ----------
157 G : networkx.Graph
158 A directed/undirected graph/multigraph
160 filter_node : callable, optional
161 A function taking a node as input, which returns `True` if the node
162 should appear in the view.
164 filter_edge : callable, optional
165 A function taking as input the two nodes describing an edge (plus the
166 edge-key if `G` is a multi-graph), which returns `True` if the edge
167 should appear in the view.
169 Returns
170 -------
171 graph : networkx.Graph
172 A read-only graph view of the input graph.
174 Examples
175 --------
176 >>> G = nx.path_graph(6)
178 Filter functions operate on the node, and return `True` if the node should
179 appear in the view:
181 >>> def filter_node(n1):
182 ... return n1 != 5
183 ...
184 >>> view = nx.subgraph_view(G, filter_node=filter_node)
185 >>> view.nodes()
186 NodeView((0, 1, 2, 3, 4))
188 We can use a closure pattern to filter graph elements based on additional
189 data --- for example, filtering on edge data attached to the graph:
191 >>> G[3][4]["cross_me"] = False
192 >>> def filter_edge(n1, n2):
193 ... return G[n1][n2].get("cross_me", True)
194 ...
195 >>> view = nx.subgraph_view(G, filter_edge=filter_edge)
196 >>> view.edges()
197 EdgeView([(0, 1), (1, 2), (2, 3), (4, 5)])
199 >>> view = nx.subgraph_view(G, filter_node=filter_node, filter_edge=filter_edge,)
200 >>> view.nodes()
201 NodeView((0, 1, 2, 3, 4))
202 >>> view.edges()
203 EdgeView([(0, 1), (1, 2), (2, 3)])
204 """
205 newG = nx.freeze(G.__class__())
206 newG._NODE_OK = filter_node
207 newG._EDGE_OK = filter_edge
209 # create view by assigning attributes from G
210 newG._graph = G
211 newG.graph = G.graph
213 newG._node = FilterAtlas(G._node, filter_node)
214 if G.is_multigraph():
215 Adj = FilterMultiAdjacency
217 def reverse_edge(u, v, k=None):
218 return filter_edge(v, u, k)
220 else:
221 Adj = FilterAdjacency
223 def reverse_edge(u, v, k=None):
224 return filter_edge(v, u)
226 if G.is_directed():
227 newG._succ = Adj(G._succ, filter_node, filter_edge)
228 newG._pred = Adj(G._pred, filter_node, reverse_edge)
229 # newG._adj is synced with _succ
230 else:
231 newG._adj = Adj(G._adj, filter_node, filter_edge)
232 return newG
235@not_implemented_for("undirected")
236def reverse_view(G):
237 """View of `G` with edge directions reversed
239 `reverse_view` returns a read-only view of the input graph where
240 edge directions are reversed.
242 Identical to digraph.reverse(copy=False)
244 Parameters
245 ----------
246 G : networkx.DiGraph
248 Returns
249 -------
250 graph : networkx.DiGraph
252 Examples
253 --------
254 >>> G = nx.DiGraph()
255 >>> G.add_edge(1, 2)
256 >>> G.add_edge(2, 3)
257 >>> G.edges()
258 OutEdgeView([(1, 2), (2, 3)])
260 >>> view = nx.reverse_view(G)
261 >>> view.edges()
262 OutEdgeView([(2, 1), (3, 2)])
263 """
264 newG = generic_graph_view(G)
265 newG._succ, newG._pred = G._pred, G._succ
266 # newG._adj is synced with _succ
267 return newG