Repository: networkx/networkx

Issue:
kamada_kawai_layout fails with dim=1
`nx.kamada_kawai_layout(G, dim=1)` Fails when called without initializations through `pos` argument.
This is cased by the default initialization which calls [circular_layout](https://github.com/networkx/networkx/blob/27dd93dda901171631fba92dc02a521cfc9e057e/networkx/drawing/layout.py#L667).

But `circular_layout()` ignores parameter `dim=1` and overwrites it with 2 without any notification. 
`circular_layout` should throw an exception if it cannot handle `dim=1`.

```
import networkx as nx

def create_bipartite_graph(n, m):
    assert n <= m
    dist_matrix = np.ones((n+m, n+m), dtype=float) * 255
    matches = n + np.random.permutation(m)[:n]
   
    dist_matrix[np.arange(n), matches] = 75
    dist_matrix[matches, np.arange(n)] = 75
    
    G = nx.Graph()
    
    edges = (dist_matrix <= 75).nonzero()
    dists = dist_matrix[edges]
    edges = (edges[0], edges[1], 
             [{'weight': x} for x in dists])
    edges = zip(*edges)
    G.add_edges_from(edges)
    return G

G = create_bipartite_graph(10, 10)
pos = nx.kamada_kawai_layout(G, dim=1) # THIS FAILS
pos = nx.kamada_kawai_layout(G, dim=1, pos=np.random.rand(20)) # THIS WORKS
```



Discussion:
Thanks for this. Seems like a good suggestion for fix: circular_layout should raise an exception for ```dim``` values it can't handle.

Resolving patch:
diff --git a/networkx/drawing/layout.py b/networkx/drawing/layout.py
--- a/networkx/drawing/layout.py
+++ b/networkx/drawing/layout.py
@@ -130,12 +130,18 @@ def circular_layout(G, scale=1, center=None, dim=2):
         Dimension of layout.
         If dim>2, the remaining dimensions are set to zero
         in the returned positions.
+        If dim<2, a ValueError is raised.
 
     Returns
     -------
     pos : dict
         A dictionary of positions keyed by node
 
+    Raises
+    -------
+    ValueError
+        If dim < 2
+
     Examples
     --------
     >>> G = nx.path_graph(4)
@@ -149,6 +155,9 @@ def circular_layout(G, scale=1, center=None, dim=2):
     """
     import numpy as np
 
+    if dim < 2:
+        raise ValueError('cannot handle dimensions < 2')
+
     G, center = _process_params(G, center, dim)
 
     paddims = max(0, (dim - 2))
@@ -188,12 +197,18 @@ def shell_layout(G, nlist=None, scale=1, center=None, dim=2):
 
     dim : int
         Dimension of layout, currently only dim=2 is supported.
+        Other dimension values result in a ValueError.
 
     Returns
     -------
     pos : dict
         A dictionary of positions keyed by node
 
+    Raises
+    -------
+    ValueError
+        If dim != 2
+
     Examples
     --------
     >>> G = nx.path_graph(4)
@@ -208,6 +223,9 @@ def shell_layout(G, nlist=None, scale=1, center=None, dim=2):
     """
     import numpy as np
 
+    if dim != 2:
+        raise ValueError('can only handle 2 dimensions')
+
     G, center = _process_params(G, center, dim)
 
     if len(G) == 0:
@@ -621,7 +639,7 @@ def kamada_kawai_layout(G, dist=None,
     pos : dict or None  optional (default=None)
         Initial positions for nodes as a dictionary with node as keys
         and values as a coordinate list or tuple.  If None, then use
-        circular_layout().
+        circular_layout() for dim >= 2 and a linear layout for dim == 1.
 
     weight : string or None   optional (default='weight')
         The edge attribute that holds the numerical value used for
@@ -664,7 +682,10 @@ def kamada_kawai_layout(G, dist=None,
             dist_mtx[row][col] = rdist[nc]
 
     if pos is None:
-        pos = circular_layout(G, dim=dim)
+        if dim >= 2:
+            pos = circular_layout(G, dim=dim)
+        else:
+            pos = {n: pt for n, pt in zip(G, np.linspace(0, 1, len(G)))}
     pos_arr = np.array([pos[n] for n in G])
 
     pos = _kamada_kawai_solve(dist_mtx, pos_arr, dim)


Test patch:
diff --git a/networkx/drawing/tests/test_layout.py b/networkx/drawing/tests/test_layout.py
--- a/networkx/drawing/tests/test_layout.py
+++ b/networkx/drawing/tests/test_layout.py
@@ -65,6 +65,7 @@ def test_smoke_int(self):
         vpos = nx.shell_layout(G)
         if self.scipy is not None:
             vpos = nx.kamada_kawai_layout(G)
+            vpos = nx.kamada_kawai_layout(G, dim=1)
 
     def test_smoke_string(self):
         G = self.Gs
@@ -76,6 +77,7 @@ def test_smoke_string(self):
         vpos = nx.shell_layout(G)
         if self.scipy is not None:
             vpos = nx.kamada_kawai_layout(G)
+            vpos = nx.kamada_kawai_layout(G, dim=1)
 
     def check_scale_and_center(self, pos, scale, center):
         center = numpy.array(center)
@@ -114,6 +116,12 @@ def test_default_scale_and_center(self):
         if self.scipy is not None:
             sc(nx.kamada_kawai_layout(G), scale=1, center=c)
 
+    def test_circular_and_shell_dim_error(self):
+        G = nx.path_graph(4)
+        assert_raises(ValueError, nx.circular_layout, G, dim=1)
+        assert_raises(ValueError, nx.shell_layout, G, dim=1)
+        assert_raises(ValueError, nx.shell_layout, G, dim=3)
+
     def test_adjacency_interface_numpy(self):
         A = nx.to_numpy_matrix(self.Gs)
         pos = nx.drawing.layout._fruchterman_reingold(A)