diff options
| author | Dan Schult <dschult@colgate.edu> | 2018-07-20 00:53:31 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-07-20 00:53:31 -0400 |
| commit | e46f4c1cc02feaa08c855ca211ef114acc8fd772 (patch) | |
| tree | 4a39727be298fff1a2e1dc86906cc1961fd6abc5 /networkx/classes/multidigraph.py | |
| parent | cdbf49ee375a836cc16121dcd75334d9fda1cf18 (diff) | |
| download | networkx-e46f4c1cc02feaa08c855ca211ef114acc8fd772.tar.gz | |
Simplify the Graphview and SubGraphView system (#3073)
* Simplify the Graphview and SubGraphView system
- add tests to show that extensions of base graph classes (only add new functions)
should be able to use the Graph.subgraph and Graph.copy methods easily
- Remove ReadOnlyGraph class in favor of existing freeze() function
- Switch all GraphView classes to generic_graph_view function
- Switch all SubGraph classes to subgraph_view function
- Introduce deprecated functions that act like the deprecated classes.
Still need to:
- add docs
- add tests
- make sure backward compatible and marked as deprecated
- remove GraphView and SubGraph construct from rest of codebase
- update release docs
Fixes #2889
Fixes #2793
Fixes #2796
Fixes #2741
* Ease subclassing for to_(un)directed
- add to_directed_class indicator functions to base classes
- start deprecating G.fresh_copy
- update function.to(un)directed
* Remove G.fresh_copy from code replace with __class__
Add deprecation warnings for GraphView classes, ReverseView and
SubGraph. Also for fresh_copy function.
Diffstat (limited to 'networkx/classes/multidigraph.py')
| -rw-r--r-- | networkx/classes/multidigraph.py | 189 |
1 files changed, 19 insertions, 170 deletions
diff --git a/networkx/classes/multidigraph.py b/networkx/classes/multidigraph.py index 329fef4f..c97869c1 100644 --- a/networkx/classes/multidigraph.py +++ b/networkx/classes/multidigraph.py @@ -233,6 +233,20 @@ class MultiDiGraph(MultiGraph, DiGraph): dict which holds attrbute 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 -------- @@ -752,108 +766,6 @@ class MultiDiGraph(MultiGraph, DiGraph): """Return True if graph is directed, False otherwise.""" return True - def fresh_copy(self): - """Return a fresh copy graph with the same data structure. - - A fresh copy has no nodes, edges or graph attributes. It is - the same data structure as the current graph. This method is - typically used to create an empty version of the graph. - - Notes - ----- - If you subclass the base class you should overwrite this method - to return your class of graph. - """ - return MultiDiGraph() - - def copy(self, as_view=False): - """Return a copy of the graph. - - The copy method by default returns an independent shallow copy - of the graph and attributes. That is, if an attribute is a - container, that container is shared by the original an the copy. - Use Python's `copy.deepcopy` for new containers. - - If `as_view` is True then a view is returned instead of a copy. - - Notes - ----- - All copies reproduce the graph structure, but data attributes - may be handled in different ways. There are four types of copies - of a graph that people might want. - - Deepcopy -- A "deepcopy" copies the graph structure as well as - all data attributes and any objects they might contain. - The entire graph object is new so that changes in the copy - do not affect the original object. (see Python's copy.deepcopy) - - Data Reference (Shallow) -- For a shallow copy the graph structure - is copied but the edge, node and graph attribute dicts are - references to those in the original graph. This saves - time and memory but could cause confusion if you change an attribute - in one graph and it changes the attribute in the other. - NetworkX does not provide this level of shallow copy. - - Independent Shallow -- This copy creates new independent attribute - dicts and then does a shallow copy of the attributes. That is, any - attributes that are containers are shared between the new graph - and the original. This is exactly what `dict.copy()` provides. - You can obtain this style copy using: - - >>> G = nx.path_graph(5) - >>> H = G.copy() - >>> H = G.copy(as_view=False) - >>> H = nx.Graph(G) - >>> H = G.fresh_copy().__class__(G) - - Fresh Data -- For fresh data, the graph structure is copied while - new empty data attribute dicts are created. The resulting graph - is independent of the original and it has no edge, node or graph - attributes. Fresh copies are not enabled. Instead use: - - >>> H = G.fresh_copy() - >>> H.add_nodes_from(G) - >>> H.add_edges_from(G.edges) - - View -- Inspired by dict-views, graph-views act like read-only - versions of the original graph, providing a copy of the original - structure without requiring any memory for copying the information. - - See the Python copy module for more information on shallow - and deep copies, https://docs.python.org/2/library/copy.html. - - Parameters - ---------- - as_view : bool, optional (default=False) - If True, the returned graph-view provides a read-only view - of the original graph without actually copying any data. - - Returns - ------- - G : Graph - A copy of the graph. - - See Also - -------- - to_directed: return a directed copy of the graph. - - Examples - -------- - >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc - >>> H = G.copy() - - """ - if as_view is True: - return nx.graphviews.MultiDiGraphView(self) - G = self.fresh_copy() - G.graph.update(self.graph) - G.add_nodes_from((n, d.copy()) for n, d in self._node.items()) - G.add_edges_from((u, v, key, datadict.copy()) - for u, nbrs in self._adj.items() - for v, keydict in nbrs.items() - for key, datadict in keydict.items()) - return G - def to_undirected(self, reciprocal=False, as_view=False): """Return an undirected representation of the digraph. @@ -905,10 +817,11 @@ class MultiDiGraph(MultiGraph, DiGraph): >>> list(G2.edges) [(0, 1)] """ + graph_class = self.to_undirected_class() if as_view is True: - return nx.graphviews.MultiGraphView(self) + return nx.graphviews.generic_graph_view(self, graph_class) # deepcopy when not a view - G = MultiGraph() + 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: @@ -924,70 +837,6 @@ class MultiDiGraph(MultiGraph, DiGraph): for key, data in keydict.items()) return G - def subgraph(self, nodes): - """Return a SubGraph view of the subgraph induced on nodes in `nodes`. - - The induced subgraph of the graph contains the nodes in `nodes` - and the edges between those nodes. - - Parameters - ---------- - nodes : list, iterable - A container of nodes which will be iterated through once. - - Returns - ------- - G : SubGraph View - A subgraph view of the graph. The graph structure cannot be - changed but node/edge attributes can and are shared with the - original graph. - - Notes - ----- - The graph, edge and node attributes are shared with the original graph. - Changes to the graph structure is ruled out by the view, but changes - to attributes are reflected in the original graph. - - To create a subgraph with its own copy of the edge/node attributes use: - G.subgraph(nodes).copy() - - For an inplace reduction of a graph to a subgraph you can remove nodes: - G.remove_nodes_from([n for n in G if n not in set(nodes)]) - - Subgraph views are sometimes NOT what you want. In most cases where - you want to do more than simply look at the induced edges, it makes - more sense to just create the subgraph as its own graph with code like: - - :: - - # Create a subgraph SG based on a (possibly multigraph) G - SG = G.fresh_copy() - SG.add_nodes_from((n, G.nodes[n]) for n in largest_wcc) - if SG.is_multigraph: - SG.add_edges_from((n, nbr, key, d) - for n, nbrs in G.adj.items() if n in largest_wcc - for nbr, keydict in nbrs.items() if nbr in largest_wcc - for key, d in keydict.items()) - else: - SG.add_edges_from((n, nbr, d) - for n, nbrs in G.adj.items() if n in largest_wcc - for nbr, d in nbrs.items() if nbr in largest_wcc) - SG.graph.update(G.graph) - - Examples - -------- - >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc - >>> H = G.subgraph([0, 1, 2]) - >>> list(H.edges) - [(0, 1), (1, 2)] - """ - induced_nodes = nx.filters.show_nodes(self.nbunch_iter(nodes)) - SubGraph = nx.graphviews.SubMultiDiGraph - # if already a subgraph, don't make a chain - if hasattr(self, '_NODE_OK'): - return SubGraph(self._graph, induced_nodes, self._EDGE_OK) - return SubGraph(self, induced_nodes) - def reverse(self, copy=True): """Return the reverse of the graph. @@ -1002,10 +851,10 @@ class MultiDiGraph(MultiGraph, DiGraph): the original graph. """ if copy: - H = self.fresh_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.MultiReverseView(self) + return nx.graphviews.reverse_view(self) |
