def test_move_nodes(self): G = ig.Graph.Full(100); partition = louvain.CPMVertexPartition(G, resolution_parameter=0.5); self.optimiser.move_nodes(partition, consider_comms=louvain.ALL_NEIGH_COMMS); self.assertListEqual( partition.sizes(), [100], msg="CPMVertexPartition(resolution_parameter=0.5) of complete graph after move nodes incorrect.");
def multilayer_louvain(G_intralayer, G_interlayer, layer_vec, gamma, omega, optimiser=None, return_partition=False): # RBConfigurationVertexPartitionWeightedLayers implements a multilayer version of "standard" modularity (i.e. # the Reichardt and Bornholdt's Potts model with configuration null model). check_multilayer_louvain_capabilities() if 'weight' not in G_intralayer.es: G_intralayer.es['weight'] = [1.0] * G_intralayer.ecount() if 'weight' not in G_interlayer.es: G_interlayer.es['weight'] = [1.0] * G_interlayer.ecount() if optimiser is None: optimiser = louvain.Optimiser() intralayer_part = louvain.RBConfigurationVertexPartitionWeightedLayers( G_intralayer, layer_vec=layer_vec, weights='weight', resolution_parameter=gamma) interlayer_part = louvain.CPMVertexPartition(G_interlayer, resolution_parameter=0.0, weights='weight') optimiser.optimise_partition_multiplex([intralayer_part, interlayer_part], layer_weights=[1, omega]) if return_partition: return intralayer_part else: return tuple(intralayer_part.membership)
def test_optimiser(self): G = reduce(ig.Graph.disjoint_union, (ig.Graph.Tree(10, 3, mode=ig.TREE_UNDIRECTED) for i in range(10))); partition = louvain.CPMVertexPartition(G, resolution_parameter=0); self.optimiser.consider_comms=louvain.ALL_NEIGH_COMMS; self.optimiser.optimise_partition(partition); self.assertListEqual( partition.sizes(), 10*[10], msg="After optimising partition failed to find different components with CPMVertexPartition(resolution_parameter=0)");
def test_neg_weight_bipartite(self): G = ig.Graph.Full_Bipartite(50, 50); G.es['weight'] = -0.5; partition = louvain.CPMVertexPartition(G, resolution_parameter=-0.5, weights='weight'); self.optimiser.consider_comms=louvain.ALL_COMMS; self.optimiser.optimise_partition(partition); self.assertListEqual( partition.sizes(), 2*[50], msg="After optimising partition failed to find bipartite structure with CPMVertexPartition(resolution_parameter=-0.1)");
def multilayer_louvain(G_intralayer, G_interlayer, layer_vec, gamma, omega, optimiser=None, return_partition=False): r"""Run the Louvain modularity maximization algorithm at a single (:math:`\gamma, \omega`) value. :param G_intralayer: intralayer graph of interest :type G_intralayer: igraph.Graph :param G_interlayer: interlayer graph of interest :type G_interlayer: igraph.Graph :param layer_vec: list of each vertex's layer membership :type layer_vec: list[int] :param gamma: gamma (intralayer resolution parameter) to run Louvain at :type gamma: float :param omega: omega (interlayer resolution parameter) to run Louvain at :type omega: float :param optimiser: if not None, use passed-in (potentially custom) louvain optimiser :type optimiser: louvain.Optimiser :param return_partition: if True, return a louvain partition. Otherwise, return a community membership tuple :type return_partition: bool :return: partition from louvain :rtype: tuple[int] or louvain.RBConfigurationVertexPartitionWeightedLayers """ # RBConfigurationVertexPartitionWeightedLayers implements a multilayer version of "standard" modularity (i.e. # the Reichardt and Bornholdt's Potts model with configuration null model). check_multilayer_louvain_capabilities() if 'weight' not in G_intralayer.es: G_intralayer.es['weight'] = [1.0] * G_intralayer.ecount() if 'weight' not in G_interlayer.es: G_interlayer.es['weight'] = [1.0] * G_interlayer.ecount() if optimiser is None: optimiser = louvain.Optimiser() intralayer_part = louvain.RBConfigurationVertexPartitionWeightedLayers( G_intralayer, layer_vec=layer_vec, weights='weight', resolution_parameter=gamma) interlayer_part = louvain.CPMVertexPartition(G_interlayer, resolution_parameter=0.0, weights='weight') optimiser.optimise_partition_multiplex([intralayer_part, interlayer_part], layer_weights=[1, omega]) if return_partition: return intralayer_part else: return tuple(intralayer_part.membership)
def test_diff_move_node_optimality(self): G = ig.Graph.Erdos_Renyi(100, p=5./100, directed=False, loops=False); partition = louvain.CPMVertexPartition(G, resolution_parameter=0.1); while 0 < self.optimiser.move_nodes(partition, consider_comms=louvain.ALL_NEIGH_COMMS): pass; for v in G.vs: neigh_comms = set(partition.membership[u.index] for u in v.neighbors()); for c in neigh_comms: self.assertLessEqual( partition.diff_move(v.index, c), 1e-10, # Allow for a small difference up to rounding error. msg="Was able to move a node to a better community, violating node optimality.");
def maximize_modularity(intralayer_resolution, interlayer_resolution): # RBConfigurationVertexPartitionWeightedLayers implements a multilayer version of "standard" modularity (i.e. # the Reichardt and Bornholdt's Potts model with configuration null model). G_interlayer.es['weight'] = interlayer_resolution intralayer_part = \ louvain.RBConfigurationVertexPartitionWeightedLayers(G_intralayer, layer_vec=layer_vec, weights='weight', resolution_parameter=intralayer_resolution) interlayer_part = louvain.CPMVertexPartition(G_interlayer, resolution_parameter=0.0, weights='weight') optimiser.optimise_partition_multiplex( [intralayer_part, interlayer_part]) return intralayer_part
def multilayer_louvain_part(G_intralayer, G_interlayer, layer_membership): if 'weight' not in G_intralayer.es: G_intralayer.es['weight'] = [1.0] * G_intralayer.ecount() if 'weight' not in G_interlayer.es: G_interlayer.es['weight'] = [1.0] * G_interlayer.ecount() intralayer_part = louvain.RBConfigurationVertexPartitionWeightedLayers( G_intralayer, layer_vec=layer_membership, weights='weight') interlayer_part = louvain.CPMVertexPartition(G_interlayer, resolution_parameter=0.0, weights='weight') return intralayer_part, interlayer_part
def louvain(i, j, val, dim, partition_method, initial_membership, weights, resolution, node_sizes, seed, verbose): import louvain import igraph as ig import numpy from scipy.sparse import csc_matrix data = csc_matrix((val, (i, j)), shape=dim) # vcount = max(data.shape) sources, targets = data.nonzero() edgelist = zip(sources.tolist(), targets.tolist()) G = ig.Graph(edges=list(edgelist)) # G = ig.Graph.Adjacency(data.tolist()) if partition_method == 'ModularityVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, weights=weights) elif partition_method == 'RBConfigurationVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, weights=weights, resolution_parameter=resolution) elif partition_method == 'RBERVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, weights=weights, node_sizes=node_sizes, resolution_parameter=resolution) elif partition_method == 'CPMVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, weights=weights, node_sizes=node_sizes, resolution_parameter=resolution) elif partition_method == 'SignificanceVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, node_sizes=node_sizes) elif partition_method == 'SurpriseVertexPartition': partition = louvain.CPMVertexPartition( G, initial_membership=initial_membership, weights=weights, node_sizes=node_sizes) else: raise ValueError('partition_method ' + partition_method + ' is NOT supported.') if seed != None: louvain.set_rng_seed(seed) optimiser = louvain.Optimiser() diff = optimiser.optimise_partition(partition) # ig.plot(partition) return partition
def plot_manual_CHAMP(G_intralayer, G_interlayer, layer_vec, partitions): """Run an inefficient method to plot optimal modularity partititions across the (gamma, omega) plane to check consistency of the CHAMP implementation""" if 'weight' not in G_intralayer.es: G_intralayer.es['weight'] = [1.0] * G_intralayer.ecount() if 'weight' not in G_interlayer.es: G_interlayer.es['weight'] = [1.0] * G_interlayer.ecount() def part_color(membership): membership_val = hash(sorted_tuple(membership)) return tuple((membership_val / x) % 1.0 for x in [157244317, 183849443, 137530733]) denser_gammas = np.linspace(0, GAMMA_END, 250) denser_omegas = np.linspace(0, OMEGA_END, 250) intralayer_part = louvain.RBConfigurationVertexPartitionWeightedLayers( G_intralayer, layer_vec=layer_vec, weights='weight') G_interlayer.es['weight'] = [1.0] * G_interlayer.ecount() interlayer_part = louvain.CPMVertexPartition(G_interlayer, resolution_parameter=0.0, weights='weight') # best_partitions = List(quality, partition, gamma, omega) best_partitions = [[(-inf, ) * 4] * len(denser_omegas) for _ in range(len(denser_gammas))] for p in partitions: intralayer_part.set_membership(p) interlayer_part.set_membership(p) interlayer_base_quality = interlayer_part.quality( ) # interlayer quality at omega=1.0 for g_index, gamma in enumerate(denser_gammas): intralayer_quality = intralayer_part.quality( resolution_parameter=gamma) for o_index, omega in enumerate(denser_omegas): # omega * interlayer.quality() matches CPMVertexPartition.quality() # with omega as interlayer edge weights (as of 7/2) Q = intralayer_quality + omega * interlayer_base_quality if Q > best_partitions[g_index][o_index][0]: best_partitions[g_index][o_index] = (Q, p, gamma, omega) gammas, omegas, colors = zip(*[(x[2], x[3], part_color(x[1])) for row in best_partitions for x in row]) plt.scatter(gammas, omegas, color=colors, s=1, marker='s') plt.xlabel("gamma") plt.ylabel("omega")
#%% Do community detection print('\nDoing community detection...') n_repl = 100 resolutions = [0.6, 1.1, 1.7] for resolution in resolutions: memberships = [] print('Detecting communities using resolution parameter {0}'.format(resolution)) for itr in range(n_repl): print('\tRun {0:02d}'.format(itr)) partition_intraslice = [louvain.RBConfigurationVertexPartition(H, weights='weight', resolution_parameter=resolution) for H in G_intraslice] partition_interslice = louvain.CPMVertexPartition(G_interslice, weights='weight', node_sizes=G_interslice.vs['node_size'], resolution_parameter=0) ##%% Optimise partitions opt = louvain.Optimiser() opt.consider_comms = louvain.ALL_NEIGH_COMMS opt.optimise_partition_multiplex(partition_intraslice + [partition_interslice]) # The membership in all partitions will be identical, so simply # consider the membership for the interslice partition and graph. memberships.append(partition_interslice.membership) ##%% Write results to file cluster_df = pd.DataFrame({attr: G_interslice.vs[attr] for attr in G_interslice.vertex_attributes()}, index=[v.index for v in G_interslice.vs]) membership_df = pd.DataFrame.from_records(zip(*memberships), columns=['run_{0}'.format(itr) for itr in range(n_repl)]);