Repository: networkx/networkx

Issue:
maximal_matching and max_weight_matching have different return types
The former returns a set of edges, the latter a dictionary. Should these return the same type of object?


Discussion:

Patch:
diff --git a/networkx/algorithms/covering.py b/networkx/algorithms/covering.py
index a70a8db4c..43c5124c2 100644
--- a/networkx/algorithms/covering.py
+++ b/networkx/algorithms/covering.py
@@ -72,9 +72,12 @@ def min_edge_cover(G, matching_algorithm=None):
                                      maxcardinality=True)
     maximum_matching = matching_algorithm(G)
     # ``min_cover`` is superset of ``maximum_matching``
-    min_cover = set(maximum_matching.items())
+    try:
+        min_cover = set(maximum_matching.items()) # bipartite matching case returns dict
+    except AttributeError:
+        min_cover = maximum_matching
     # iterate for uncovered nodes
-    uncovered_nodes = set(G) - {v for u, v in min_cover}
+    uncovered_nodes = set(G) - {v for u, v in min_cover} - {u for u, v in min_cover}
     for v in uncovered_nodes:
         # Since `v` is uncovered, each edge incident to `v` will join it
         # with a covered node (otherwise, if there were an edge joining
diff --git a/networkx/algorithms/matching.py b/networkx/algorithms/matching.py
index 4ff1da4df..ccf9c3770 100644
--- a/networkx/algorithms/matching.py
+++ b/networkx/algorithms/matching.py
@@ -69,7 +69,8 @@ def matching_dict_to_set(matching):
     # appear in `matching.items()`, so we use a set of sets. This way,
     # only the (frozen)set `{u, v}` appears as an element in the
     # returned set.
-    return set(map(frozenset, matching.items()))
+
+    return set((u,v) for (u,v) in set(map(frozenset, matching.items())))
 
 
 def is_matching(G, matching):
@@ -172,11 +173,8 @@ def max_weight_matching(G, maxcardinality=False, weight='weight'):
 
     Returns
     -------
-    mate : dictionary
-       The matching is returned as a dictionary, mate, such that
-       mate[v] == w if node v is matched to node w.  Unmatched nodes do not
-       occur as a key in mate.
-
+    matching : set
+        A maximal matching of the graph.
 
     Notes
     -----
@@ -249,7 +247,7 @@ def max_weight_matching(G, maxcardinality=False, weight='weight'):
     # Get a list of vertices.
     gnodes = list(G)
     if not gnodes:
-        return {}  # don't bother with empty graphs
+        return set( ) # don't bother with empty graphs
 
     # Find the maximum edge weight.
     maxweight = 0
@@ -931,4 +929,4 @@ def max_weight_matching(G, maxcardinality=False, weight='weight'):
     if allinteger:
         verifyOptimum()
 
-    return mate
+    return matching_dict_to_set(mate)
diff --git a/networkx/drawing/layout.py b/networkx/drawing/layout.py
index 71d7df3b7..93a88c3fe 100644
--- a/networkx/drawing/layout.py
+++ b/networkx/drawing/layout.py
@@ -232,6 +232,7 @@ def fruchterman_reingold_layout(G, k=None,
                                 pos=None,
                                 fixed=None,
                                 iterations=50,
+                                threshold = 1e-4,
                                 weight='weight',
                                 scale=1,
                                 center=None,
@@ -257,7 +258,11 @@ def fruchterman_reingold_layout(G, k=None,
         Nodes to keep fixed at initial position.
 
     iterations : int  optional (default=50)
-        Number of iterations of spring-force relaxation
+        Maximum number of iterations taken
+
+    threshold: float optional (default = 1e-4)
+        Threshold for relative error in node position changes.
+        The iteration stops if the error is below this threshold.
 
     weight : string or None   optional (default='weight')
         The edge attribute that holds the numerical value used for
@@ -318,18 +323,18 @@ def fruchterman_reingold_layout(G, k=None,
             raise ValueError
         A = nx.to_scipy_sparse_matrix(G, weight=weight, dtype='f')
         if k is None and fixed is not None:
-            # We must adjust k by domain size for layouts not near 1x1
+        # We must adjust k by domain size for layouts not near 1x1
             nnodes, _ = A.shape
             k = dom_size / np.sqrt(nnodes)
         pos = _sparse_fruchterman_reingold(A, k, pos_arr, fixed,
-                                           iterations, dim)
+                                               iterations, threshold, dim)
     except:
         A = nx.to_numpy_matrix(G, weight=weight)
         if k is None and fixed is not None:
             # We must adjust k by domain size for layouts not near 1x1
             nnodes, _ = A.shape
             k = dom_size / np.sqrt(nnodes)
-        pos = _fruchterman_reingold(A, k, pos_arr, fixed, iterations, dim)
+        pos = _fruchterman_reingold(A, k, pos_arr, fixed, iterations, threshold, dim)
     if fixed is None:
         pos = rescale_layout(pos, scale=scale) + center
     pos = dict(zip(G, pos))
@@ -340,7 +345,7 @@ spring_layout = fruchterman_reingold_layout
 
 
 def _fruchterman_reingold(A, k=None, pos=None, fixed=None,
-                          iterations=50, dim=2):
+                          iterations=50, threshold = 1e-4, dim=2):
     # Position nodes in adjacency matrix A using Fruchterman-Reingold
     # Entry point for NetworkX graph is fruchterman_reingold_layout()
     try:
@@ -401,11 +406,14 @@ def _fruchterman_reingold(A, k=None, pos=None, fixed=None,
         pos += delta_pos
         # cool temperature
         t -= dt
+        err = np.linalg.norm(delta_pos)/nnodes
+        if err < threshold:
+            break
     return pos
 
 
 def _sparse_fruchterman_reingold(A, k=None, pos=None, fixed=None,
-                                 iterations=50, dim=2):
+                                 iterations=50, threshold=1e-4, dim=2):
     # Position nodes in adjacency matrix A using Fruchterman-Reingold
     # Entry point for NetworkX graph is fruchterman_reingold_layout()
     # Sparse version
@@ -472,9 +480,13 @@ def _sparse_fruchterman_reingold(A, k=None, pos=None, fixed=None,
         # update positions
         length = np.sqrt((displacement**2).sum(axis=0))
         length = np.where(length < 0.01, 0.1, length)
-        pos += (displacement * t / length).T
+        delta_pos = (displacement * t / length).T
+        pos += delta_pos
         # cool temperature
         t -= dt
+        err = np.linalg.norm(delta_pos)/nnodes
+        if err < threshold:
+            break
     return pos
 
 
diff --git a/networkx/drawing/nx_agraph.py b/networkx/drawing/nx_agraph.py
index 535c2e559..4cd5b5de9 100644
--- a/networkx/drawing/nx_agraph.py
+++ b/networkx/drawing/nx_agraph.py
@@ -150,18 +150,29 @@ def to_agraph(N):
 
     # add nodes
     for n, nodedata in N.nodes(data=True):
-        A.add_node(n, **nodedata)
+        A.add_node(n)
+        if nodedata is not None:
+            a = A.get_node(n)
+            a.attr.update({k: str(v) for k, v in nodedata.items()})
 
     # loop over edges
-
     if N.is_multigraph():
         for u, v, key, edgedata in N.edges(data=True, keys=True):
-            str_edata = {k: str(v) for k, v in edgedata.items() if k != 'key'}
-            A.add_edge(u, v, key=str(key), **str_edata)
+            str_edgedata = {k: str(v) for k, v in edgedata.items() if k != 'key'}
+            A.add_edge(u, v, key=str(key))
+            if edgedata is not None:
+                a = A.get_edge(u,v)
+                a.attr.update(str_edgedata)
+
+
     else:
         for u, v, edgedata in N.edges(data=True):
             str_edgedata = {k: str(v) for k, v in edgedata.items()}
-            A.add_edge(u, v, **str_edgedata)
+            A.add_edge(u, v)
+            if edgedata is not None:
+                a = A.get_edge(u,v)
+                a.attr.update(str_edgedata)
+
 
     return A
 


Test patch:
diff --git a/networkx/algorithms/tests/test_covering.py b/networkx/algorithms/tests/test_covering.py
index 1b87c2fa5..7d2c44cd6 100644
--- a/networkx/algorithms/tests/test_covering.py
+++ b/networkx/algorithms/tests/test_covering.py
@@ -24,7 +24,7 @@ class TestMinEdgeCover:
         G = nx.Graph()
         G.add_edge(0, 1)
         assert_equal(nx.min_edge_cover(G),
-                     {(0, 1), (1, 0)})
+                     {(0, 1)})
 
     def test_bipartite_explicit(self):
         G = nx.Graph()
@@ -34,6 +34,7 @@ class TestMinEdgeCover:
                           (2, 'c'), (3, 'c'), (4, 'a')])
         min_cover = nx.min_edge_cover(G, nx.algorithms.bipartite.matching.
                                       eppstein_matching)
+        min_cover2 = nx.min_edge_cover(G)
         assert_true(nx.is_edge_cover(G, min_cover))
         assert_equal(len(min_cover), 8)
 
@@ -41,7 +42,7 @@ class TestMinEdgeCover:
         G = nx.complete_graph(10)
         min_cover = nx.min_edge_cover(G)
         assert_true(nx.is_edge_cover(G, min_cover))
-        assert_equal(len(min_cover), 10)
+        assert_equal(len(min_cover), 5)
 
 
 class TestIsEdgeCover:
diff --git a/networkx/algorithms/tests/test_matching.py b/networkx/algorithms/tests/test_matching.py
index b6b4ef4b1..0eb0f1c32 100644
--- a/networkx/algorithms/tests/test_matching.py
+++ b/networkx/algorithms/tests/test_matching.py
@@ -5,7 +5,8 @@ from nose.tools import assert_equal
 from nose.tools import assert_false
 from nose.tools import assert_true
 import networkx as nx
-
+from networkx.algorithms.matching import matching_dict_to_set
+from networkx.testing import assert_edges_equal
 
 class TestMaxWeightMatching(object):
     """Unit tests for the
@@ -16,28 +17,28 @@ class TestMaxWeightMatching(object):
     def test_trivial1(self):
         """Empty graph"""
         G = nx.Graph()
-        assert_equal(nx.max_weight_matching(G), {})
+        assert_equal(nx.max_weight_matching(G), set())
 
     def test_trivial2(self):
         """Self loop"""
         G = nx.Graph()
         G.add_edge(0, 0, weight=100)
-        assert_equal(nx.max_weight_matching(G), {})
+        assert_equal(nx.max_weight_matching(G), set())
 
     def test_trivial3(self):
         """Single edge"""
         G = nx.Graph()
         G.add_edge(0, 1)
-        assert_equal(nx.max_weight_matching(G),
-                     {0: 1, 1: 0})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({0: 1, 1: 0}))
 
     def test_trivial4(self):
         """Small graph"""
         G = nx.Graph()
         G.add_edge('one', 'two', weight=10)
         G.add_edge('two', 'three', weight=11)
-        assert_equal(nx.max_weight_matching(G),
-                     {'three': 'two', 'two': 'three'})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({'three': 'two', 'two': 'three'}))
 
     def test_trivial5(self):
         """Path"""
@@ -45,18 +46,18 @@ class TestMaxWeightMatching(object):
         G.add_edge(1, 2, weight=5)
         G.add_edge(2, 3, weight=11)
         G.add_edge(3, 4, weight=5)
-        assert_equal(nx.max_weight_matching(G),
-                     {2: 3, 3: 2})
-        assert_equal(nx.max_weight_matching(G, 1),
-                     {1: 2, 2: 1, 3: 4, 4: 3})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({2: 3, 3: 2}))
+        assert_edges_equal(nx.max_weight_matching(G, 1),
+                    matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3}))
 
     def test_trivial6(self):
         """Small graph with arbitrary weight attribute"""
         G = nx.Graph()
         G.add_edge('one', 'two', weight=10, abcd=11)
         G.add_edge('two', 'three', weight=11, abcd=10)
-        assert_equal(nx.max_weight_matching(G, weight='abcd'),
-                     {'one': 'two', 'two': 'one'})
+        assert_edges_equal(nx.max_weight_matching(G, weight='abcd'),
+                     matching_dict_to_set({'one': 'two', 'two': 'one'}))
 
     def test_floating_point_weights(self):
         """Floating point weights"""
@@ -65,8 +66,8 @@ class TestMaxWeightMatching(object):
         G.add_edge(2, 3, weight=math.exp(1))
         G.add_edge(1, 3, weight=3.0)
         G.add_edge(1, 4, weight=math.sqrt(2.0))
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 4, 2: 3, 3: 2, 4: 1})
+        assert_edges_equal(nx.max_weight_matching(G),
+                    matching_dict_to_set({1: 4, 2: 3, 3: 2, 4: 1}))
 
     def test_negative_weights(self):
         """Negative weights"""
@@ -76,38 +77,38 @@ class TestMaxWeightMatching(object):
         G.add_edge(2, 3, weight=1)
         G.add_edge(2, 4, weight=-1)
         G.add_edge(3, 4, weight=-6)
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1})
-        assert_equal(nx.max_weight_matching(G, 1),
-                     {1: 3, 2: 4, 3: 1, 4: 2})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1}))
+        assert_edges_equal(nx.max_weight_matching(G, 1),
+                     matching_dict_to_set({1: 3, 2: 4, 3: 1, 4: 2}))
 
     def test_s_blossom(self):
         """Create S-blossom and use it for augmentation:"""
         G = nx.Graph()
         G.add_weighted_edges_from([(1, 2, 8), (1, 3, 9),
                                    (2, 3, 10), (3, 4, 7)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1, 3: 4, 4: 3})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3}))
 
         G.add_weighted_edges_from([(1, 6, 5), (4, 5, 6)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}))
 
     def test_s_t_blossom(self):
         """Create S-blossom, relabel as T-blossom, use for augmentation:"""
         G = nx.Graph()
         G.add_weighted_edges_from([(1, 2, 9), (1, 3, 8), (2, 3, 10),
                                    (1, 4, 5), (4, 5, 4), (1, 6, 3)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}))
         G.add_edge(4, 5, weight=3)
         G.add_edge(1, 6, weight=4)
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}))
         G.remove_edge(1, 6)
         G.add_edge(3, 6, weight=4)
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1, 3: 6, 4: 5, 5: 4, 6: 3})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1, 3: 6, 4: 5, 5: 4, 6: 3}))
 
     def test_nested_s_blossom(self):
         """Create nested S-blossom, use for augmentation:"""
@@ -117,7 +118,7 @@ class TestMaxWeightMatching(object):
                                    (2, 4, 8), (3, 5, 8), (4, 5, 10),
                                    (5, 6, 6)])
         assert_equal(nx.max_weight_matching(G),
-                     {1: 3, 2: 4, 3: 1, 4: 2, 5: 6, 6: 5})
+                     matching_dict_to_set({1: 3, 2: 4, 3: 1, 4: 2, 5: 6, 6: 5}))
 
     def test_nested_s_blossom_relabel(self):
         """Create S-blossom, relabel as S, include in nested S-blossom:"""
@@ -125,8 +126,8 @@ class TestMaxWeightMatching(object):
         G.add_weighted_edges_from([(1, 2, 10), (1, 7, 10), (2, 3, 12),
                                    (3, 4, 20), (3, 5, 20), (4, 5, 25),
                                    (5, 6, 10), (6, 7, 10), (7, 8, 8)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1, 3: 4, 4: 3, 5: 6, 6: 5, 7: 8, 8: 7})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3, 5: 6, 6: 5, 7: 8, 8: 7}))
 
     def test_nested_s_blossom_expand(self):
         """Create nested S-blossom, augment, expand recursively:"""
@@ -135,8 +136,8 @@ class TestMaxWeightMatching(object):
                                    (2, 4, 12), (3, 5, 12), (4, 5, 14),
                                    (4, 6, 12), (5, 7, 12), (6, 7, 14),
                                    (7, 8, 12)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1, 3: 5, 4: 6, 5: 3, 6: 4, 7: 8, 8: 7})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1, 3: 5, 4: 6, 5: 3, 6: 4, 7: 8, 8: 7}))
 
     def test_s_blossom_relabel_expand(self):
         """Create S-blossom, relabel as T, expand:"""
@@ -144,8 +145,8 @@ class TestMaxWeightMatching(object):
         G.add_weighted_edges_from([(1, 2, 23), (1, 5, 22), (1, 6, 15),
                                    (2, 3, 25), (3, 4, 22), (4, 5, 25),
                                    (4, 8, 14), (5, 7, 13)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4}))
 
     def test_nested_s_blossom_relabel_expand(self):
         """Create nested S-blossom, relabel as T, expand:"""
@@ -153,8 +154,8 @@ class TestMaxWeightMatching(object):
         G.add_weighted_edges_from([(1, 2, 19), (1, 3, 20), (1, 8, 8),
                                    (2, 3, 25), (2, 4, 18), (3, 5, 18),
                                    (4, 5, 13), (4, 7, 7), (5, 6, 7)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 8, 2: 3, 3: 2, 4: 7, 5: 6, 6: 5, 7: 4, 8: 1})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 8, 2: 3, 3: 2, 4: 7, 5: 6, 6: 5, 7: 4, 8: 1}))
 
     def test_nasty_blossom1(self):
         """Create blossom, relabel as T in more than one way, expand,
@@ -165,9 +166,9 @@ class TestMaxWeightMatching(object):
                                    (3, 4, 45), (4, 5, 50), (1, 6, 30),
                                    (3, 9, 35), (4, 8, 35), (5, 7, 26),
                                    (9, 10, 5)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
-                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
+                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9}))
 
     def test_nasty_blossom2(self):
         """Again but slightly different:"""
@@ -176,9 +177,9 @@ class TestMaxWeightMatching(object):
                                    (3, 4, 45), (4, 5, 50), (1, 6, 30),
                                    (3, 9, 35), (4, 8, 26), (5, 7, 40),
                                    (9, 10, 5)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
-                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
+                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9}))
 
     def test_nasty_blossom_least_slack(self):
         """Create blossom, relabel as T, expand such that a new
@@ -189,9 +190,9 @@ class TestMaxWeightMatching(object):
                                    (3, 4, 45), (4, 5, 50), (1, 6, 30),
                                    (3, 9, 35), (4, 8, 28), (5, 7, 26),
                                    (9, 10, 5)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
-                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 8, 5: 7,
+                      6: 1, 7: 5, 8: 4, 9: 10, 10: 9}))
 
     def test_nasty_blossom_augmenting(self):
         """Create nested blossom, relabel as T in more than one way"""
@@ -203,9 +204,9 @@ class TestMaxWeightMatching(object):
                                    (5, 6, 94), (6, 7, 50), (1, 8, 30),
                                    (3, 11, 35), (5, 9, 36), (7, 10, 26),
                                    (11, 12, 5)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 8, 2: 3, 3: 2, 4: 6, 5: 9, 6: 4,
-                      7: 10, 8: 1, 9: 5, 10: 7, 11: 12, 12: 11})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 8, 2: 3, 3: 2, 4: 6, 5: 9, 6: 4,
+                      7: 10, 8: 1, 9: 5, 10: 7, 11: 12, 12: 11}))
 
     def test_nasty_blossom_expand_recursively(self):
         """Create nested S-blossom, relabel as S, expand recursively:"""
@@ -214,9 +215,9 @@ class TestMaxWeightMatching(object):
                                    (2, 4, 55), (3, 5, 55), (4, 5, 50),
                                    (1, 8, 15), (5, 7, 30), (7, 6, 10),
                                    (8, 10, 10), (4, 9, 30)])
-        assert_equal(nx.max_weight_matching(G),
-                     {1: 2, 2: 1, 3: 5, 4: 9, 5: 3,
-                      6: 7, 7: 6, 8: 10, 9: 4, 10: 8})
+        assert_edges_equal(nx.max_weight_matching(G),
+                     matching_dict_to_set({1: 2, 2: 1, 3: 5, 4: 9, 5: 3,
+                      6: 7, 7: 6, 8: 10, 9: 4, 10: 8}))
 
 
 class TestIsMatching(object):
diff --git a/networkx/drawing/tests/test_agraph.py b/networkx/drawing/tests/test_agraph.py
index 46074d114..9d23a74ea 100644
--- a/networkx/drawing/tests/test_agraph.py
+++ b/networkx/drawing/tests/test_agraph.py
@@ -24,6 +24,7 @@ class TestAGraph(object):
         G.graph['metal'] = 'bronze'
         return G
 
+
     def assert_equal(self, G1, G2):
         assert_nodes_equal(G1.nodes(), G2.nodes())
         assert_edges_equal(G1.edges(), G2.edges())
@@ -79,3 +80,21 @@ class TestAGraph(object):
         G.add_edge(1, 2, weight=7)
         G.add_edge(2, 3, weight=8)
         nx.nx_agraph.view_pygraphviz(G, edgelabel='weight')
+
+    def test_from_agraph_name(self):
+        G = nx.Graph(name='test')
+        A = nx.nx_agraph.to_agraph(G)
+        H = nx.nx_agraph.from_agraph(A)
+        assert_equal(G.name, 'test')
+
+
+    def test_graph_with_reserved_keywords(self):
+        # test attribute/keyword clash case for #1582
+        # node: n
+        # edges: u,v
+        G = nx.Graph()
+        G = self.build_graph(G)
+        G.node['E']['n']='keyword'
+        G.edges[('A','B')]['u']='keyword'
+        G.edges[('A','B')]['v']='keyword'
+        A = nx.nx_agraph.to_agraph(G)