Repository: networkx/networkx

Issue:
Construction `complete_bipartite_graph` does not set the 'bipartite' node attribute.
The complete bipartite graph generator does not set the 'bipartite' attribute. This is particularly problematic since the `complete_bipartite_graph` is the fallback method for `bipartite_random_graph(n,m,p)` when `p>=1` and for `bipartite_gnmk_random_graph(n,m,k)` when `k>=n*m`. Therefore the latter graph generators may not set the 'bipartite' attribute, despite what is documented. The obvious fix is to make `complete_bipartite_graph` set the node attribute, too.



Discussion:

Resolving patch:
diff --git a/networkx/generators/bipartite.py b/networkx/generators/bipartite.py
--- a/networkx/generators/bipartite.py
+++ b/networkx/generators/bipartite.py
@@ -24,9 +24,51 @@
          'bipartite_preferential_attachment_graph',
          'bipartite_random_graph',
          'bipartite_gnmk_random_graph',
+         'complete_bipartite_graph',
          ]
 
 
+def complete_bipartite_graph(n1, n2, create_using=None):
+    """Return the complete bipartite graph `K_{n_1,n_2}`.
+
+    Composed of two partitions with `n_1` nodes in the first
+    and `n_2` nodes in the second. Each node in the first is
+    connected to each node in the second.
+
+    Parameters
+    ----------
+    n1 : integer
+       Number of nodes for node set A.
+    n2 : integer
+       Number of nodes for node set B.
+    create_using : NetworkX graph instance, optional
+       Return graph of this type.
+
+    Notes
+    -----
+    Node labels are the integers 0 to `n_1 + n_2 - 1`.
+
+    The nodes are assigned the attribute 'bipartite' with the value 0 or 1
+    to indicate which bipartite set the node belongs to.
+
+    """
+    if create_using is None:
+        G = nx.Graph()
+    else:
+        if create_using.is_directed():
+            raise nx.NetworkXError("Directed Graph not supported")
+        G = create_using
+        G.clear()
+    
+    top = set(range(n1))
+    bottom = set(range(n1, n1+n2))
+    G.add_nodes_from(top, bipartite=1)
+    G.add_nodes_from(bottom, bipartite=0)
+    G.add_edges_from((u, v) for u in top for v in bottom)
+    G.graph['name'] = "complete_bipartite_graph(%d,%d)" % (n1, n2)
+    return G
+
+
 def bipartite_configuration_model(aseq, bseq, create_using=None, seed=None):
     """Return a random bipartite graph from two given degree sequences.
 
diff --git a/networkx/generators/classic.py b/networkx/generators/classic.py
--- a/networkx/generators/classic.py
+++ b/networkx/generators/classic.py
@@ -17,12 +17,12 @@
 #    All rights reserved.
 #    BSD license.
 import itertools
+from networkx.generators.bipartite import complete_bipartite_graph
 __author__ ="""Aric Hagberg (hagberg@lanl.gov)\nPieter Swart (swart@lanl.gov)"""
 
 __all__ = [ 'balanced_tree',
             'barbell_graph',
             'complete_graph',
-            'complete_bipartite_graph',
             'circular_ladder_graph',
             'cycle_graph',
             'dorogovtsev_goltsev_mendes_graph',
@@ -192,25 +192,6 @@ def complete_graph(n,create_using=None):
     return G
 
 
-def complete_bipartite_graph(n1,n2,create_using=None):
-    """Return the complete bipartite graph K_{n1_n2}.
-
-    Composed of two partitions with n1 nodes in the first
-    and n2 nodes in the second. Each node in the first is
-    connected to each node in the second.
-
-    Node labels are the integers 0 to n1+n2-1
-
-    """
-    if create_using is not None and create_using.is_directed():
-        raise nx.NetworkXError("Directed Graph not supported")
-    G=empty_graph(n1+n2,create_using)
-    G.name="complete_bipartite_graph(%d,%d)"%(n1,n2)
-    for v1 in range(n1):
-        for v2 in range(n2):
-            G.add_edge(v1,n1+v2)
-    return G
-
 def circular_ladder_graph(n,create_using=None):
     """Return the circular ladder graph CL_n of length n.
 
@@ -500,6 +481,8 @@ def wheel_graph(n,create_using=None):
    Node labels are the integers 0 to n - 1.
 
     """
+    if n == 0:
+        return nx.empty_graph(n, create_using=create_using)
     G=star_graph(n-1,create_using)
     G.name="wheel_graph(%d)"%n
     G.add_edges_from([(v,v+1) for v in range(1,n-1)])


Test patch:
diff --git a/networkx/generators/tests/test_bipartite.py b/networkx/generators/tests/test_bipartite.py
--- a/networkx/generators/tests/test_bipartite.py
+++ b/networkx/generators/tests/test_bipartite.py
@@ -9,6 +9,38 @@
 """
 
 class TestGeneratorsBipartite():
+    def test_complete_bipartite_graph(self):
+        G=complete_bipartite_graph(0,0)
+        assert_true(is_isomorphic( G, null_graph() ))
+
+        for i in [1, 5]:
+            G=complete_bipartite_graph(i,0)
+            assert_true(is_isomorphic( G, empty_graph(i) ))
+            G=complete_bipartite_graph(0,i)
+            assert_true(is_isomorphic( G, empty_graph(i) ))
+
+        G=complete_bipartite_graph(2,2)
+        assert_true(is_isomorphic( G, cycle_graph(4) ))
+
+        G=complete_bipartite_graph(1,5)
+        assert_true(is_isomorphic( G, star_graph(5) ))
+
+        G=complete_bipartite_graph(5,1)
+        assert_true(is_isomorphic( G, star_graph(5) ))
+
+        # complete_bipartite_graph(m1,m2) is a connected graph with
+        # m1+m2 nodes and  m1*m2 edges
+        for m1, m2 in [(5, 11), (7, 3)]:
+            G=complete_bipartite_graph(m1,m2)
+            assert_equal(number_of_nodes(G), m1 + m2)
+            assert_equal(number_of_edges(G), m1 * m2)
+
+        assert_raises(networkx.exception.NetworkXError,
+                      complete_bipartite_graph, 7, 3, create_using=DiGraph())
+
+        mG=complete_bipartite_graph(7, 3, create_using=MultiGraph())
+        assert_equal(mG.edges(), G.edges())
+
     def test_configuration_model(self):
         aseq=[3,3,3,3]
         bseq=[2,2,2,2,2]
diff --git a/networkx/generators/tests/test_classic.py b/networkx/generators/tests/test_classic.py
--- a/networkx/generators/tests/test_classic.py
+++ b/networkx/generators/tests/test_classic.py
@@ -136,38 +136,6 @@ def test_complete_digraph(self):
             assert_true(number_of_nodes(g) == m)
             assert_true(number_of_edges(g) == m * (m - 1))
 
-    def test_complete_bipartite_graph(self):
-        G=complete_bipartite_graph(0,0)
-        assert_true(is_isomorphic( G, null_graph() ))
-
-        for i in [1, 5]:
-            G=complete_bipartite_graph(i,0)
-            assert_true(is_isomorphic( G, empty_graph(i) ))
-            G=complete_bipartite_graph(0,i)
-            assert_true(is_isomorphic( G, empty_graph(i) ))
-
-        G=complete_bipartite_graph(2,2)
-        assert_true(is_isomorphic( G, cycle_graph(4) ))
-
-        G=complete_bipartite_graph(1,5)
-        assert_true(is_isomorphic( G, star_graph(5) ))
-
-        G=complete_bipartite_graph(5,1)
-        assert_true(is_isomorphic( G, star_graph(5) ))
-
-        # complete_bipartite_graph(m1,m2) is a connected graph with
-        # m1+m2 nodes and  m1*m2 edges
-        for m1, m2 in [(5, 11), (7, 3)]:
-            G=complete_bipartite_graph(m1,m2)
-            assert_equal(number_of_nodes(G), m1 + m2)
-            assert_equal(number_of_edges(G), m1 * m2)
-
-        assert_raises(networkx.exception.NetworkXError,
-                      complete_bipartite_graph, 7, 3, create_using=DiGraph())
-
-        mG=complete_bipartite_graph(7, 3, create_using=MultiGraph())
-        assert_equal(mG.edges(), G.edges())
-
     def test_circular_ladder_graph(self):
         G=circular_ladder_graph(5)
         assert_raises(networkx.exception.NetworkXError, circular_ladder_graph,