summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Schult <dschult@colgate.edu>2015-09-06 19:02:09 -0400
committerDan Schult <dschult@colgate.edu>2015-09-06 19:02:09 -0400
commit8ccaa9ad25622382e38fe536e18db33edcfbe632 (patch)
treecbc7dc50312432a08921297d549c859f0f6e68bb
parentc92d78917f45a76b2634284e6e3f0f3da8fd2b56 (diff)
parent2caaf086efe3fbead76657ae2dd92e298ccaabed (diff)
downloadnetworkx-v1.10.1.tar.gz
Merge pull request #1760 from dschult/fix-layoutv1.10.1
Fix layout.py and revert changes to default scales.
-rw-r--r--doc/source/reference/drawing.rst1
-rw-r--r--networkx/drawing/layout.py285
-rw-r--r--networkx/drawing/tests/test_layout.py115
3 files changed, 237 insertions, 164 deletions
diff --git a/doc/source/reference/drawing.rst b/doc/source/reference/drawing.rst
index b5a76b7b..41f00aad 100644
--- a/doc/source/reference/drawing.rst
+++ b/doc/source/reference/drawing.rst
@@ -78,6 +78,7 @@ Graph Layout
:toctree: generated/
circular_layout
+ fruchterman_reingold_layout
random_layout
shell_layout
spring_layout
diff --git a/networkx/drawing/layout.py b/networkx/drawing/layout.py
index 5d9cc991..3b93bbde 100644
--- a/networkx/drawing/layout.py
+++ b/networkx/drawing/layout.py
@@ -4,6 +4,11 @@ Layout
******
Node positioning algorithms for graph drawing.
+
+The default scales and centering for these layouts are
+typically squares with side [0, 1] or [0, scale].
+The two circular layout routines (circular_layout and
+shell_layout) have size [-1, 1] or [-scale, scale].
"""
# Copyright (C) 2004-2015 by
# Aric Hagberg <hagberg@lanl.gov>
@@ -13,7 +18,8 @@ Node positioning algorithms for graph drawing.
# BSD license.
import collections
import networkx as nx
-__author__ = """Aric Hagberg (hagberg@lanl.gov)\nDan Schult(dschult@colgate.edu)"""
+__author__ = """\n""".join(['Aric Hagberg <aric.hagberg@gmail.com>',
+ 'Dan Schult(dschult@colgate.edu)'])
__all__ = ['circular_layout',
'random_layout',
'shell_layout',
@@ -21,32 +27,13 @@ __all__ = ['circular_layout',
'spectral_layout',
'fruchterman_reingold_layout']
-def process_params(G, center, dim):
- # Some boilerplate code.
- import numpy as np
-
- if not isinstance(G, nx.Graph):
- empty_graph = nx.Graph()
- empty_graph.add_nodes_from(G)
- G = empty_graph
-
- if center is None:
- center = np.zeros(dim)
- else:
- center = np.asarray(center)
-
- if len(center) != dim:
- msg = "length of center coordinates must match dimension of layout"
- raise ValueError(msg)
-
- return G, center
-
-def random_layout(G, dim=2, center=None):
- """Position nodes uniformly at random in the unit square.
+def random_layout(G, dim=2, scale=1., center=None):
+ """Position nodes uniformly at random.
For every node, a position is generated by choosing each of dim
- coordinates uniformly at random on the interval [0.0, 1.0).
+ coordinates uniformly at random on the default interval [0.0, 1.0),
+ or on an interval of length `scale` centered at `center`.
NumPy (http://scipy.org) is required for this function.
@@ -58,8 +45,11 @@ def random_layout(G, dim=2, center=None):
dim : int
Dimension of layout.
- center : array-like or None
- Coordinate pair around which to center the layout.
+ scale : float (default 1)
+ Scale factor for positions
+
+ center : array-like (default scale*0.5 in each dim)
+ Coordinate around which to center the layout.
Returns
-------
@@ -70,21 +60,17 @@ def random_layout(G, dim=2, center=None):
--------
>>> G = nx.lollipop_graph(4, 3)
>>> pos = nx.random_layout(G)
-
"""
import numpy as np
- G, center = process_params(G, center, dim)
shape = (len(G), dim)
- pos = np.random.random(shape) + center
- pos = pos.astype(np.float32)
- pos = dict(zip(G, pos))
-
- return pos
+ pos = np.random.random(shape) * scale
+ if center is not None:
+ pos += np.asarray(center) - 0.5 * scale
+ return dict(zip(G, pos))
-def circular_layout(G, dim=2, scale=1, center=None):
- # dim=2 only
+def circular_layout(G, dim=2, scale=1., center=None):
"""Position nodes on a circle.
Parameters
@@ -94,11 +80,11 @@ def circular_layout(G, dim=2, scale=1, center=None):
dim : int
Dimension of layout, currently only dim=2 is supported
- scale : float
- Scale factor for positions
+ scale : float (default 1)
+ Scale factor for positions, i.e. radius of circle.
- center : array-like or None
- Coordinate pair around which to center the layout.
+ center : array-like (default origin)
+ Coordinate around which to center the layout.
Returns
-------
@@ -118,23 +104,18 @@ def circular_layout(G, dim=2, scale=1, center=None):
"""
import numpy as np
- G, center = process_params(G, center, dim)
-
if len(G) == 0:
- pos = {}
- elif len(G) == 1:
- pos = {G.nodes()[0]: center}
- else:
- # Discard the extra angle since it matches 0 radians.
- theta = np.linspace(0, 1, len(G) + 1)[:-1] * 2 * np.pi
- theta = theta.astype(np.float32)
- pos = np.column_stack([np.cos(theta), np.sin(theta)])
- pos = _rescale_layout(pos, scale=scale) + center
- pos = dict(zip(G, pos))
+ return {}
- return pos
+ twopi = 2.0*np.pi
+ theta = np.arange(0, twopi, twopi/len(G))
+ pos = np.column_stack([np.cos(theta), np.sin(theta)]) * scale
+ if center is not None:
+ pos += np.asarray(center)
-def shell_layout(G, nlist=None, dim=2, scale=1, center=None):
+ return dict(zip(G, pos))
+
+def shell_layout(G, nlist=None, dim=2, scale=1., center=None):
"""Position nodes in concentric circles.
Parameters
@@ -147,11 +128,11 @@ def shell_layout(G, nlist=None, dim=2, scale=1, center=None):
dim : int
Dimension of layout, currently only dim=2 is supported
- scale : float
- Scale factor for positions
+ scale : float (default 1)
+ Scale factor for positions, i.e.radius of largest shell
- center : array-like or None
- Coordinate pair around which to center the layout.
+ center : array-like (default origin)
+ Coordinate around which to center the layout.
Returns
-------
@@ -172,39 +153,42 @@ def shell_layout(G, nlist=None, dim=2, scale=1, center=None):
"""
import numpy as np
- G, center = process_params(G, center, dim)
-
if len(G) == 0:
return {}
- elif len(G) == 1:
- return {G.nodes()[0]: center}
-
if nlist is None:
# draw the whole graph in one shell
- nlist = [list(G.nodes())]
+ nlist = [list(G)]
+ numb_shells = len(nlist)
if len(nlist[0]) == 1:
# single node at center
radius = 0.0
+ numb_shells -= 1
else:
# else start at r=1
radius = 1.0
+ # distance between shells
+ gap = (scale / numb_shells) if numb_shells else scale
+ radius *= gap
npos={}
+ twopi = 2.0*np.pi
for nodes in nlist:
- # Discard the extra angle since it matches 0 radians.
- theta = np.linspace(0, 1, len(nodes) + 1)[:-1] * 2 * np.pi
- theta = theta.astype(np.float32)
- pos = np.column_stack([np.cos(theta), np.sin(theta)])
- pos = _rescale_layout(pos, scale=scale * radius / len(nlist)) + center
+ theta = np.arange(0, twopi, twopi/len(nodes))
+ pos = np.column_stack([np.cos(theta), np.sin(theta)]) * radius
npos.update(zip(nodes, pos))
- radius += 1.0
+ radius += gap
+
+ if center is not None:
+ center = np.asarray(center)
+ for n,p in npos.items():
+ npos[n] = p + center
return npos
-def fruchterman_reingold_layout(G,dim=2,k=None,
+def fruchterman_reingold_layout(G, dim=2, k=None,
pos=None,
fixed=None,
iterations=50,
@@ -215,7 +199,7 @@ def fruchterman_reingold_layout(G,dim=2,k=None,
Parameters
----------
- G : NetworkX graph or list of nodes
+ G : NetworkX graph
dim : int
Dimension of layout
@@ -225,7 +209,6 @@ def fruchterman_reingold_layout(G,dim=2,k=None,
1/sqrt(n) where n is the number of nodes. Increase this value
to move nodes farther apart.
-
pos : dict or None optional (default=None)
Initial positions for nodes as a dictionary with node as keys
and values as a list or tuple. If None, then use random initial
@@ -233,21 +216,21 @@ def fruchterman_reingold_layout(G,dim=2,k=None,
fixed : list or None optional (default=None)
Nodes to keep fixed at initial position.
+ If any nodes are fixed, the scale and center features are not used.
iterations : int optional (default=50)
Number of iterations of spring-force relaxation
weight : string or None optional (default='weight')
The edge attribute that holds the numerical value used for
- the edge weight. If None, then all edge weights are 1.
+ the effective spring constant. If None, edge weights are 1.
scale : float (default=1.0)
Scale factor for positions. The nodes are positioned
- in a box of size [0,scale] x [0,scale].
-
- center : array-like or None
- Coordinate pair around which to center the layout.
+ in a box of size `scale` in each dim centered at `center`.
+ center : array-like (default scale/2 in each dim)
+ Coordinate around which to center the layout.
Returns
-------
@@ -259,66 +242,62 @@ def fruchterman_reingold_layout(G,dim=2,k=None,
>>> G=nx.path_graph(4)
>>> pos=nx.spring_layout(G)
- # The same using longer function name
+ # this function has two names:
+ # spring_layout and fruchterman_reingold_layout
>>> pos=nx.fruchterman_reingold_layout(G)
"""
import numpy as np
- G, center = process_params(G, center, dim)
+ if len(G) == 0:
+ return {}
if fixed is not None:
nfixed = dict(zip(G, range(len(G))))
fixed = np.asarray([nfixed[v] for v in fixed])
+ if pos is None:
+ msg = "Keyword pos must be specified if any nodes are fixed"
+ raise ValueError(msg)
+
if pos is not None:
# Determine size of existing domain to adjust initial positions
- dom_size = max(flatten(pos.values()))
+ pos_coords = np.array(list(pos.values()))
+ min_coords = pos_coords.min(0)
+ domain_size = pos_coords.max(0) - min_coords
shape = (len(G), dim)
- pos_arr = np.random.random(shape) * dom_size + center
+ pos_arr = np.random.random(shape) * domain_size + min_coords
for i,n in enumerate(G):
if n in pos:
pos_arr[i] = np.asarray(pos[n])
else:
pos_arr=None
- if len(G) == 0:
- return {}
- if len(G) == 1:
- return {G.nodes()[0]: center}
-
+ if k is None and fixed is not None:
+ # Adjust k for domains larger than 1x1
+ k=domain_size.max()/np.sqrt(len(G))
try:
# Sparse matrix
if len(G) < 500: # sparse solver for large graphs
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 that are not near 1x1
- nnodes,_ = A.shape
- k = dom_size / np.sqrt(nnodes)
- pos = _sparse_fruchterman_reingold(A, dim, k, pos_arr, fixed, iterations)
+ A = nx.to_scipy_sparse_matrix(G, weight=weight, dtype='f')
+ pos = _sparse_fruchterman_reingold(A,dim,k,pos_arr,fixed,iterations)
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 that are not near 1x1
- nnodes,_ = A.shape
- k = dom_size / np.sqrt(nnodes)
+ A = nx.to_numpy_matrix(G, weight=weight)
pos = _fruchterman_reingold(A, dim, k, pos_arr, fixed, iterations)
+
if fixed is None:
- pos = _rescale_layout(pos, scale=scale) + center
- pos = dict(zip(G,pos))
- return pos
+ pos = _rescale_layout(pos, scale)
+ if center is not None:
+ pos += np.asarray(center) - 0.5 * scale
+
+ return dict(zip(G,pos))
spring_layout=fruchterman_reingold_layout
-def _fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
- iterations=50):
+def _fruchterman_reingold(A,dim=2,k=None,pos=None,fixed=None,iterations=50):
# Position nodes in adjacency matrix A using Fruchterman-Reingold
# Entry point for NetworkX graph is fruchterman_reingold_layout()
- try:
- import numpy as np
- except ImportError:
- raise ImportError("_fruchterman_reingold() requires numpy: http://scipy.org/ ")
-
+ import numpy as np
try:
nnodes,_=A.shape
except AttributeError:
@@ -327,7 +306,7 @@ def _fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
A=np.asarray(A) # make sure we have an array instead of a matrix
- if pos==None:
+ if pos is None:
# random initial positions
pos=np.asarray(np.random.random((nnodes,dim)),dtype=A.dtype)
else:
@@ -339,8 +318,7 @@ def _fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
k=np.sqrt(1.0/nnodes)
# the initial "temperature" is about .1 of domain area (=1x1)
# this is the largest step allowed in the dynamics.
- # We need to calculate this in case our fixed positions force our domain
- # to be much bigger than 1x1
+ # Calculate domain in case our fixed positions are bigger than 1x1
t = max(max(pos.T[0]) - min(pos.T[0]), max(pos.T[1]) - min(pos.T[1]))*0.1
# simple cooling scheme.
# linearly step down by dt on each iteration so last iteration is size dt.
@@ -363,7 +341,7 @@ def _fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
.sum(axis=1)
# update positions
length=np.sqrt((displacement**2).sum(axis=1))
- length=np.where(length<0.01,0.1,length)
+ length=np.where(length<0.01,0.01,length)
delta_pos=np.transpose(np.transpose(displacement)*t/length)
if fixed is not None:
# don't change positions of fixed nodes
@@ -371,6 +349,8 @@ def _fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
pos+=delta_pos
# cool temperature
t-=dt
+ if fixed is None:
+ pos = _rescale_layout(pos)
return pos
@@ -379,10 +359,7 @@ def _sparse_fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
# Position nodes in adjacency matrix A using Fruchterman-Reingold
# Entry point for NetworkX graph is fruchterman_reingold_layout()
# Sparse version
- try:
- import numpy as np
- except ImportError:
- raise ImportError("_sparse_fruchterman_reingold() requires numpy: http://scipy.org/ ")
+ import numpy as np
try:
nnodes,_=A.shape
except AttributeError:
@@ -398,7 +375,7 @@ def _sparse_fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
except:
A=(coo_matrix(A)).tolil()
- if pos==None:
+ if pos is None:
# random initial positions
pos=np.asarray(np.random.random((nnodes,dim)),dtype=A.dtype)
else:
@@ -406,7 +383,7 @@ def _sparse_fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
pos=pos.astype(A.dtype)
# no fixed nodes
- if fixed==None:
+ if fixed is None:
fixed=[]
# optimal distance between nodes
@@ -414,7 +391,8 @@ def _sparse_fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
k=np.sqrt(1.0/nnodes)
# the initial "temperature" is about .1 of domain area (=1x1)
# this is the largest step allowed in the dynamics.
- t=0.1
+ # Calculate domain in case our fixed positions are bigger than 1x1
+ t = max(max(pos.T[0]) - min(pos.T[0]), max(pos.T[1]) - min(pos.T[1]))*0.1
# simple cooling scheme.
# linearly step down by dt on each iteration so last iteration is size dt.
dt=t/float(iterations+1)
@@ -439,14 +417,16 @@ def _sparse_fruchterman_reingold(A, dim=2, k=None, pos=None, fixed=None,
(delta*(k*k/distance**2-Ai*distance/k)).sum(axis=1)
# update positions
length=np.sqrt((displacement**2).sum(axis=0))
- length=np.where(length<0.01,0.1,length)
+ length=np.where(length<0.01,0.01,length)
pos+=(displacement*t/length).T
# cool temperature
t-=dt
+ if fixed is None:
+ pos = _rescale_layout(pos)
return pos
-def spectral_layout(G, dim=2, weight='weight', scale=1, center=None):
+def spectral_layout(G, dim=2, weight='weight', scale=1., center=None):
"""Position nodes using the eigenvectors of the graph Laplacian.
Parameters
@@ -460,11 +440,12 @@ def spectral_layout(G, dim=2, weight='weight', scale=1, center=None):
The edge attribute that holds the numerical value used for
the edge weight. If None, then all edge weights are 1.
- scale : float
- Scale factor for positions
+ scale : float optional (default 1)
+ Scale factor for positions, i.e. nodes placed in a box with
+ side [0, scale] or centered on `center` if provided.
- center : array-like or None
- Coordinate pair around which to center the layout.
+ center : array-like (default scale/2 in each dim)
+ Coordinate around which to center the layout.
Returns
-------
@@ -487,15 +468,18 @@ def spectral_layout(G, dim=2, weight='weight', scale=1, center=None):
# handle some special cases that break the eigensolvers
import numpy as np
- G, center = process_params(G, center, dim)
-
if len(G) <= 2:
if len(G) == 0:
- pos = np.array([])
+ return {}
elif len(G) == 1:
- pos = np.array([center])
- else:
- pos = np.array([np.zeros(dim), np.array(center)*2.0])
+ if center is not None:
+ pos = np.asarray(center)
+ else:
+ pos = np.ones((1,dim)) * scale * 0.5
+ else: #len(G) == 2
+ pos = np.array([np.zeros(dim), np.ones(dim) * scale])
+ if center is not None:
+ pos += np.asarray(center) - scale * 0.5
return dict(zip(G,pos))
try:
# Sparse matrix
@@ -514,9 +498,11 @@ def spectral_layout(G, dim=2, weight='weight', scale=1, center=None):
A = A + np.transpose(A)
pos = _spectral(A, dim)
- pos = _rescale_layout(pos, scale) + center
- pos = dict(zip(G,pos))
- return pos
+ pos = _rescale_layout(pos, scale)
+ if center is not None:
+ pos += np.asarray(center) - 0.5 * scale
+
+ return dict(zip(G,pos))
def _spectral(A, dim=2):
@@ -578,20 +564,19 @@ def _sparse_spectral(A,dim=2):
return np.real(eigenvectors[:,index])
-def _rescale_layout(pos,scale=1):
- # rescale to (-scale,scale) in all axes
+def _rescale_layout(pos, scale=1.):
+ # rescale to [0, scale) in each axis
- # shift origin to (0,0)
- lim=0 # max coordinate for all axes
- for i in range(pos.shape[1]):
- pos[:,i]-=pos[:,i].mean()
- lim=max(pos[:,i].max(),lim)
- # rescale to (-scale,scale) in all directions, preserves aspect
+ # Find max length over all dimensions
+ maxlim=0
for i in range(pos.shape[1]):
- pos[:,i]*=scale/lim
+ pos[:,i] -= pos[:,i].min() # shift min to zero
+ maxlim = max(maxlim, pos[:,i].max())
+ if maxlim > 0:
+ for i in range(pos.shape[1]):
+ pos[:,i] *= scale / maxlim
return pos
-
# fixture for nose tests
def setup_module(module):
from nose import SkipTest
@@ -603,17 +588,3 @@ def setup_module(module):
import scipy
except:
raise SkipTest("SciPy not available")
-
-def flatten(l):
- try:
- bs = basestring
- except NameError:
- # Py3k
- bs = str
- for el in l:
- if isinstance(el, collections.Iterable) and not isinstance(el, bs):
- for sub in flatten(el):
- yield sub
- else:
- yield el
-
diff --git a/networkx/drawing/tests/test_layout.py b/networkx/drawing/tests/test_layout.py
index bcd6d236..8366d252 100644
--- a/networkx/drawing/tests/test_layout.py
+++ b/networkx/drawing/tests/test_layout.py
@@ -32,14 +32,115 @@ class TestLayout(object):
vpos=nx.shell_layout(G)
def test_smoke_string(self):
- G=self.Gs
- vpos=nx.random_layout(G)
- vpos=nx.circular_layout(G)
- vpos=nx.spring_layout(G)
- vpos=nx.fruchterman_reingold_layout(G)
- vpos=nx.spectral_layout(G)
- vpos=nx.shell_layout(G)
+ G = self.Gs
+ vpos = nx.random_layout(G)
+ vpos = nx.circular_layout(G)
+ vpos = nx.spring_layout(G)
+ vpos = nx.fruchterman_reingold_layout(G)
+ vpos = nx.spectral_layout(G)
+ vpos = nx.shell_layout(G)
+
+ def test_empty_graph(self):
+ G=nx.Graph()
+ vpos = nx.random_layout(G)
+ vpos = nx.circular_layout(G)
+ vpos = nx.spring_layout(G)
+ vpos = nx.fruchterman_reingold_layout(G)
+ vpos = nx.shell_layout(G)
+ vpos = nx.spectral_layout(G)
+ # center arg
+ vpos = nx.random_layout(G, scale=2, center=(4,5))
+ vpos = nx.circular_layout(G, scale=2, center=(4,5))
+ vpos = nx.spring_layout(G, scale=2, center=(4,5))
+ vpos = nx.shell_layout(G, scale=2, center=(4,5))
+ vpos = nx.spectral_layout(G, scale=2, center=(4,5))
+
+ def test_single_node(self):
+ G = nx.Graph()
+ G.add_node(0)
+ vpos = nx.random_layout(G)
+ vpos = nx.circular_layout(G)
+ vpos = nx.spring_layout(G)
+ vpos = nx.fruchterman_reingold_layout(G)
+ vpos = nx.shell_layout(G)
+ vpos = nx.spectral_layout(G)
+ # center arg
+ vpos = nx.random_layout(G, scale=2, center=(4,5))
+ vpos = nx.circular_layout(G, scale=2, center=(4,5))
+ vpos = nx.spring_layout(G, scale=2, center=(4,5))
+ vpos = nx.shell_layout(G, scale=2, center=(4,5))
+ vpos = nx.spectral_layout(G, scale=2, center=(4,5))
+
+ def check_scale_and_center(self, pos, scale, center):
+ center = numpy.array(center)
+ low = center - 0.5 * scale
+ hi = center + 0.5 * scale
+ vpos = numpy.array(list(pos.values()))
+ length = vpos.max(0) - vpos.min(0)
+ assert (length <= scale).all()
+ assert (vpos >= low).all()
+ assert (vpos <= hi).all()
+
+ def test_scale_and_center_arg(self):
+ G = nx.complete_graph(9)
+ G.add_node(9)
+ vpos = nx.random_layout(G, scale=2, center=(4,5))
+ self.check_scale_and_center(vpos, scale=2, center=(4,5))
+ vpos = nx.spring_layout(G, scale=2, center=(4,5))
+ self.check_scale_and_center(vpos, scale=2, center=(4,5))
+ vpos = nx.spectral_layout(G, scale=2, center=(4,5))
+ self.check_scale_and_center(vpos, scale=2, center=(4,5))
+ # circular can have twice as big length
+ vpos = nx.circular_layout(G, scale=2, center=(4,5))
+ self.check_scale_and_center(vpos, scale=2*2, center=(4,5))
+ vpos = nx.shell_layout(G, scale=2, center=(4,5))
+ self.check_scale_and_center(vpos, scale=2*2, center=(4,5))
+
+ # check default center and scale
+ vpos = nx.random_layout(G)
+ self.check_scale_and_center(vpos, scale=1, center=(0.5,0.5))
+ vpos = nx.spring_layout(G)
+ self.check_scale_and_center(vpos, scale=1, center=(0.5,0.5))
+ vpos = nx.spectral_layout(G)
+ self.check_scale_and_center(vpos, scale=1, center=(0.5,0.5))
+ vpos = nx.circular_layout(G)
+ self.check_scale_and_center(vpos, scale=2, center=(0,0))
+ vpos = nx.shell_layout(G)
+ self.check_scale_and_center(vpos, scale=2, center=(0,0))
+
+ def test_shell_layout(self):
+ G = nx.complete_graph(9)
+ shells=[[0], [1,2,3,5], [4,6,7,8]]
+ vpos = nx.shell_layout(G, nlist=shells)
+ vpos = nx.shell_layout(G, nlist=shells, scale=2, center=(3,4))
+ shells=[[0,1,2,3,5], [4,6,7,8]]
+ vpos = nx.shell_layout(G, nlist=shells)
+ vpos = nx.shell_layout(G, nlist=shells, scale=2, center=(3,4))
+
+ def test_spring_args(self):
+ G = nx.complete_graph(9)
+ vpos = nx.spring_layout(G, dim=3)
+ assert_equal(vpos[0].shape, (3,))
+ vpos = nx.spring_layout(G, fixed=[0,1], pos={1:(0,0), 2:(1,1)})
+ vpos = nx.spring_layout(G, k=2, fixed=[0,1], pos={1:(0,0), 2:(1,1)})
+ vpos = nx.spring_layout(G, scale=3, center=(2,5))
+ vpos = nx.spring_layout(G, scale=3)
+ vpos = nx.spring_layout(G, center=(2,5))
+ def test_spectral_for_small_graphs(self):
+ G = nx.Graph()
+ vpos = nx.spectral_layout(G)
+ vpos = nx.spectral_layout(G, center=(2,3))
+ G.add_node(0)
+ vpos = nx.spectral_layout(G)
+ vpos = nx.spectral_layout(G, center=(2,3))
+ G.add_node(1)
+ vpos = nx.spectral_layout(G)
+ vpos = nx.spectral_layout(G, center=(2,3))
+ # 3 nodes should allow eigensolvers to work
+ G.add_node(2)
+ vpos = nx.spectral_layout(G)
+ vpos = nx.spectral_layout(G, center=(2,3))
def test_adjacency_interface_numpy(self):
A=nx.to_numpy_matrix(self.Gs)