@@ -43,7 +43,13 @@ def empty_module(msg='Empty module'):
43
43
#-----------------------------------------------------------------------------
44
44
45
45
class GraphPartition (object ):
46
- """Represent a graph partition."""
46
+ """Represent a graph partition.
47
+
48
+ The main object keeping track of the data is the .index attribute, a dict
49
+ that maps integer module labels to node sets. This dict's labels are
50
+ always assumed to start at 0 and not to point ever to empty modules. If
51
+ empty modules are created during any module manipulations, they will be
52
+ removed from the index and the remaining modules will be relabeled."""
47
53
48
54
def __init__ (self , graph , index ):
49
55
"""New partition, given a graph and a dict of module->nodes.
@@ -346,20 +352,16 @@ def apply_module_split(self, m, n1, n2, split_modules, e_new, a_new):
346
352
#Otherwise need to create a new function to update/recompute mod_e and
347
353
#mod_a.
348
354
349
- # This checks whether there is an empty module. If so, renames the keys.
350
- #if len(self.index[m1])<1:
351
- # self.index.pop(m1)
352
- # rename_keys(self.index,m1)
353
- # This checks whether there is an empty module. If so, renames the keys.
354
- #if len(self.index[m2])<1:
355
- # self.index.pop(m2)
356
- # rename_keys(self.index,m2)
357
-
358
- # For now, rather than renumbering, just check if this ever happens
355
+ # If there are empty modules after the operation, remove them from the
356
+ # index and rename the partition labels
359
357
if len (self .index [m1 ])< 1 :
360
- empty_module ('Empty module after module split, old mod' )
358
+ self .index .pop (m1 )
359
+ rename_keys (self .index , m1 )
360
+ return
361
361
if len (self .index [m2 ])< 1 :
362
- empty_module ('Empty module after module split, new mod' )
362
+ self .index .pop (m2 )
363
+ rename_keys (self .index , m2 )
364
+ return
363
365
364
366
def node_update (self , n , m1 , m2 ):
365
367
"""Moves a single node within or between modules
@@ -448,6 +450,12 @@ def apply_node_update(self, n, m1, m2, node_moved_mods, e_new, a_new):
448
450
The module that n used to belong to.
449
451
m2 : module identifier
450
452
The module that n will now belong to.
453
+ node_moved_mods : tuple
454
+ The two sets of modules for modules m1 and m2.
455
+ e_new : 2-tuple of arrays
456
+ The E arrays for m1 and m2
457
+ a_new : 2-tuple of arrays
458
+ The A arrays for m1 and m2
451
459
452
460
Returns
453
461
-------
@@ -459,18 +467,27 @@ def apply_node_update(self, n, m1, m2, node_moved_mods, e_new, a_new):
459
467
self .index [m1 ] = node_moved_mods [0 ]
460
468
self .index [m2 ] = node_moved_mods [1 ]
461
469
470
+ # If we end up with an empty module, we need to remove it from the
471
+ # partition, and store the information only for the new one.
472
+
462
473
# This checks whether there is an empty module. If so, renames the keys.
463
- if len (self .index [m1 ])< 1 :
464
- empty_module ('Empty module after node move' )
465
- #self.index.pop(m1)
466
- #rename_keys(self.index,m1)
474
+ if len (self .index [m1 ]) < 1 :
475
+ #empty_module('Empty module after node move')
476
+ self .index .pop (m1 )
477
+ rename_keys (self .index , m1 )
478
+ # Once the index structure changes, the labeling of E and A arrays
479
+ # will need to be recomputed (we could propagate the changes
480
+ # throughout, but it's extremely brittle and easy to make a very
481
+ # hard to debug error. Safer to just recompute the arrays in this
482
+ # case).
483
+ self .mod_e , self .mod_a = self ._edge_info ()
467
484
#if m1 < m2:
468
485
# m2 = m2 - 1 #only need to rename this index if m1 is before m2
469
-
470
- self .mod_e [m1 ] = e_new [0 ]
471
- self .mod_a [m1 ] = a_new [0 ]
472
- self .mod_e [m2 ] = e_new [1 ]
473
- self .mod_a [m2 ] = a_new [1 ]
486
+ else :
487
+ self .mod_e [m1 ] = e_new [0 ]
488
+ self .mod_a [m1 ] = a_new [0 ]
489
+ self .mod_e [m2 ] = e_new [1 ]
490
+ self .mod_a [m2 ] = a_new [1 ]
474
491
475
492
return m2
476
493
@@ -1425,9 +1442,13 @@ def adjust_partition(g, partition, max_iter=None):
1425
1442
Partition with higher modularity.
1426
1443
1427
1444
"""
1445
+ # Static copy of the entire list of nodes in the graph
1428
1446
nodes = g .nodes ()
1447
+ # Set of module labels in the initial partition
1429
1448
P = set (range (len (partition )))
1430
1449
1450
+ # Create a dict that maps nodes to the partition label they belong to.
1451
+ # This is effectively a reverse of the partition.index.
1431
1452
node_map = {}
1432
1453
for p in P :
1433
1454
for node in partition .index [p ]:
0 commit comments