Repository: networkx/networkx

Issue:
fixing a subset of nodes in spring_layout
The following layout works reasonably well,
```
import networkx as nx

G = nx.Graph()
G.add_edges_from([(0,1),(1,2),(2,0),(2,3)])
pos = nx.spring_layout(G)
print(pos)
```
However, the results from run to run are not easily reproduced because the initialization is random.  

To mitigate this we can fix node 0 to (0,0), as follows,
```
init_pos = {0:(0.0, 0.0)}
fixed_pos = [0]
pos = nx.spring_layout(G, pos=init_pos, fixed=fixed_pos)
print(pos)
```
This results in  the following error,
```
networkx/drawing/layout.py:342: RuntimeWarning: divide by zero encountered
```
A work around seems to be to provide initial points to all positions as follows,
```
import random
init_pos = {n:(random.uniform(-1.0, 1.0), random.uniform(-1.0, 1.0)) for n in G}
init_pos[0] = (0.0, 0.0)
fixed_pos = [0]
pos = nx.spring_layout(G, pos=init_pos, fixed=fixed_pos)
print(pos)
```

Discussion:

Patch:
diff --git a/doc/release/migration_guide_from_1.x_to_2.0.rst b/doc/release/migration_guide_from_1.x_to_2.0.rst
index f8e82f97c..7b96b8240 100644
--- a/doc/release/migration_guide_from_1.x_to_2.0.rst
+++ b/doc/release/migration_guide_from_1.x_to_2.0.rst
@@ -108,3 +108,28 @@ Let's have a look on them.
 	[1]
 
 The same changes apply to MultiGraphs and MultiDiGraphs.
+
+ 
+-------
+
+The order of arguments to ``set_edge_attributes`` and ``set_node_attributes`` has
+changed.  The position of ``name`` and ``values`` has been swapped, and ``name`` now
+defaults to ``None``.  The previous call signature of ``(graph, name, value)`` has
+been changed to ``(graph, value, name=None)``. The new style allows for ``name`` to
+be ommitted in favor of passing a dictionary of dictionaries to ``values``.
+
+A simple method for migrating existing code to the new version is to explicitly
+specify the keyword argument names. This method is backwards compatible and
+ensures the correct arguments are passed, regardless of the order. For example the old code
+
+    >>> G = nx.Graph([(1, 2), (1, 3)])
+    >>> nx.set_node_attributes(G, 'label', {1: 'one', 2: 'two', 3: 'three'})
+    >>> nx.set_edge_attributes(G, 'label', {(1, 2): 'path1', (2, 3): 'path2'})
+
+Will cause ``TypeError: unhashable type: 'dict'`` in the new version. The code
+can be refactored as
+
+    >>> G = nx.Graph([(1, 2), (1, 3)])
+    >>> nx.set_node_attributes(G, name='label', values={1: 'one', 2: 'two', 3: 'three'})
+    >>> nx.set_edge_attributes(G, name='label', values={(1, 2): 'path1', (2, 3): 'path2'})
+    
diff --git a/networkx/algorithms/approximation/kcomponents.py b/networkx/algorithms/approximation/kcomponents.py
index 7119b2891..72b06eff8 100644
--- a/networkx/algorithms/approximation/kcomponents.py
+++ b/networkx/algorithms/approximation/kcomponents.py
@@ -286,7 +286,7 @@ class _AntiGraph(nx.Graph):
     def adj(self):
         return self.AntiAdjacencyView(self)
 
-    class AntiDegreeView(nx.DegreeView):
+    class AntiDegreeView(nx.reportviews.DegreeView):
         def __iter__(self):
             all_nodes = set(self._succ)
             for n in self._nodes:
diff --git a/networkx/classes/__init__.py b/networkx/classes/__init__.py
index 29d4a3293..08dc41aa4 100644
--- a/networkx/classes/__init__.py
+++ b/networkx/classes/__init__.py
@@ -2,8 +2,10 @@ from .graph import Graph
 from .digraph import DiGraph
 from .multigraph import MultiGraph
 from .multidigraph import MultiDiGraph
-from .views import *
-from .subgraph import *
 from .ordered import *
 
 from .function import *
+
+import networkx.classes.coreviews
+import networkx.classes.graphviews
+import networkx.classes.reportviews
diff --git a/networkx/classes/coreviews.py b/networkx/classes/coreviews.py
new file mode 100644
index 000000000..3a3fa5f7f
--- /dev/null
+++ b/networkx/classes/coreviews.py
@@ -0,0 +1,351 @@
+#    Copyright (C) 2004-2017 by
+#    Aric Hagberg <hagberg@lanl.gov>
+#    Dan Schult <dschult@colgate.edu>
+#    Pieter Swart <swart@lanl.gov>
+#    All rights reserved.
+#    BSD license.
+#
+# Authors: Aric Hagberg (hagberg@lanl.gov),
+#          Pieter Swart (swart@lanl.gov),
+#          Dan Schult(dschult@colgate.edu)
+"""
+"""
+from itertools import chain
+from collections import Mapping, Set, Iterable
+import networkx as nx
+
+__all__ = ['AtlasView', 'AdjacencyView', 'MultiAdjacencyView',
+           'UnionAtlas', 'UnionAdjacency',
+           'UnionMultiInner', 'UnionMultiAdjacency',
+           'FilterAtlas', 'FilterAdjacency',
+           'FilterMultiInner', 'FilterMultiAdjacency',
+           'ReadOnlyGraph',
+           ]
+
+
+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 __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 __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, self._atlas)
+
+
+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 __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 __repr__(self):
+        return '%s(%r, %r)' % (self.__class__.__name__, self._succ, self._pred)
+
+
+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 __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 __repr__(self):
+        return '%s(%r, %r)' % (self.__class__.__name__, self._succ, self._pred)
+
+
+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 ReadOnlyGraph(object):
+    """A Mixin Class to mask the write methods of a graph class."""
+
+    def not_allowed(self, *args, **kwds):
+        msg = "SubGraph Views are readonly. Mutations not allowed"
+        raise nx.NetworkXError(msg)
+
+    add_node = not_allowed
+    remove_node = not_allowed
+    add_nodes_from = not_allowed
+    remove_nodes_from = not_allowed
+
+    add_edge = not_allowed
+    remove_edge = not_allowed
+    add_edges_from = not_allowed
+    add_weighted_edges_from = not_allowed
+    remove_edges_from = not_allowed
+
+    clear = not_allowed
+
+
+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._atlas if self.NODE_OK(n))
+
+    def __iter__(self):
+        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("Key {} not found".format(key))
+
+    def copy(self):
+        return {u: d for u, d in self._atlas.items()
+                if self.NODE_OK(u)}
+
+    def __repr__(self):
+        return '%s(%r, %r)' % (self.__class__.__name__, self._atlas,
+                               self.NODE_OK)
+
+
+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._atlas if self.NODE_OK(n))
+
+    def __iter__(self):
+        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("Key {} not found".format(node))
+
+    def copy(self):
+        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 __repr__(self):
+        return '%s(%r, %r, %r)' % (self.__class__.__name__, self._atlas,
+                                   self.NODE_OK, self.EDGE_OK)
+
+
+class FilterMultiInner(FilterAdjacency):  # muliedge_seconddict
+    def __len__(self):
+        return sum(1 for n in self)
+
+    def __iter__(self):
+        for n in self._atlas:
+            if self.NODE_OK(n):
+                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("Key {} not found".format(nbr))
+
+    def copy(self):
+        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("Key {} not found".format(node))
+
+    def copy(self):
+        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)}
+
+
diff --git a/networkx/classes/digraph.py b/networkx/classes/digraph.py
index 39e58e207..8ca946690 100644
--- a/networkx/classes/digraph.py
+++ b/networkx/classes/digraph.py
@@ -13,7 +13,8 @@ from copy import deepcopy
 
 import networkx as nx
 from networkx.classes.graph import Graph
-from networkx.classes.views import AtlasView2, OutEdgeView, InEdgeView, \
+from networkx.classes.coreviews import AdjacencyView
+from networkx.classes.reportviews import OutEdgeView, InEdgeView, \
         DiDegreeView, InDegreeView, OutDegreeView
 from networkx.exception import NetworkXError
 import networkx.convert as convert
@@ -294,15 +295,15 @@ class DiGraph(Graph):
 
     @property
     def adj(self):
-        return AtlasView2(self._succ)
+        return AdjacencyView(self._succ)
 
     @property
     def succ(self):
-        return AtlasView2(self._succ)
+        return AdjacencyView(self._succ)
 
     @property
     def pred(self):
-        return AtlasView2(self._pred)
+        return AdjacencyView(self._pred)
 
     def add_node(self, n, **attr):
         """Add a single node n and update node attributes.
diff --git a/networkx/classes/function.py b/networkx/classes/function.py
index a71a54ef9..c2c5fe34f 100644
--- a/networkx/classes/function.py
+++ b/networkx/classes/function.py
@@ -25,6 +25,8 @@ from networkx.utils import pairwise, not_implemented_for
 __all__ = ['nodes', 'edges', 'degree', 'degree_histogram', 'neighbors',
            'number_of_nodes', 'number_of_edges', 'density',
            'is_directed', 'info', 'freeze', 'is_frozen', 'subgraph',
+           'induced_subgraph', 'edge_subgraph', 'reverse_view',
+           'to_directed', 'to_undirected',
            'add_star', 'add_path', 'add_cycle',
            'create_empty_copy', 'set_node_attributes',
            'get_node_attributes', 'set_edge_attributes',
@@ -304,6 +306,127 @@ def subgraph(G, nbunch):
     return G.subgraph(nbunch)
 
 
+def induced_subgraph(G, nbunch):
+    """Return a SubGraph view of `G` showing only nodes in nbunch.
+
+    The induced subgraph of a graph on a set of nodes N is the
+    graph with nodes N and edges from G which have both ends in N.
+
+    Parameters
+    ----------
+    G : NetworkX Graph
+    nbunch : node, container of nodes or None (for all nodes)
+
+    Returns
+    -------
+    subgraph : SubGraph View
+        A read-only view of the subgraph in `G` induced by the nodes.
+        Changes to the graph `G` will be reflected in the view.
+
+    Notes
+    -----
+    To create a mutable subgraph with its own copies of nodes
+    edges and attributes use `subgraph.copy()` or `Graph(subgraph)`
+
+    For an inplace reduction of a graph to a subgraph you can remove nodes:
+    `G.remove_nodes_from(n in G if n not in set(nbunch))`
+
+    Examples
+    --------
+    >>> import networkx as nx
+    >>> 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(G.nbunch_iter(nbunch))
+    if G.is_multigraph():
+        if G.is_directed():
+            return nx.graphviews.MultiDiSubGraph(G, induced_nodes)
+        return nx.graphviews.MultiSubGraph(G, induced_nodes)
+    if G.is_directed():
+        return nx.graphviews.DiSubGraph(G, induced_nodes)
+    return nx.graphviews.SubGraph(G, induced_nodes)
+
+
+def edge_subgraph(G, edges):
+    """Returns a view of the subgraph induced by the specified edges.
+
+    The induced subgraph contains each edge in `edges` and each
+    node incident to any of those edges.
+
+    Parameters
+    ----------
+    G : NetworkX Graph
+    edges : iterable
+        An iterable of edges. Edges not present in `G` are ignored.
+
+    Returns
+    -------
+    subgraph : SubGraph View
+        A read-only edge-induced subgraph of `G`.
+        Changes to `G` are reflected in the view.
+
+    Notes
+    -----
+    To create a mutable subgraph with its own copies of nodes
+    edges and attributes use `subgraph.copy()` or `Graph(subgraph)`
+
+    Examples
+    --------
+    >>> import networkx as nx
+    >>> G = nx.path_graph(5)
+    >>> H = G.edge_subgraph([(0, 1), (3, 4)])
+    >>> list(H.nodes)
+    [0, 1, 3, 4]
+    >>> list(H.edges)
+    [(0, 1), (3, 4)]
+    """
+    edges = set(edges)
+    nodes = set()
+    for e in edges:
+        nodes.update(e[:2])
+    induced_nodes = nx.classes.filters.show_nodes(nodes)
+    if G.is_multigraph():
+        if G.is_directed():
+            induced_edges = nx.classes.filters.show_multidiedges(edges)
+            return nx.graphviews.MultiDiSubGraph(G, induced_nodes, induced_edges)
+        induced_edges = nx.classes.filters.show_multiedges(edges)
+        return nx.graphviews.MultiSubGraph(G, induced_nodes, induced_edges)
+    if G.is_directed():
+        induced_edges = nx.classes.filters.show_diedges(edges)
+        return nx.graphviews.DiSubGraph(G, induced_nodes, induced_edges)
+    induced_edges = nx.classes.filters.show_edges(edges)
+    return nx.graphviews.SubGraph(G, induced_nodes, induced_edges)
+
+
+@not_implemented_for('undirected')
+def reverse_view(digraph):
+    if digraph.is_multigraph():
+        return nx.graphviews.MultiReverseView(digraph)
+    return nx.graphviews.ReverseView(digraph)
+
+
+def to_directed(graph):
+    if graph.is_directed():
+        if graph.is_multigraph():
+            return induced_subgraph(graph, graph)
+        return induced_subgraph(graph, graph)
+    if graph.is_multigraph():
+        return nx.graphviews.MultiDirectedView(graph)
+    return nx.graphviews.DirectedView(graph)
+
+
+def to_undirected(digraph):
+    if digraph.is_directed():
+        if digraph.is_multigraph():
+            return nx.graphviews.MultiUnDirectedView(digraph)
+        return nx.graphviews.UnDirectedView(digraph)
+    if digraph.is_multigraph():
+        return induced_subgraph(digraph, digraph)
+    return induced_subgraph(digraph, digraph)
+
+
 def create_empty_copy(G, with_data=True):
     """Return a copy of the graph G with all of the edges removed.
 
diff --git a/networkx/classes/graph.py b/networkx/classes/graph.py
index 6fa345a42..a4885cac5 100644
--- a/networkx/classes/graph.py
+++ b/networkx/classes/graph.py
@@ -22,8 +22,8 @@ from copy import deepcopy
 from collections import Mapping
 
 import networkx as nx
-from networkx.classes.views import AtlasView, AtlasView2
-from networkx.classes.views import NodeView, EdgeView, DegreeView
+from networkx.classes.coreviews import AtlasView, AdjacencyView
+from networkx.classes.reportviews import NodeView, EdgeView, DegreeView
 from networkx.exception import NetworkXError
 import networkx.convert as convert
 from networkx.utils import pairwise
@@ -310,7 +310,7 @@ class Graph(object):
 
     @property
     def adj(self):
-        return AtlasView2(self._adj)
+        return AdjacencyView(self._adj)
 
     @property
     def name(self):
diff --git a/networkx/classes/graphviews.py b/networkx/classes/graphviews.py
new file mode 100644
index 000000000..5954b8e31
--- /dev/null
+++ b/networkx/classes/graphviews.py
@@ -0,0 +1,162 @@
+#    Copyright (C) 2004-2017 by
+#    Aric Hagberg <hagberg@lanl.gov>
+#    Dan Schult <dschult@colgate.edu>
+#    Pieter Swart <swart@lanl.gov>
+#    All rights reserved.
+#    BSD license.
+#
+# Author:  Aric Hagberg (hagberg@lanl.gov),
+#          Pieter Swart (swart@lanl.gov),
+#          Dan Schult(dschult@colgate.edu)
+"""View of Graph with restricted nodes and edges.
+
+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.
+"""
+"""Views of Graph for reverse, to_directed, to_undirected.
+
+In some 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.
+"""
+from collections import Mapping
+
+from networkx.classes import Graph, DiGraph, MultiGraph, MultiDiGraph
+from networkx.classes.coreviews import ReadOnlyGraph, \
+                         AtlasView, AdjacencyView, MultiAdjacencyView, \
+                         FilterAtlas, FilterAdjacency, FilterMultiAdjacency, \
+                         UnionAdjacency, UnionMultiAdjacency
+from networkx.classes.filters import no_filter, show_nodes, show_edges
+from networkx.exception import NetworkXError, NetworkXNotImplemented
+from networkx.utils import not_implemented_for
+
+
+__all__ = ['SubGraph', 'DiSubGraph', 'MultiSubGraph', 'MultiDiSubGraph',
+           'ReverseView', 'MultiReverseView',
+           'DirectedView', 'MultiDirectedView',
+           'UnDirectedView', 'MultiUnDirectedView',
+           ]
+
+
+class SubGraph(ReadOnlyGraph, Graph):
+    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
+        self._graph = graph
+        self._NODE_OK = filter_node
+        self._EDGE_OK = filter_edge
+
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = FilterAtlas(graph._node, filter_node)
+        self._adj = FilterAdjacency(graph._adj, filter_node, filter_edge)
+
+
+class DiSubGraph(ReadOnlyGraph, DiGraph):
+    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
+        self._graph = graph
+        self._NODE_OK = filter_node
+        self._EDGE_OK = filter_edge
+
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = FilterAtlas(graph._node, filter_node)
+        self._adj = FilterAdjacency(graph._adj, filter_node, filter_edge)
+        self._pred = FilterAdjacency(graph._pred, filter_node,
+                                     lambda u, v: filter_edge(v, u))
+        self._succ = self._adj
+
+
+class MultiSubGraph(ReadOnlyGraph, MultiGraph):
+    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
+        self._graph = graph
+        self._NODE_OK = filter_node
+        self._EDGE_OK = filter_edge
+
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = FilterAtlas(graph._node, filter_node)
+        self._adj = FilterMultiAdjacency(graph._adj, filter_node, filter_edge)
+
+
+class MultiDiSubGraph(ReadOnlyGraph, MultiDiGraph):
+    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
+        self._graph = graph
+        self._NODE_OK = filter_node
+        self._EDGE_OK = filter_edge
+
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = FilterAtlas(graph._node, filter_node)
+        FMA = FilterMultiAdjacency
+        self._adj = FMA(graph._adj, filter_node, filter_edge)
+        self._pred = FMA(graph._pred, filter_node,
+                         lambda u, v, k: filter_edge(v, u, k))
+        self._succ = self._adj
+
+
+class ReverseView(ReadOnlyGraph, DiGraph):
+    def __init__(self, graph):
+        if not graph.is_directed():
+            msg = "not implemented for undirected type"
+            raise NetworkXNotImplemented(msg)
+
+        self._graph = graph
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = graph._node
+        self._adj = graph._pred
+        self._pred = graph._succ
+        self._succ = self._adj
+
+
+class MultiReverseView(ReadOnlyGraph, MultiDiGraph):
+    def __init__(self, graph):
+        if not graph.is_directed():
+            msg = "not implemented for undirected type"
+            raise NetworkXNotImplemented(msg)
+
+        self._graph = graph
+        # Set graph interface
+        self.graph = graph.graph
+        self._node = graph._node
+        self._adj = graph._pred
+        self._pred = graph._succ
+        self._succ = self._adj
+
+
+class DirectedView(ReadOnlyGraph, DiGraph):
+    def __init__(self, graph):
+        self._graph = graph
+        self.graph = graph.graph
+        self._node = graph._node
+        self._pred = graph._adj
+        self._succ = graph._adj
+        self._adj = self._succ
+
+
+class MultiDirectedView(ReadOnlyGraph, MultiDiGraph):
+    def __init__(self, graph):
+        self._graph = graph
+        self.graph = graph.graph
+        self._node = graph._node
+        self._pred = graph._adj
+        self._succ = graph._adj
+        self._adj = self._succ
+
+
+class UnDirectedView(ReadOnlyGraph, Graph):
+    UnionAdj = UnionAdjacency
+    def __init__(self, digraph):
+        self._graph = digraph
+        self.graph = digraph.graph
+        self._node = digraph._node
+        self._adj = self.UnionAdj(digraph._succ, digraph._pred)
+
+
+class MultiUnDirectedView(ReadOnlyGraph, MultiGraph):
+    UnionAdj = UnionMultiAdjacency
+    def __init__(self, digraph):
+        self._graph = digraph
+        self.graph = digraph.graph
+        self._node = digraph._node
+        self._adj = self.UnionAdj(digraph._succ, digraph._pred)
diff --git a/networkx/classes/multidigraph.py b/networkx/classes/multidigraph.py
index ef3031ba6..a97341faf 100644
--- a/networkx/classes/multidigraph.py
+++ b/networkx/classes/multidigraph.py
@@ -15,8 +15,8 @@ import networkx as nx
 from networkx.classes.graph import Graph  # for doctests
 from networkx.classes.digraph import DiGraph
 from networkx.classes.multigraph import MultiGraph
-from networkx.classes.views import AtlasView3
-from networkx.classes.views import OutMultiEdgeView, InMultiEdgeView, \
+from networkx.classes.coreviews import MultiAdjacencyView
+from networkx.classes.reportviews import OutMultiEdgeView, InMultiEdgeView, \
         DiMultiDegreeView, OutMultiDegreeView, InMultiDegreeView
 from networkx.exception import NetworkXError
 
@@ -106,7 +106,7 @@ class MultiDiGraph(MultiGraph, DiGraph):
 
     >>> keys = G.add_edges_from([(4,5,dict(route=282)), (4,5,dict(route=37))])
     >>> G[4]
-    AtlasView2({5: {0: {}, 1: {'route': 282}, 2: {'route': 37}}})
+    AdjacencyView({5: {0: {}, 1: {'route': 282}, 2: {'route': 37}}})
 
     **Attributes:**
 
@@ -153,7 +153,7 @@ class MultiDiGraph(MultiGraph, DiGraph):
     >>> len(G)  # number of nodes in graph
     5
     >>> G[1] # adjacency dict-like view keyed by neighbor to edge attributes
-    AtlasView2({2: {0: {'weight': 4}, 1: {'color': 'blue'}}})
+    AdjacencyView({2: {0: {'weight': 4}, 1: {'color': 'blue'}}})
 
     The fastest way to traverse all edges of a graph is via
     adjacency():
@@ -248,15 +248,15 @@ class MultiDiGraph(MultiGraph, DiGraph):
 
     @property
     def adj(self):
-        return AtlasView3(self._succ)
+        return MultiAdjacencyView(self._succ)
 
     @property
     def succ(self):
-        return AtlasView3(self._succ)
+        return MultiAdjacencyView(self._succ)
 
     @property
     def pred(self):
-        return AtlasView3(self._pred)
+        return MultiAdjacencyView(self._pred)
 
     def add_edge(self, u, v, key=None, **attr):
         """Add an edge between u and v.
diff --git a/networkx/classes/multigraph.py b/networkx/classes/multigraph.py
index 551c1182b..1cddd40fd 100644
--- a/networkx/classes/multigraph.py
+++ b/networkx/classes/multigraph.py
@@ -13,8 +13,8 @@ from copy import deepcopy
 
 import networkx as nx
 from networkx.classes.graph import Graph
-from networkx.classes.views import AtlasView3
-from networkx.classes.views import MultiEdgeView, MultiDegreeView
+from networkx.classes.coreviews import MultiAdjacencyView
+from networkx.classes.reportviews import MultiEdgeView, MultiDegreeView
 from networkx import NetworkXError
 from networkx.utils import iterable
 
@@ -103,9 +103,9 @@ class MultiGraph(Graph):
     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,{'route':282}), (4,5,{'route':37})])
+    >>> keys = G.add_edges_from([(4,5,{'route':28}), (4,5,{'route':37})])
     >>> G[4]
-    AtlasView2({3: {0: {}}, 5: {0: {}, 1: {'route': 282}, 2: {'route': 37}}})
+    AdjacencyView({3: {0: {}}, 5: {0: {}, 1: {'route': 28}, 2: {'route': 37}}})
 
     **Attributes:**
 
@@ -152,7 +152,7 @@ class MultiGraph(Graph):
     >>> len(G)  # number of nodes in graph
     5
     >>> G[1] # adjacency dict-like view keyed by neighbor to edge attributes
-    AtlasView2({2: {0: {'weight': 4}, 1: {'color': 'blue'}}})
+    AdjacencyView({2: {0: {'weight': 4}, 1: {'color': 'blue'}}})
 
     The fastest way to traverse all edges of a graph is via
     adjacency():
@@ -247,7 +247,7 @@ class MultiGraph(Graph):
 
     @property
     def adj(self):
-        return AtlasView3(self._adj)
+        return MultiAdjacencyView(self._adj)
 
     def new_edge_key(self, u, v):
         """Return an unused key for edges between nodes `u` and `v`.
diff --git a/networkx/classes/views.py b/networkx/classes/reportviews.py
similarity index 95%
rename from networkx/classes/views.py
rename to networkx/classes/reportviews.py
index efe669b76..e0c3ce8aa 100644
--- a/networkx/classes/views.py
+++ b/networkx/classes/reportviews.py
@@ -95,8 +95,7 @@ EdgeDataView
 from collections import Mapping, Set, Iterable
 import networkx as nx
 
-__all__ = ['AtlasView', 'AtlasView2', 'AtlasView3',
-           'NodeView', 'NodeDataView',
+__all__ = ['NodeView', 'NodeDataView',
            'EdgeView', 'OutEdgeView', 'InEdgeView',
            'EdgeDataView', 'OutEdgeDataView', 'InEdgeDataView',
            'MultiEdgeView', 'OutMultiEdgeView', 'InMultiEdgeView',
@@ -1110,72 +1109,3 @@ class InMultiEdgeView(OutMultiEdgeView):
     def __getitem__(self, e):
         u, v, k = e
         return self._adjdict[v][u][k]
-
-
-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
-    ========
-    AtlasView2 - View into dict-of-dict-of-dict
-    AtlasView3 - View into dict-of-dict-of-dict-of-dict
-    """
-    __slots__ = ('_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 self._atlas.copy()
-
-    def __repr__(self):
-        return '%s(%r)' % (self.__class__.__name__, self._atlas)
-
-
-class AtlasView2(AtlasView):
-    """An AtlasView2 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
-    AtlasView3 - 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])
-
-
-class AtlasView3(AtlasView2):
-    """An AtlasView3 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
-    AtlasView2 - View into dict-of-dict-of-dict
-    """
-    __slots__ = ()   # Still uses AtlasView slots names _atlas
-
-    def __getitem__(self, name):
-        return AtlasView2(self._atlas[name])
diff --git a/networkx/classes/subgraph.py b/networkx/classes/subgraph.py
deleted file mode 100644
index 6407ff1e6..000000000
--- a/networkx/classes/subgraph.py
+++ /dev/null
@@ -1,334 +0,0 @@
-#    Copyright (C) 2004-2017 by
-#    Aric Hagberg <hagberg@lanl.gov>
-#    Dan Schult <dschult@colgate.edu>
-#    Pieter Swart <swart@lanl.gov>
-#    All rights reserved.
-#    BSD license.
-#
-# Author:  Aric Hagberg (hagberg@lanl.gov),
-#          Pieter Swart (swart@lanl.gov),
-#          Dan Schult(dschult@colgate.edu)
-"""View of Graph with restricted nodes and edges.
-
-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.
-"""
-from collections import Mapping
-
-import networkx as nx
-from networkx.classes import Graph, DiGraph, MultiGraph, MultiDiGraph
-from networkx.classes.filters import no_filter, show_nodes, show_edges
-from networkx.exception import NetworkXError
-
-__all__ = ['SubGraph', 'DiSubGraph', 'MultiSubGraph', 'MultiDiSubGraph',
-           'induced_subgraph', 'edge_subgraph']
-
-
-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._atlas if self.NODE_OK(n))
-
-    def __iter__(self):
-        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("Key {} not found".format(key))
-
-    def copy(self):
-        return {u: d for u, d in self._atlas.items()
-                if self.NODE_OK(u)}
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (self.__class__.__name__, self._atlas,
-                               self.NODE_OK)
-
-
-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._atlas if self.NODE_OK(n))
-
-    def __iter__(self):
-        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("Key {} not found".format(node))
-
-    def copy(self):
-        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 __repr__(self):
-        return '%s(%r, %r, %r)' % (self.__class__.__name__, self._atlas,
-                                   self.NODE_OK, self.EDGE_OK)
-
-
-class FilterMultiInner(FilterAdjacency):  # muliedge_seconddict
-    def __len__(self):
-        return sum(1 for n in self)
-
-    def __iter__(self):
-        for n in self._atlas:
-            if self.NODE_OK(n):
-                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("Key {} not found".format(nbr))
-
-    def copy(self):
-        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("Key {} not found".format(node))
-
-    def copy(self):
-        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)}
-
-
-class SubGraph(Graph):
-    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
-        self._graph = graph
-        self._NODE_OK = filter_node
-        self._EDGE_OK = filter_edge
-
-        # Set graph interface
-        self.graph = graph.graph
-        self._node = FilterAtlas(graph._node, filter_node)
-        self._adj = FilterAdjacency(graph._adj, filter_node, filter_edge)
-
-    def not_allowed(self, *args, **kwds):
-        msg = "SubGraph Views are readonly. Mutations not allowed"
-        raise NetworkXError(msg)
-
-    add_node = not_allowed
-    remove_node = not_allowed
-    add_nodes_from = not_allowed
-    remove_nodes_from = not_allowed
-
-    add_edge = not_allowed
-    remove_edge = not_allowed
-    add_edges_from = not_allowed
-    add_weighted_edges_from = not_allowed
-    remove_edges_from = not_allowed
-
-    clear = not_allowed
-
-
-class DiSubGraph(DiGraph):
-    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
-        self._graph = graph
-        self._NODE_OK = filter_node
-        self._EDGE_OK = filter_edge
-
-        # Set graph interface
-        self.graph = graph.graph
-        self._node = FilterAtlas(graph._node, filter_node)
-        self._adj = FilterAdjacency(graph._adj, filter_node, filter_edge)
-        self._pred = FilterAdjacency(graph._pred, filter_node,
-                                     lambda u, v: filter_edge(v, u))
-        self._succ = self._adj
-
-    def not_allowed(self, *args, **kwds):
-        msg = "SubGraph Views are readonly. Mutations not allowed"
-        raise NetworkXError(msg)
-
-    add_node = not_allowed
-    remove_node = not_allowed
-    add_nodes_from = not_allowed
-    remove_nodes_from = not_allowed
-
-    add_edge = not_allowed
-    remove_edge = not_allowed
-    add_edges_from = not_allowed
-    add_weighted_edges_from = not_allowed
-    remove_edges_from = not_allowed
-
-    clear = not_allowed
-
-
-class MultiSubGraph(MultiGraph):
-    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
-        self._graph = graph
-        self._NODE_OK = filter_node
-        self._EDGE_OK = filter_edge
-
-        # Set graph interface
-        self.graph = graph.graph
-        self._node = FilterAtlas(graph._node, filter_node)
-        self._adj = FilterMultiAdjacency(graph._adj, filter_node, filter_edge)
-
-    def not_allowed(self, *args, **kwds):
-        msg = "SubGraph Views are readonly. Mutations not allowed"
-        raise NetworkXError(msg)
-
-    add_node = not_allowed
-    remove_node = not_allowed
-    add_nodes_from = not_allowed
-    remove_nodes_from = not_allowed
-
-    add_edge = not_allowed
-    remove_edge = not_allowed
-    add_edges_from = not_allowed
-    add_weighted_edges_from = not_allowed
-    remove_edges_from = not_allowed
-
-    clear = not_allowed
-
-
-class MultiDiSubGraph(MultiDiGraph):
-    def __init__(self, graph, filter_node=no_filter, filter_edge=no_filter):
-        self._graph = graph
-        self._NODE_OK = filter_node
-        self._EDGE_OK = filter_edge
-
-        # Set graph interface
-        self.graph = graph.graph
-        self._node = FilterAtlas(graph._node, filter_node)
-        FMA = FilterMultiAdjacency
-        self._adj = FMA(graph._adj, filter_node, filter_edge)
-        self._pred = FMA(graph._pred, filter_node,
-                         lambda u, v, k: filter_edge(v, u, k))
-        self._succ = self._adj
-
-    def not_allowed(self, *args, **kwds):
-        msg = "SubGraph Views are readonly. Mutations not allowed"
-        raise NetworkXError(msg)
-
-    add_node = not_allowed
-    remove_node = not_allowed
-    add_nodes_from = not_allowed
-    remove_nodes_from = not_allowed
-
-    add_edge = not_allowed
-    remove_edge = not_allowed
-    add_edges_from = not_allowed
-    add_weighted_edges_from = not_allowed
-    remove_edges_from = not_allowed
-
-    clear = not_allowed
-
-
-def induced_subgraph(G, nbunch):
-    """Return a SubGraph view of `G` showing only nodes in nbunch.
-
-    The induced subgraph of a graph on a set of nodes N is the
-    graph with nodes N and edges from G which have both ends in N.
-
-    Parameters
-    ----------
-    G : NetworkX Graph
-    nbunch : node, container of nodes or None (for all nodes)
-
-    Returns
-    -------
-    subgraph : SubGraph View
-        A read-only view of the subgraph in `G` induced by the nodes.
-        Changes to the graph `G` will be reflected in the view.
-
-    Notes
-    -----
-    To create a mutable subgraph with its own copies of nodes
-    edges and attributes use `subgraph.copy()` or `nx.Graph(subgraph)`
-
-    For an inplace reduction of a graph to a subgraph you can remove nodes:
-    `G.remove_nodes_from(n in G if n not in set(nbunch))`
-
-    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 = show_nodes(G.nbunch_iter(nbunch))
-    if G.is_multigraph():
-        if G.is_directed():
-            return MultiDiSubGraph(G, induced_nodes)
-        return MultiSubGraph(G, induced_nodes)
-    if G.is_directed():
-        return DiSubGraph(G, induced_nodes)
-    return SubGraph(G, induced_nodes)
-
-
-def edge_subgraph(G, edges):
-    """Returns the subgraph induced by the specified edges.
-
-    The induced subgraph contains each edge in `edges` and each
-    node incident to any of those edges.
-
-    Parameters
-    ----------
-    G : NetworkX Graph
-    edges : iterable
-        An iterable of edges. Edges not present in `G` are ignored.
-
-    Returns
-    -------
-    subgraph : SubGraph View
-        A read-only edge-induced subgraph of `G`.
-        Changes to `G` are reflected in the view.
-
-    Notes
-    -----
-    To create a mutable subgraph with its own copies of nodes
-    edges and attributes use `subgraph.copy()` or `nx.Graph(subgraph)`
-
-    Examples
-    --------
-    >>> G = nx.path_graph(5)
-    >>> H = G.edge_subgraph([(0, 1), (3, 4)])
-    >>> list(H.nodes)
-    [0, 1, 3, 4]
-    >>> list(H.edges)
-    [(0, 1), (3, 4)]
-    """
-    edges = set(edges)
-    nodes = set()
-    for e in edges:
-        nodes.update(e[:2])
-    induced_edges = show_edges(edges)
-    induced_nodes = show_nodes(nodes)
-    if G.is_multigraph():
-        if G.is_directed():
-            return MultiDiSubGraph(G, induced_nodes, induced_edges)
-        return MultiSubGraph(G, induced_nodes, induced_edges)
-    if G.is_directed():
-        return DiSubGraph(G, induced_nodes, induced_edges)
-    return SubGraph(G, induced_nodes, induced_edges)
diff --git a/networkx/drawing/layout.py b/networkx/drawing/layout.py
index 5ba23d143..a930d7aba 100644
--- a/networkx/drawing/layout.py
+++ b/networkx/drawing/layout.py
@@ -298,6 +298,8 @@ def fruchterman_reingold_layout(G, k=None,
     if pos is not None:
         # Determine size of existing domain to adjust initial positions
         dom_size = max(coord for pos_tup in pos.values() for coord in pos_tup)
+        if dom_size == 0:
+            dom_size = 1
         shape = (len(G), dim)
         pos_arr = np.random.random(shape) * dom_size + center
         for i, n in enumerate(G):
diff --git a/networkx/readwrite/graphml.py b/networkx/readwrite/graphml.py
index 0eb02a3cb..52ca13566 100644
--- a/networkx/readwrite/graphml.py
+++ b/networkx/readwrite/graphml.py
@@ -181,7 +181,7 @@ def generate_graphml(G, encoding='utf-8', prettyprint=True):
 
 
 @open_file(0, mode='rb')
-def read_graphml(path, node_type=str):
+def read_graphml(path, node_type=str, edge_key_type=int):
     """Read graph in GraphML format from path.
 
     Parameters
@@ -193,6 +193,10 @@ def read_graphml(path, node_type=str):
     node_type: Python type (default: str)
        Convert node ids to this type
 
+    edge_key_type: Python type (default: int)
+       Convert graphml edge ids to this type as key of multi-edges
+
+
     Returns
     -------
     graph: NetworkX graph
@@ -229,7 +233,7 @@ def read_graphml(path, node_type=str):
     the file to "file.graphml.gz".
 
     """
-    reader = GraphMLReader(node_type=node_type)
+    reader = GraphMLReader(node_type=node_type, edge_key_type=edge_key_type)
     # need to check for multiple graphs
     glist = list(reader(path=path))
     if len(glist) == 0:
@@ -470,8 +474,6 @@ class GraphMLWriter(GraphML):
                                               id=make_str(key))
                 default = G.graph.get('edge_default', {})
                 self.add_attributes("edge", edge_element, data, default)
-                self.add_attributes("edge", edge_element,
-                                    {'key': key}, default)
                 graph_element.append(edge_element)
         else:
             for u, v, data in G.edges(data=True):
@@ -637,9 +639,6 @@ class GraphMLWriterLxml(GraphMLWriter):
         if G.is_multigraph():
             attributes = {}
             for u, v, ekey, d in G.edges(keys=True, data=True):
-                self.attribute_types[("key", "edge")].add(type(ekey))
-                if "key" not in attributes:
-                    attributes["key"] = ekey
                 for k, v in d.items():
                     self.attribute_types[(make_str(k), "edge")].add(type(v))
                     if k not in attributes:
@@ -693,14 +692,16 @@ else:
 
 class GraphMLReader(GraphML):
     """Read a GraphML document.  Produces NetworkX graph objects."""
-    def __init__(self, node_type=str):
+    def __init__(self, node_type=str, edge_key_type=int):
         try:
             import xml.etree.ElementTree
         except ImportError:
             msg = 'GraphML reader requires xml.elementtree.ElementTree'
             raise ImportError(msg)
         self.node_type = node_type
+        self.edge_key_type = edge_key_type
         self.multigraph = False  # assume multigraph and test for multiedges
+        self.edge_ids = {}  # dict mapping (u,v) tuples to id edge attributes
 
     def __call__(self, path=None, string=None):
         if path is not None:
@@ -748,11 +749,12 @@ class GraphMLReader(GraphML):
         # switch to Graph or DiGraph if no parallel edges were found.
         if not self.multigraph:
             if G.is_directed():
-                return nx.DiGraph(G)
+                G = nx.DiGraph(G)
             else:
-                return nx.Graph(G)
-        else:
-            return G
+                G = nx.Graph(G)
+            nx.set_edge_attributes(G, values=self.edge_ids, name='id')
+
+        return G
 
     def add_node(self, G, node_xml, graphml_keys):
         """Add a node to the graph.
@@ -792,17 +794,19 @@ class GraphMLReader(GraphML):
         # attribute is specified
         edge_id = edge_element.get("id")
         if edge_id:
-            if 'key' in data and make_str(data['key']) == edge_id:
-                # If id there equivalent to 'key' attribute, use key
-                edge_id = data.pop('key')
-            else:
-                data["id"] = edge_id
+            # self.edge_ids is used by `make_graph` method for non-multigraphs
+            self.edge_ids[source, target] = edge_id
+            try:
+                edge_id = self.edge_key_type(edge_id)
+            except ValueError:  # Could not convert.
+                pass
+        else:
+            edge_id = data.get('key')
+
         if G.has_edge(source, target):
             # mark this as a multigraph
             self.multigraph = True
-        if edge_id is None:
-            # no id specified, try using 'key' attribute as id
-            edge_id = data.pop('key', None)
+
         # Use add_edges_from to avoid error with add_edge when `'key' in data`
         G.add_edges_from([(source, target, edge_id, data)])
 
diff --git a/networkx/utils/contextmanagers.py b/networkx/utils/contextmanagers.py
index c4d515b68..7475ba5a0 100644
--- a/networkx/utils/contextmanagers.py
+++ b/networkx/utils/contextmanagers.py
@@ -19,11 +19,13 @@ def reversed(G):
     """
     directed = G.is_directed()
     if directed:
-        G.reverse(copy=False)
+        G._pred, G._succ = G._succ, G._pred
+        G._adj = G._succ
 
     try:
         yield
     finally:
         if directed:
             # Reverse the reverse.
-            G.reverse(copy=False)
+            G._pred, G._succ = G._succ, G._pred
+            G._adj = G._succ


Test patch:
diff --git a/networkx/algorithms/flow/tests/test_mincost.py b/networkx/algorithms/flow/tests/test_mincost.py
index cc367c8e4..fae01fafe 100644
--- a/networkx/algorithms/flow/tests/test_mincost.py
+++ b/networkx/algorithms/flow/tests/test_mincost.py
@@ -449,7 +449,6 @@ class TestMinCostFlow:
     def test_large(self):
         fname = os.path.join(os.path.dirname(__file__), 'netgen-2.gpickle.bz2')
         G = nx.read_gpickle(fname)
-        print(dir(G))
         flowCost, flowDict = nx.network_simplex(G)
         assert_equal(6749969302, flowCost)
         assert_equal(6749969302, nx.cost_of_flow(G, flowDict))
diff --git a/networkx/classes/tests/test_coreviews.py b/networkx/classes/tests/test_coreviews.py
new file mode 100644
index 000000000..3f04ebdc3
--- /dev/null
+++ b/networkx/classes/tests/test_coreviews.py
@@ -0,0 +1,227 @@
+from nose.tools import assert_equal, assert_not_equal, assert_is,\
+    assert_is_not, assert_true, assert_false, assert_raises
+
+import networkx as nx
+
+
+class test_atlasview(object):
+    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_len(self):
+        assert_equal(len(self.av), len(self.d))
+
+    def test_iter(self):
+        assert_equal(list(self.av), list(self.d))
+
+    def test_getitem(self):
+        assert_is(self.av[1], self.d[1])
+        assert_equal(self.av[2]['color'], 1)
+        assert_raises(KeyError, self.av.__getitem__, 3)
+
+    def test_copy(self):
+        avcopy = self.av.copy()
+        assert_equal(avcopy[0], self.av[0])
+        assert_is_not(avcopy[0], self.av[0])
+        assert_is_not(avcopy, self.av)
+        avcopy[5] = {}
+        assert_not_equal(avcopy, self.av)
+
+        avcopy[0]['ht'] = 4
+        assert_not_equal(avcopy[0], self.av[0])
+        self.av[0]['ht'] = 4
+        assert_equal(avcopy[0], self.av[0])
+        del self.av[0]['ht']
+
+        assert_false(hasattr(self.av, '__setitem__'))
+
+    def test_items(self):
+        assert_equal(sorted(self.av.items()), sorted(self.d.items()))
+
+    def test_repr(self):
+        out = "AtlasView(" + str(self.d) + ")"
+        assert_equal(str(self.av), out)
+
+
+class test_adjacencyview(object):
+    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_len(self):
+        assert_equal(len(self.adjview), len(self.adj))
+
+    def test_iter(self):
+        assert_equal(list(self.adjview), list(self.adj))
+
+    def test_getitem(self):
+        assert_is_not(self.adjview[1], self.adj[1])
+        assert_is(self.adjview[3][0], self.adjview[0][3])
+        assert_equal(self.adjview[2][3]['color'], 1)
+        assert_raises(KeyError, self.adjview.__getitem__, 4)
+
+    def test_copy(self):
+        avcopy = self.adjview.copy()
+        assert_equal(avcopy[0], self.adjview[0])
+        assert_is_not(avcopy[0], self.adjview[0])
+
+        avcopy[2][3]['ht'] = 4
+        assert_not_equal(avcopy[2], self.adjview[2])
+        self.adjview[2][3]['ht'] = 4
+        assert_equal(avcopy[2], self.adjview[2])
+        del self.adjview[2][3]['ht']
+
+        assert_false(hasattr(self.adjview, '__setitem__'))
+
+    def test_items(self):
+        view_items = sorted((n, dict(d)) for n, d in self.adjview.items())
+        assert_equal(view_items, sorted(self.adj.items()))
+
+    def test_repr(self):
+        out = self.adjview.__class__.__name__ + "(" + str(self.adj) + ")"
+        assert_equal(str(self.adjview), out)
+
+
+class test_multiadjacencyview(test_adjacencyview):
+    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_is_not(self.adjview[1], self.adj[1])
+        assert_is(self.adjview[3][0][3], self.adjview[0][3][3])
+        assert_equal(self.adjview[3][2][3]['color'], 1)
+        assert_raises(KeyError, self.adjview.__getitem__, 4)
+
+    def test_copy(self):
+        avcopy = self.adjview.copy()
+        assert_equal(avcopy[0], self.adjview[0])
+        assert_is_not(avcopy[0], self.adjview[0])
+
+        avcopy[2][3][8]['ht'] = 4
+        assert_not_equal(avcopy[2], self.adjview[2])
+        self.adjview[2][3][8]['ht'] = 4
+        assert_equal(avcopy[2], self.adjview[2])
+        del self.adjview[2][3][8]['ht']
+
+        assert_false(hasattr(self.adjview, '__setitem__'))
+
+
+class test_unionatlas(object):
+    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_len(self):
+        assert_equal(len(self.av), len(self.s) + len(self.p))
+
+    def test_iter(self):
+        assert_equal(set(self.av), set(self.s) | set(self.p))
+
+    def test_getitem(self):
+        assert_is(self.av[0], self.s[0])
+        assert_is(self.av[4], self.p[4])
+        assert_equal(self.av[2]['color'], 1)
+        assert_raises(KeyError, self.av[2].__getitem__, 'watch')
+        assert_raises(KeyError, self.av.__getitem__, 8)
+
+    def test_copy(self):
+        avcopy = self.av.copy()
+        assert_equal(avcopy[0], self.av[0])
+        assert_is_not(avcopy[0], self.av[0])
+        assert_is_not(avcopy, self.av)
+        avcopy[5] = {}
+        assert_not_equal(avcopy, self.av)
+
+        avcopy[0]['ht'] = 4
+        assert_not_equal(avcopy[0], self.av[0])
+        self.av[0]['ht'] = 4
+        assert_equal(avcopy[0], self.av[0])
+        del self.av[0]['ht']
+
+        assert_false(hasattr(self.av, '__setitem__'))
+
+    def test_items(self):
+        expected = dict(self.p.items())
+        expected.update(self.s)
+        print(sorted(self.av.items()), sorted(expected.items()))
+        assert_equal(sorted(self.av.items()), sorted(expected.items()))
+
+    def test_repr(self):
+        out = "{}({}, {})".format(self.av.__class__.__name__, self.s, self.p)
+        assert_equal(str(self.av), out)
+
+
+class test_unionadjacency(object):
+    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_len(self):
+        assert_equal(len(self.adjview), len(self.s))
+
+    def test_iter(self):
+        assert_equal(list(self.adjview), list(self.s))
+
+    def test_getitem(self):
+        assert_is_not(self.adjview[1], self.s[1])
+        assert_is(self.adjview[3][0], self.adjview[0][3])
+        assert_equal(self.adjview[2][3]['color'], 1)
+        assert_raises(KeyError, self.adjview.__getitem__, 4)
+
+    def test_copy(self):
+        avcopy = self.adjview.copy()
+        assert_equal(avcopy[0], self.adjview[0])
+        assert_is_not(avcopy[0], self.adjview[0])
+
+        avcopy[2][3]['ht'] = 4
+        assert_not_equal(avcopy[2], self.adjview[2])
+        self.adjview[2][3]['ht'] = 4
+        assert_equal(avcopy[2], self.adjview[2])
+        del self.adjview[2][3]['ht']
+
+        assert_false(hasattr(self.adjview, '__setitem__'))
+
+    def test_repr(self):
+        clsname = self.adjview.__class__.__name__
+        out = "{}({}, {})".format(clsname, self.s, self.p)
+        assert_equal(str(self.adjview), out)
+
+
+class test_unionmultiadjacency(test_unionadjacency):
+    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_is_not(self.adjview[1], self.s[1])
+        assert_is(self.adjview[3][0][9], self.adjview[0][3][9])
+        assert_equal(self.adjview[3][2][9]['color'], 1)
+        assert_raises(KeyError, self.adjview.__getitem__, 4)
+
+    def test_copy(self):
+        avcopy = self.adjview.copy()
+        assert_equal(avcopy[0], self.adjview[0])
+        assert_is_not(avcopy[0], self.adjview[0])
+
+        avcopy[2][3][8]['ht'] = 4
+        assert_not_equal(avcopy[2], self.adjview[2])
+        self.adjview[2][3][8]['ht'] = 4
+        assert_equal(avcopy[2], self.adjview[2])
+        del self.adjview[2][3][8]['ht']
+
+        assert_false(hasattr(self.adjview, '__setitem__'))
diff --git a/networkx/classes/tests/test_filters.py b/networkx/classes/tests/test_filters.py
new file mode 100644
index 000000000..624352166
--- /dev/null
+++ b/networkx/classes/tests/test_filters.py
@@ -0,0 +1,175 @@
+from nose.tools import assert_equals, assert_true, assert_false, assert_raises
+import networkx as nx
+
+class TestFilterFactory(object):
+    def test_no_filter(self):
+        nf = nx.filters.no_filter
+        assert_true(nf())
+        assert_true(nf(1))
+        assert_true(nf(2, 1))
+
+    def test_hide_nodes(self):
+        f = nx.classes.filters.hide_nodes([1, 2, 3])
+        assert_false(f(1))
+        assert_false(f(2))
+        assert_false(f(3))
+        assert_true(f(4))
+        assert_true(f(0))
+        assert_true(f('a'))
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f)
+
+    def test_show_nodes(self):
+        f = nx.classes.filters.show_nodes([1, 2, 3])
+        assert_true(f(1))
+        assert_true(f(2))
+        assert_true(f(3))
+        assert_false(f(4))
+        assert_false(f(0))
+        assert_false(f('a'))
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f)
+
+    def test_hide_edges(self):
+        factory = nx.classes.filters.hide_edges
+        f = factory([(1, 2), (3, 4)])
+        assert_false(f(1, 2))
+        assert_false(f(3, 4))
+        assert_false(f(4, 3))
+        assert_true(f(2, 3))
+        assert_true(f(0, -1))
+        assert_true(f('a', 'b'))
+        assert_raises(TypeError, f, 1, 2, 3)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2, 3)])
+
+    def test_show_edges(self):
+        factory = nx.classes.filters.show_edges
+        f = factory([(1, 2), (3, 4)])
+        assert_true(f(1, 2))
+        assert_true(f(3, 4))
+        assert_true(f(4, 3))
+        assert_false(f(2, 3))
+        assert_false(f(0, -1))
+        assert_false(f('a', 'b'))
+        assert_raises(TypeError, f, 1, 2, 3)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2, 3)])
+
+    def test_hide_diedges(self):
+        factory = nx.classes.filters.hide_diedges
+        f = factory([(1, 2), (3, 4)])
+        assert_false(f(1, 2))
+        assert_false(f(3, 4))
+        assert_true(f(4, 3))
+        assert_true(f(2, 3))
+        assert_true(f(0, -1))
+        assert_true(f('a', 'b'))
+        assert_raises(TypeError, f, 1, 2, 3)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2, 3)])
+
+    def test_show_diedges(self):
+        factory = nx.classes.filters.show_diedges
+        f = factory([(1, 2), (3, 4)])
+        assert_true(f(1, 2))
+        assert_true(f(3, 4))
+        assert_false(f(4, 3))
+        assert_false(f(2, 3))
+        assert_false(f(0, -1))
+        assert_false(f('a', 'b'))
+        assert_raises(TypeError, f, 1, 2, 3)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_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_false(f(1, 2, 0))
+        assert_false(f(1, 2, 1))
+        assert_true(f(1, 2, 2))
+        assert_true(f(3, 4, 0))
+        assert_false(f(3, 4, 1))
+        assert_false(f(4, 3, 1))
+        assert_true(f(4, 3, 0))
+        assert_true(f(2, 3, 0))
+        assert_true(f(0, -1, 0))
+        assert_true(f('a', 'b', 0))
+        assert_raises(TypeError, f, 1, 2, 3, 4)
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2)])
+        assert_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_true(f(1, 2, 0))
+        assert_true(f(1, 2, 1))
+        assert_false(f(1, 2, 2))
+        assert_false(f(3, 4, 0))
+        assert_true(f(3, 4, 1))
+        assert_true(f(4, 3, 1))
+        assert_false(f(4, 3, 0))
+        assert_false(f(2, 3, 0))
+        assert_false(f(0, -1, 0))
+        assert_false(f('a', 'b', 0))
+        assert_raises(TypeError, f, 1, 2, 3, 4)
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2)])
+        assert_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_false(f(1, 2, 0))
+        assert_false(f(1, 2, 1))
+        assert_true(f(1, 2, 2))
+        assert_true(f(3, 4, 0))
+        assert_false(f(3, 4, 1))
+        assert_true(f(4, 3, 1))
+        assert_true(f(4, 3, 0))
+        assert_true(f(2, 3, 0))
+        assert_true(f(0, -1, 0))
+        assert_true(f('a', 'b', 0))
+        assert_raises(TypeError, f, 1, 2, 3, 4)
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2)])
+        assert_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_true(f(1, 2, 0))
+        assert_true(f(1, 2, 1))
+        assert_false(f(1, 2, 2))
+        assert_false(f(3, 4, 0))
+        assert_true(f(3, 4, 1))
+        assert_false(f(4, 3, 1))
+        assert_false(f(4, 3, 0))
+        assert_false(f(2, 3, 0))
+        assert_false(f(0, -1, 0))
+        assert_false(f('a', 'b', 0))
+        assert_raises(TypeError, f, 1, 2, 3, 4)
+        assert_raises(TypeError, f, 1, 2)
+        assert_raises(TypeError, f, 1)
+        assert_raises(TypeError, f)
+        assert_raises(TypeError, factory, [1, 2, 3])
+        assert_raises(ValueError, factory, [(1, 2)])
+        assert_raises(ValueError, factory, [(1, 2, 3, 4)])
diff --git a/networkx/classes/tests/test_graphviews.py b/networkx/classes/tests/test_graphviews.py
new file mode 100644
index 000000000..eebbec41e
--- /dev/null
+++ b/networkx/classes/tests/test_graphviews.py
@@ -0,0 +1,125 @@
+from nose.tools import assert_in, assert_not_in, assert_equal
+from nose.tools import assert_raises, assert_true, assert_false
+import networkx as nx
+
+
+class test_reverse_view(object):
+    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_equal(rv._node, prv._node)
+        assert_equal(rv._adj, prv._adj)
+        assert_equal(rv.graph, prv.graph)
+
+    def test_contains(self):
+        assert_in((2, 3), self.G.edges)
+        assert_not_in((3, 2), self.G.edges)
+        assert_not_in((2, 3), self.rv.edges)
+        assert_in((3, 2), self.rv.edges)
+
+    def test_iter(self):
+        expected = sorted(tuple(reversed(e)) for e in self.G.edges)
+        assert_equal(sorted(self.rv.edges), expected)
+
+
+class test_multi_reverse_view(object):
+    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_equal(rv._node, prv._node)
+        assert_equal(rv._adj, prv._adj)
+        assert_equal(rv.graph, prv.graph)
+
+    def test_contains(self):
+        assert_in((2, 3, 0), self.G.edges)
+        assert_not_in((3, 2, 0), self.G.edges)
+        assert_not_in((2, 3, 0), self.rv.edges)
+        assert_in((3, 2, 0), self.rv.edges)
+        assert_in((5, 4, 1), self.rv.edges)
+        assert_not_in((4, 5, 1), self.rv.edges)
+
+    def test_iter(self):
+        expected = sorted((v, u, k) for u, v, k in self.G.edges)
+        assert_equal(sorted(self.rv.edges), expected)
+
+
+class test_to_directed(object):
+    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_false(self.G.is_directed())
+        assert_true(self.dv.is_directed())
+
+    def test_already_directed(self):
+        dd = nx.to_directed(self.dv)
+        Mdd = nx.to_directed(self.Mdv)
+
+    def test_pickle(self):
+        import pickle
+        dv = self.dv
+        pdv = pickle.loads(pickle.dumps(dv, -1))
+        assert_equal(dv._node, pdv._node)
+        assert_equal(dv._succ, pdv._succ)
+        assert_equal(dv._pred, pdv._pred)
+        assert_equal(dv.graph, pdv.graph)
+
+    def test_contains(self):
+        assert_in((2, 3), self.G.edges)
+        assert_in((3, 2), self.G.edges)
+        assert_in((2, 3), self.dv.edges)
+        assert_in((3, 2), 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_equal(sorted(self.dv.edges), expected)
+
+
+class test_to_undirected(object):
+    def setup(self):
+        self.G = nx.path_graph(9)
+        self.G = nx.path_graph(9, create_using=nx.DiGraph())
+        self.dv = nx.to_undirected(self.G)
+        self.MG = nx.path_graph(9, create_using=nx.MultiDiGraph())
+        self.Mdv = nx.to_undirected(self.MG)
+
+    def test_directed(self):
+        assert_true(self.G.is_directed())
+        assert_false(self.dv.is_directed())
+
+    def test_already_directed(self):
+        dd = nx.to_undirected(self.dv)
+        Mdd = nx.to_undirected(self.Mdv)
+
+    def test_pickle(self):
+        import pickle
+        dv = self.dv
+        pdv = pickle.loads(pickle.dumps(dv, -1))
+        assert_equal(dv._node, pdv._node)
+        assert_equal(dv._adj, pdv._adj)
+        assert_equal(dv.graph, pdv.graph)
+
+    def test_contains(self):
+        assert_in((2, 3), self.G.edges)
+        assert_not_in((3, 2), self.G.edges)
+        assert_in((2, 3), self.dv.edges)
+        assert_in((3, 2), self.dv.edges)
+
+    def test_iter(self):
+        expected = sorted(self.G.edges)
+        assert_equal(sorted(self.dv.edges), expected)
diff --git a/networkx/classes/tests/test_ordered.py b/networkx/classes/tests/test_ordered.py
index e488f1dc8..adde306e3 100644
--- a/networkx/classes/tests/test_ordered.py
+++ b/networkx/classes/tests/test_ordered.py
@@ -1,3 +1,4 @@
+from nose.tools import assert_equals
 import networkx as nx
 
 
@@ -14,3 +15,23 @@ class SmokeTestOrdered(object):
 
     def test_multidigraph():
         G = nx.OrderedMultiDiGraph()
+
+class TestOrderedFeatures(object):
+    def setUp(self):
+        self.G = nx.OrderedDiGraph()
+        self.G.add_nodes_from([1,2,3])
+        self.G.add_edges_from([(2, 3), (1, 3)])
+
+    def test_subgraph_order(self):
+        G = self.G
+        G_sub = G.subgraph([1,2,3])
+        #G_sub = nx.induced_subgraph(G, [1,2,3])
+        assert_equals(list(G.nodes), list(G_sub.nodes))
+        assert_equals(list(G.edges), list(G_sub.edges))
+        # This is a bug pointed out in #2048   
+        # Will be fixed in coming commit and these tests changed
+        # FIXME take out the sorted. replace with list
+        assert_equals(list(G.pred[3]), sorted(G_sub.pred[3], reverse=True))
+        assert_equals([2, 1], sorted(G_sub.pred[3], reverse=True))
+        assert_equals([], list(G_sub.succ[3]))
+
diff --git a/networkx/classes/tests/test_subgraph.py b/networkx/classes/tests/test_subgraphviews.py
similarity index 88%
rename from networkx/classes/tests/test_subgraph.py
rename to networkx/classes/tests/test_subgraphviews.py
index 850553390..5d894893e 100644
--- a/networkx/classes/tests/test_subgraph.py
+++ b/networkx/classes/tests/test_subgraphviews.py
@@ -5,12 +5,13 @@ import networkx as nx
 
 
 class test_graphview(object):
+    gview = nx.graphviews.SubGraph
+    graph = nx.Graph
     hide_edges_filter = staticmethod(nx.filters.hide_edges)
     show_edges_filter = staticmethod(nx.filters.show_edges)
 
     def setUp(self):
-        self.gview = nx.SubGraph
-        self.G = nx.path_graph(9)
+        self.G = nx.path_graph(9, create_using=self.graph())
         self.hide_edges_w_hide_nodes = {(3, 4), (4, 5), (5, 6)}
 
     def test_hidden_nodes(self):
@@ -32,9 +33,9 @@ class test_graphview(object):
         assert_equal(G.size(), 7 if G.is_multigraph() else 5)
 
     def test_hidden_edges(self):
-        self.hide_edges = [(2, 3), (8, 7), (222, 223)]
-        self.edges_gone = self.hide_edges_filter(self.hide_edges)
-        G = self.gview(self.G, filter_edge=self.edges_gone)
+        hide_edges = [(2, 3), (8, 7), (222, 223)]
+        edges_gone = self.hide_edges_filter(hide_edges)
+        G = self.gview(self.G, filter_edge=edges_gone)
         assert_equal(self.G.nodes, G.nodes)
         if G.is_directed():
             assert_equal(self.G.edges - G.edges, {(2, 3)})
@@ -52,8 +53,8 @@ class test_graphview(object):
         assert_equal(G.degree(3), 1)
 
     def test_shown_node(self):
-        self.induced_subgraph = nx.filters.show_nodes([2, 3, 111])
-        G = self.gview(self.G, filter_node=self.induced_subgraph)
+        induced_subgraph = nx.filters.show_nodes([2, 3, 111])
+        G = self.gview(self.G, filter_node=induced_subgraph)
         assert_equal(set(G.nodes), {2, 3})
         if G.is_directed():
             assert_equal(list(G[3]), [])
@@ -89,28 +90,24 @@ class test_graphview(object):
 
 
 class test_digraphview(test_graphview):
+    gview = nx.graphviews.DiSubGraph
+    graph = nx.DiGraph
     hide_edges_filter = staticmethod(nx.filters.hide_diedges)
     show_edges_filter = staticmethod(nx.filters.show_diedges)
-
-    def setUp(self):
-        self.gview = nx.DiSubGraph
-        self.G = nx.path_graph(9, create_using=nx.DiGraph())
-        self.hide_edges_w_hide_nodes = {(3, 4), (4, 5), (5, 6)}
+    hide_edges = [(2, 3), (8, 7), (222, 223)]
+    excluded = {(2, 3), (3, 4), (4, 5), (5, 6)}
 
     def test_inoutedges(self):
-        hide_edges = [(2, 3), (8, 7), (222, 223)]
-        edges_gone = nx.filters.hide_diedges(hide_edges)
+        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)
 
-        excluded = {(2, 3), (3, 4), (4, 5), (5, 6)}
-        assert_equal(self.G.in_edges - G.in_edges, excluded)
-        assert_equal(self.G.out_edges - G.out_edges, excluded)
+        assert_equal(self.G.in_edges - G.in_edges, self.excluded)
+        assert_equal(self.G.out_edges - G.out_edges, self.excluded)
 
     def test_pred(self):
-        hide_edges = [(2, 3), (8, 7), (222, 223)]
-        edges_gone = nx.filters.hide_diedges(hide_edges)
+        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)
@@ -119,8 +116,7 @@ class test_digraphview(test_graphview):
         assert_equal(list(G.pred[6]), [])
 
     def test_inout_degree(self):
-        hide_edges = [(2, 3), (8, 7), (222, 223)]
-        edges_gone = nx.filters.hide_diedges(hide_edges)
+        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)
@@ -133,7 +129,7 @@ class test_digraphview(test_graphview):
 
 # multigraph
 class test_multigraphview(test_graphview):
-    gview = nx.MultiSubGraph
+    gview = nx.graphviews.MultiSubGraph
     graph = nx.MultiGraph
     hide_edges_filter = staticmethod(nx.filters.hide_multiedges)
     show_edges_filter = staticmethod(nx.filters.show_multiedges)
@@ -187,11 +183,25 @@ class test_multigraphview(test_graphview):
 
 
 # multidigraph
-class test_multidigraphview(test_multigraphview):
-    gview = nx.MultiDiSubGraph
+class test_multidigraphview(test_multigraphview, test_digraphview):
+    gview = nx.graphviews.MultiDiSubGraph
     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_equal(G.degree(2), 3)
+        assert_equal(G.out_degree(2), 2)
+        assert_equal(G.in_degree(2), 1)
+        assert_equal(G.size(), 6)
 
 
 # induced_subgraph
diff --git a/networkx/classes/tests/test_views.py b/networkx/classes/tests/test_views.py
index ebab02e7a..225daf554 100644
--- a/networkx/classes/tests/test_views.py
+++ b/networkx/classes/tests/test_views.py
@@ -11,7 +11,7 @@ class test_nodeview(object):
 
     def test_pickle(self):
         import pickle
-        nv = self.G.nodes()  # nx.NodeView(self.G)
+        nv = self.G.nodes()  # NodeView(self.G)
         pnv = pickle.loads(pickle.dumps(nv, -1))
         assert_equal(nv, pnv)
         assert_equal(nv.__slots__, pnv.__slots__)
@@ -121,7 +121,7 @@ class test_edgedataview(object):
     def setup(self):
         self.G = nx.path_graph(9)
         self.DG = nx.path_graph(9, create_using=nx.DiGraph())
-        self.eview = nx.EdgeView
+        self.eview = nx.reportviews.EdgeView
 
         def modify_edge(G, e, **kwds):
             G._adj[e[0]][e[1]].update(kwds)
@@ -202,7 +202,7 @@ class test_edgedataview(object):
 class test_edgeview(object):
     def setup(self):
         self.G = nx.path_graph(9)
-        self.eview = nx.EdgeView
+        self.eview = nx.reportviews.EdgeView
 
         def modify_edge(G, e, **kwds):
             G._adj[e[0]][e[1]].update(kwds)
@@ -306,7 +306,7 @@ class test_edgeview(object):
 class test_directed_edges(test_edgeview):
     def setup(self):
         self.G = nx.path_graph(9, nx.DiGraph())
-        self.eview = nx.OutEdgeView
+        self.eview = nx.reportviews.OutEdgeView
 
         def modify_edge(G, e, **kwds):
             G._adj[e[0]][e[1]].update(kwds)
@@ -322,7 +322,7 @@ class test_directed_edges(test_edgeview):
 class test_inedges(test_edgeview):
     def setup(self):
         self.G = nx.path_graph(9, nx.DiGraph())
-        self.eview = nx.InEdgeView
+        self.eview = nx.reportviews.InEdgeView
 
         def modify_edge(G, e, **kwds):
             G._adj[e[0]][e[1]].update(kwds)
@@ -339,7 +339,7 @@ class test_multiedges(test_edgeview):
     def setup(self):
         self.G = nx.path_graph(9, nx.MultiGraph())
         self.G.add_edge(1, 2, key=3, foo='bar')
-        self.eview = nx.MultiEdgeView
+        self.eview = nx.reportviews.MultiEdgeView
 
         def modify_edge(G, e, **kwds):
             if len(e) == 2:
@@ -478,7 +478,7 @@ class test_directed_multiedges(test_multiedges):
     def setup(self):
         self.G = nx.path_graph(9, nx.MultiDiGraph())
         self.G.add_edge(1, 2, key=3, foo='bar')
-        self.eview = nx.OutMultiEdgeView
+        self.eview = nx.reportviews.OutMultiEdgeView
 
         def modify_edge(G, e, **kwds):
             if len(e) == 2:
@@ -497,7 +497,7 @@ class test_in_multiedges(test_multiedges):
     def setup(self):
         self.G = nx.path_graph(9, nx.MultiDiGraph())
         self.G.add_edge(1, 2, key=3, foo='bar')
-        self.eview = nx.InMultiEdgeView
+        self.eview = nx.reportviews.InMultiEdgeView
 
         def modify_edge(G, e, **kwds):
             if len(e) == 2:
@@ -515,7 +515,7 @@ class test_in_multiedges(test_multiedges):
 # Degrees
 class test_degreeview(object):
     GRAPH = nx.Graph
-    dview = nx.DegreeView
+    dview = nx.reportviews.DegreeView
 
     def setup(self):
         self.G = nx.path_graph(6, self.GRAPH())
@@ -590,7 +590,7 @@ class test_degreeview(object):
 
 class test_didegreeview(test_degreeview):
     GRAPH = nx.DiGraph
-    dview = nx.DiDegreeView
+    dview = nx.reportviews.DiDegreeView
 
     def test_repr(self):
         dv = self.G.degree()
@@ -600,7 +600,7 @@ class test_didegreeview(test_degreeview):
 
 class test_outdegreeview(test_degreeview):
     GRAPH = nx.DiGraph
-    dview = nx.OutDegreeView
+    dview = nx.reportviews.OutDegreeView
 
     def test_repr(self):
         dv = self.G.out_degree()
@@ -643,7 +643,7 @@ class test_outdegreeview(test_degreeview):
 
 class test_indegreeview(test_degreeview):
     GRAPH = nx.DiGraph
-    dview = nx.InDegreeView
+    dview = nx.reportviews.InDegreeView
 
     def test_repr(self):
         dv = self.G.in_degree()
@@ -686,7 +686,7 @@ class test_indegreeview(test_degreeview):
 
 class test_multidegreeview(test_degreeview):
     GRAPH = nx.MultiGraph
-    dview = nx.MultiDegreeView
+    dview = nx.reportviews.MultiDegreeView
 
     def test_repr(self):
         dv = self.G.degree()
@@ -729,7 +729,7 @@ class test_multidegreeview(test_degreeview):
 
 class test_dimultidegreeview(test_multidegreeview):
     GRAPH = nx.MultiDiGraph
-    dview = nx.DiMultiDegreeView
+    dview = nx.reportviews.DiMultiDegreeView
 
     def test_repr(self):
         dv = self.G.degree()
@@ -739,7 +739,7 @@ class test_dimultidegreeview(test_multidegreeview):
 
 class test_outmultidegreeview(test_degreeview):
     GRAPH = nx.MultiDiGraph
-    dview = nx.OutMultiDegreeView
+    dview = nx.reportviews.OutMultiDegreeView
 
     def test_repr(self):
         dv = self.G.out_degree()
@@ -782,7 +782,7 @@ class test_outmultidegreeview(test_degreeview):
 
 class test_inmultidegreeview(test_degreeview):
     GRAPH = nx.MultiDiGraph
-    dview = nx.InMultiDegreeView
+    dview = nx.reportviews.InMultiDegreeView
 
     def test_repr(self):
         dv = self.G.in_degree()
diff --git a/networkx/drawing/tests/test_layout.py b/networkx/drawing/tests/test_layout.py
index d2ddf5325..8b6d25d1b 100644
--- a/networkx/drawing/tests/test_layout.py
+++ b/networkx/drawing/tests/test_layout.py
@@ -27,6 +27,18 @@ class TestLayout(object):
         nx.add_path(self.Gs, 'abcdef')
         self.bigG = nx.grid_2d_graph(25, 25) #bigger than 500 nodes for sparse
 
+    def test_spring_init_pos(self):
+        # Tests GH #2448
+        import math
+        G = nx.Graph()
+        G.add_edges_from([(0, 1), (1, 2), (2, 0), (2, 3)])
+
+        init_pos = {0: (0.0, 0.0)}
+        fixed_pos = [0]
+        pos = nx.fruchterman_reingold_layout(G, pos=init_pos, fixed=fixed_pos)
+        has_nan = any(math.isnan(c) for coords in pos.values() for c in coords)
+        assert_false(has_nan, 'values should not be nan')
+
     def test_smoke_int(self):
         G = self.Gi
         vpos = nx.random_layout(G)
diff --git a/networkx/readwrite/tests/test_graphml.py b/networkx/readwrite/tests/test_graphml.py
index 5ee10463a..6bd50e9eb 100644
--- a/networkx/readwrite/tests/test_graphml.py
+++ b/networkx/readwrite/tests/test_graphml.py
@@ -337,6 +337,35 @@ class TestReadGraphML(BaseGraphML):
         H = nx.parse_graphml(s)
         assert_equal(sorted(H.edges(keys=True)), expected)
 
+    def test_preserve_multi_edge_data(self):
+        """
+        Test that data and keys of edges are preserved on consequent
+        write and reads
+        """
+        G = nx.MultiGraph()
+        G.add_node(1)
+        G.add_node(2)
+        G.add_edges_from([
+            # edges with no data, no keys:
+            (1, 2),
+            # edges with only data:
+            (1, 2, dict(key='data_key1')),
+            (1, 2, dict(id='data_id2')),
+            (1, 2, dict(key='data_key3', id='data_id3')),
+            # edges with both data and keys:
+            (1, 2, 103, dict(key='data_key4')),
+            (1, 2, 104, dict(id='data_id5')),
+            (1, 2, 105, dict(key='data_key6', id='data_id7')),
+        ])
+        fh = io.BytesIO()
+        nx.write_graphml(G, fh)
+        fh.seek(0)
+        H = nx.read_graphml(fh, node_type=int)
+        assert_edges_equal(
+            G.edges(data=True, keys=True), H.edges(data=True, keys=True)
+        )
+        assert_equal(G._adj, H._adj)
+
     def test_yfiles_extension(self):
         data = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <graphml xmlns="http://graphml.graphdrawing.org/xmlns"