summaryrefslogtreecommitdiff
path: root/networkx/classes/multidigraph.py
diff options
context:
space:
mode:
authorDan Schult <dschult@colgate.edu>2018-07-20 00:53:31 -0400
committerGitHub <noreply@github.com>2018-07-20 00:53:31 -0400
commite46f4c1cc02feaa08c855ca211ef114acc8fd772 (patch)
tree4a39727be298fff1a2e1dc86906cc1961fd6abc5 /networkx/classes/multidigraph.py
parentcdbf49ee375a836cc16121dcd75334d9fda1cf18 (diff)
downloadnetworkx-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.py189
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)