summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinas Gjoka <minas.gjoka@gmail.com>2016-02-25 17:22:18 -0800
committerMinas Gjoka <minas.gjoka@gmail.com>2016-02-25 17:22:18 -0800
commit3696a660ef5a18148a1aa6b8842eb22a0e7d1cf9 (patch)
tree02133988ca923bd07b11e236e02f060149e7e9b9
parent75f40a539cc01baf451ef3e93aa50464f1c2386d (diff)
downloadnetworkx-3696a660ef5a18148a1aa6b8842eb22a0e7d1cf9.tar.gz
Handled white space characters.
-rw-r--r--networkx/generators/joint_degree_seq.py200
-rw-r--r--networkx/generators/tests/test_joint_degree_seq.py69
2 files changed, 132 insertions, 137 deletions
diff --git a/networkx/generators/joint_degree_seq.py b/networkx/generators/joint_degree_seq.py
index 7405d476..221a691b 100644
--- a/networkx/generators/joint_degree_seq.py
+++ b/networkx/generators/joint_degree_seq.py
@@ -19,7 +19,7 @@ def is_valid_joint_degree(nkk):
""" Checks whether the given joint degree dictionary (*nkk*) is realizable
as a simple graph by evaluating five necessary and sufficient
conditions.
-
+
Here is the list of five conditions that an input nkk needs to satisfy for
simple graph realizability:
- Condition 1: nkk[k][l] is integer for all k,l
@@ -30,7 +30,7 @@ def is_valid_joint_degree(nkk):
- Condition 5: nkk[k][k] is an even integer i.e. stubs are counted
twice for equal degree pairs. this is an assumption that
the method joint_degree_model expects to be true.
-
+
Parameters
----------
nkk : dictionary of dictionary of integers
@@ -42,106 +42,101 @@ def is_valid_joint_degree(nkk):
boolean:
returns true if given joint degree dictionary is realizable, else
returns false.
-
+
References
----------
.. [1] M. Gjoka, M. Kurant, A. Markopoulou, "2.5K Graphs: from Sampling
to Generation", IEEE Infocom, 2013.
-
"""
-
-
+
nk = {}
for k in nkk:
- if k>0:
+ if k > 0:
k_size = sum(nkk[k].values()) / k
if not k_size.is_integer(): # condition 2
return False
nk[k] = k_size
-
-
+
for k in nkk:
for l in nkk[k]:
- if not float(nkk[k][l]).is_integer(): # condition 1
- return False
-
- if (k!=l) and ( nkk[k][l] > nk[k]*nk[l] ): #condition 3
+ if not float(nkk[k][l]).is_integer(): # condition 1
return False
- elif (k==l):
- if ( nkk[k][k] > nk[k]*(nk[k]-1) ): # condition 4
+
+ if (k != l) and (nkk[k][l] > nk[k]*nk[l]): #condition 3
+ return False
+ elif k == l:
+ if nkk[k][k] > nk[k]*(nk[k]-1): # condition 4
+ return False
+ if nkk[k][k] % 2 != 0: # condition 5
return False
- if (nkk[k][k] % 2 != 0): # condition 5
- return False
-
-
- # if all five above conditions have been satisfied then the input nkk is
- # realizable as a simple graph.
+
+
+ # if all five above conditions have been satisfied then the input nkk is
+ # realizable as a simple graph.
return True
-def _neighbor_switch(G, w, unsat, h_node_residual, avoid_node_id=None):
+def _neighbor_switch(G, w, unsat, h_node_residual, avoid_node_id=None):
""" neighbor_switch releases one free stub for node *w*, while preserving
joint degree in G. First, it selects node w_prime that (1) has the same
degree as *w* and (2) is unsaturated. Then, it selects node t, a neighbor
of *w*, that is not connected to w_prime and does an edge swap i.e. removes
(*w*,t) and adds (w_prime,t). Gjoka et. al. [1] prove that if (1) and (2)
are true then such an edge swap is always possible.
-
Parameters
----------
- G : networkx undirected graph
+ G : networkx undirected graph
graph within which the edge swap will take place
w : integer
- node id for which we need to perform a neighbor switch
+ node id for which we need to perform a neighbor switch
unsat: set of integers
set of node ids that have the same degree as w and are unsaturated
h_node_residual: dict of integers
for a given node, keeps track of the remaining stubs to be added.
avoid_node_id: integer
node id to avoid when selecting w_prime. used for a rare edge case.
-
-
+
References
----------
.. [1] M. Gjoka, B. Tillman, A. Markopoulou, "Construction of Simple
Graphs with a Target Joint Degree Matrix and Beyond", IEEE Infocom, '15
-
"""
-
- if avoid_node_id==None or h_node_residual[avoid_node_id]>1:
- # select node w_prime that has the same degree as w and is unsatured
+
+ if (avoid_node_id == None) or (h_node_residual[avoid_node_id] > 1):
+ # select node w_prime that has the same degree as w and is unsatured
w_prime = next(iter(unsat))
else:
- # assume that inside method joint_degree_model the node pair (v,w) is a candidate
- # for connection (v=avoid_node_id). if neighbor_switch is called for node w inside method
- # joint_degree_model and (1) candidate nodes v=avoid_node_id and w have the same degree i.e.
- # degree(v)=degree(w) and (2) node v=avoid_node_id is a potential candidate for w_prime
- # but has only one stub left i.e. h_node_residual[v]==1, then prevent v from
- # being selected as w_prime. This is a rare edge case.
-
- iter_var = iter(unsat)
+ # assume that inside method joint_degree_model the node pair (v,w) is a
+ # candidate for connection (v=avoid_node_id). if neighbor_switch is
+ # called for node w inside method joint_degree_model and (1) candidate
+ # nodes v=avoid_node_id and w have the same degree i.e.
+ # degree(v)=degree(w) and (2) node v=avoid_node_id is a potential
+ # candidate for w_prime but has only one stub left i.e.
+ # h_node_residual[v]==1, then prevent v from being selected as w_prime.
+ # This is a rare edge case.
+
+ iter_var = iter(unsat)
while True:
w_prime = next(iter_var)
if w_prime != avoid_node_id:
break
-
+
# select node t, a neighbor of w, that is not connected to w_prime
w_prime_neighbs = G[w_prime] # slightly faster declaring this variable
- for v in G[w]:
- if (v not in w_prime_neighbs) and (v!=w_prime):
+ for v in G[w]:
+ if (v not in w_prime_neighbs) and (v != w_prime):
t = v
break
-
+
# removes (w,t), add (w_prime,t) and update data structures
G.remove_edge(w, t)
G.add_edge(w_prime, t)
- h_node_residual[w] += 1
- h_node_residual[w_prime] -= 1
+ h_node_residual[w] += 1
+ h_node_residual[w_prime] -= 1
if h_node_residual[w_prime] == 0:
unsat.remove(w_prime)
-
-def joint_degree_model(nkk,seed=None):
+def joint_degree_model(nkk, seed=None):
"""Return a random simple graph with the given joint degree dict (*nkk*).
Parameters
@@ -170,14 +165,14 @@ def joint_degree_model(nkk,seed=None):
not reached its target yet i.e. (for given k,l): n_edges_add < nkk[k][l].
It then adds edge (v,w) and always increases the number of edges in graph
G by one.
-
+
The intelligence of the algorithm lies in the fact that it is always
possible to add an edge between disconnected nodes v and w, for which
nkk[degree(v)][degree(w)] has not reached its target, even if one or both
nodes do not have free stubs. If either node v or w does not have a free
stub, we perform a "neighbor switch", an edge rewiring move that releases
a free stub while keeping *nkk* the same.
-
+
The algorithm continues for E (number of edges in the graph) iterations of
the "while loop", at the which point all entries of the given nkk[k][l]
have reached their target values and the construction is complete.
@@ -196,9 +191,8 @@ def joint_degree_model(nkk,seed=None):
... 4: {1: 1, 2: 2, 3: 1}}
>>> G=nx.joint_degree_model(joint_degree_dict)
>>>
-
"""
-
+
if not is_valid_joint_degree(nkk):
msg = 'Input joint degree dict not realizable as a simple graph'
raise nx.NetworkXError(msg)
@@ -207,86 +201,88 @@ def joint_degree_model(nkk,seed=None):
random.seed(seed)
# compute degree vector from joint degree nkk
- nk = { k: sum(l.values())//k for k,l in nkk.items() if k>0 }
-
+ nk = {k: sum(l.values())//k for k, l in nkk.items() if k > 0}
+
# start with empty N-node graph
N = sum(nk.values())
G = nx.empty_graph(N)
-
-
- # for a given degree group, keep the list of all node ids
+
+
+ # for a given degree group, keep the list of all node ids
h_degree_nodelist = {}
-
- # for a given node, keep track of the remaining stubs to be added.
+
+ # for a given node, keep track of the remaining stubs to be added.
h_node_residual = {}
-
+
# populate h_degree_nodelist and h_node_residual
- nodeid = 0
+ nodeid = 0
for degree, num_nodes in nk.items():
h_degree_nodelist[degree] = range(nodeid, nodeid+num_nodes)
for v in h_degree_nodelist[degree]:
- h_node_residual[v] = degree
+ h_node_residual[v] = degree
nodeid += int(num_nodes)
-
- # iterate over every degree pair (k,l) and add the number of edges given for each pair
+
+ # iterate over every degree pair (k,l) and add the number of edges given
+ # for each pair
for k in nkk:
for l in nkk[k]:
-
- # n_edges_add is the number of edges to add for the degree pair (k,l)
- n_edges_add = nkk[k][l]
-
- if (n_edges_add>0) and (k>=l):
+
+ # n_edges_add is the number of edges to add for the
+ # degree pair (k,l)
+ n_edges_add = nkk[k][l]
+
+ if (n_edges_add > 0) and (k >= l):
# number of nodes with degree k and l
k_size = nk[k]
- l_size = nk[l]
-
- # k_nodes and l_nodes consist of all nodes of degree k and l respectivelyu
+ l_size = nk[l]
+
+ # k_nodes and l_nodes consist of all nodes of degree k and l
k_nodes = h_degree_nodelist[k]
l_nodes = h_degree_nodelist[l]
- # k_unsat and l_unsat consist of nodes of degree k and l that are unsaturated
- # i.e. those nodes that have at least one available stub
- k_unsat = set(v for v in k_nodes if h_node_residual[v]>0)
-
- if k!=l:
- l_unsat = set(w for w in l_nodes if h_node_residual[w]>0)
+ # k_unsat and l_unsat consist of nodes of degree k and l that
+ # are unsaturated i.e. nodes that have at least 1 available stub
+ k_unsat = set(v for v in k_nodes if h_node_residual[v] > 0)
+
+ if k != l:
+ l_unsat = set(w for w in l_nodes if h_node_residual[w] > 0)
else:
l_unsat = k_unsat
n_edges_add = nkk[k][l]//2
-
+
while n_edges_add > 0:
-
+
# randomly pick nodes v and w that have degrees k and l
- v = k_nodes[ random.randrange(k_size) ]
- w = l_nodes[ random.randrange(l_size) ]
-
+ v = k_nodes[random.randrange(k_size)]
+ w = l_nodes[random.randrange(l_size)]
+
# if nodes v and w are disconnected then attempt to connect
- if not G.has_edge(v,w) and (v!=w):
-
+ if not G.has_edge(v, w) and (v != w):
+
# if node v has no free stubs then do neighbor switch
- if h_node_residual[v]==0:
- _neighbor_switch(G, v, k_unsat, h_node_residual)
-
+ if h_node_residual[v] == 0:
+ _neighbor_switch(G, v, k_unsat, h_node_residual)
+
# if node w has no free stubs then do neighbor switch
- if h_node_residual[w]==0:
- if k!=l:
- _neighbor_switch(G, w, l_unsat, h_node_residual)
+ if h_node_residual[w] == 0:
+ if k != l:
+ _neighbor_switch(G, w, l_unsat, h_node_residual)
else:
- _neighbor_switch(G, w, l_unsat, h_node_residual, avoid_node_id=v)
-
- # add edge (v,w) and update data structures
- G.add_edge(v,w)
+ _neighbor_switch(G, w, l_unsat, h_node_residual,\
+ avoid_node_id=v)
+
+ # add edge (v, w) and update data structures
+ G.add_edge(v, w)
h_node_residual[v] -= 1
- h_node_residual[w] -= 1
- n_edges_add -= 1
-
+ h_node_residual[w] -= 1
+ n_edges_add -= 1
+
if h_node_residual[v] == 0:
k_unsat.discard(v)
if h_node_residual[w] == 0:
- l_unsat.discard(w)
-
-
- G.name="joint_degree_model %d nodes"%(G.order())
- return G
+ l_unsat.discard(w)
+
+ G.name = "joint_degree_model %d nodes"%(G.order())
+ return G
diff --git a/networkx/generators/tests/test_joint_degree_seq.py b/networkx/generators/tests/test_joint_degree_seq.py
index d6b8b0ff..0e4b3098 100644
--- a/networkx/generators/tests/test_joint_degree_seq.py
+++ b/networkx/generators/tests/test_joint_degree_seq.py
@@ -7,51 +7,50 @@ from networkx.generators import powerlaw_cluster_graph
def test_is_valid_joint_degree():
# valid joint degree that satisfies all five conditions
nkk = {1: {4: 1},
- 2: {2: 2, 3: 2, 4: 2},
- 3: {2: 2, 4: 1},
- 4: {1: 1, 2: 2, 3: 1}
- }
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 1, 2: 2, 3: 1}}
assert_true(is_valid_joint_degree(nkk))
-
+
# condition 1
nkk_1 = {1: {4: 1.5}, # nkk[1][4] not integer
- 2: {2: 2, 3: 2, 4: 2},
- 3: {2: 2, 4: 1},
- 4: {1: 1.5, 2: 2, 3: 1}
- }
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 1.5, 2: 2, 3: 1}}
assert_false(is_valid_joint_degree(nkk_1))
-
+
# condition 2
nkk_2 = {1: {4: 1},
- 2: {2: 2, 3: 2, 4: 3}, # nk[2] = sum(nkk[2][j)/2, is not an intger
- 3: {2: 2, 4: 1},
- 4: {1: 1, 2: 3, 3: 1} # nk[4] = sum(nkk[4][j)/4, is not an integer
- }
- assert_false(is_valid_joint_degree(nkk_2))
-
+ 2: {2: 2, 3: 2, 4: 3}, # nk[2] = sum(nkk[2][j)/2, is not an int
+ 3: {2: 2, 4: 1},
+ 4: {1: 1, 2: 3, 3: 1}} # nk[4] = sum(nkk[4][j)/4, is not an int
+ assert_false(is_valid_joint_degree(nkk_2))
+
# condition 3 and 4
nkk_3 = {1: {4: 2}, #nkk[1][4]>nk[1][nk[4] (not possible)
- 2: {2: 2, 3: 2, 4: 2},
- 3: {2: 2, 4: 1},
- 4: {1: 2, 2: 2, 3: 1}
- }
-
+ 2: {2: 2, 3: 2, 4: 2},
+ 3: {2: 2, 4: 1},
+ 4: {1: 2, 2: 2, 3: 1}}
+ assert_false(is_valid_joint_degree(nkk_3))
+
# condition 5
- nkk_5 = {1:{1:9}} # nkk[1][1] not even
- assert_false(is_valid_joint_degree(nkk_5))
-
-def test_joint_degree_model(ntimes = 100):
- for i in range(ntimes):
- seed = time.time()
-
- n, m, p = 20, 10, 1
- # generate random graph with model powerlaw_cluster and calculate its joint degree
+ nkk_5 = {1:{1:9}} # nkk[1][1] not even
+ assert_false(is_valid_joint_degree(nkk_5))
+
+def test_joint_degree_model(ntimes=100):
+ for _ in range(ntimes):
+ seed = time.time()
+
+ n, m, p = 20, 10, 1
+ # generate random graph with model powerlaw_cluster and calculate
+ # its joint degree
g = powerlaw_cluster_graph(n, m, p, seed=seed)
- nkk_g = degree_mixing_dict(g,normalized=False)
-
+ nkk_g = degree_mixing_dict(g, normalized=False)
+
# generate simple undirected graph with given joint degree nkk_g
G = joint_degree_model(nkk_g)
- nkk_G = degree_mixing_dict(G,normalized=False)
-
- #assert that the given joint degree is equal to the generated graph's joint degree
+ nkk_G = degree_mixing_dict(G, normalized=False)
+
+ # assert that the given joint degree is equal to the generated
+ # graph's joint degree
assert_true(nkk_g == nkk_G)