def test_diff_move():
    intraslice = ig.Graph.Read_Ncol("multilayer_SBM_interslice_edges.csv",
                                    directed=False)
    n = intraslice.vcount()
    layer_vec = [0] * n
    membership = list(range(n))

    part_rbc = louvain.RBConfigurationVertexPartition(
        intraslice, resolution_parameter=1.0, initial_membership=membership)
    part_weighted_layers = louvain.RBConfigurationVertexPartitionWeightedLayers(
        intraslice,
        resolution_parameter=1.0,
        layer_vec=layer_vec,
        initial_membership=membership)

    # check diff_move() - quality() consistency across 100 random moves
    for repeat in range(100):
        v = randint(0, n - 1)
        c = randint(0, n - 1)
        old_quality = part_weighted_layers.quality()
        wl_diff = part_weighted_layers.diff_move(v, c)
        part_weighted_layers.move_node(v, c)
        true_diff = part_weighted_layers.quality() - old_quality

        rbc_diff = part_rbc.diff_move(v, c)
        part_rbc.move_node(v, c)

        assert isclose(
            wl_diff, true_diff
        ), "WeightedLayers diff_move() inconsistent with quality()"
        assert isclose(
            wl_diff, rbc_diff
        ), "WeightedLayers diff_move() inconsistent with single-layer"
        assert isclose(part_weighted_layers.quality(), part_rbc.quality(
        )), "WeightedLayers quality() inconsistent with single-layer"

    # check rng consistency between RBConfigurationVertexPartition and its WeightedLayers variant
    # with various seeds and intraslice resolution parameters
    for gamma in np.linspace(0.5, 1.5, 10):
        shared_seed = randint(-1 << 31, (1 << 31) - 1)  # random int32

        louvain.set_rng_seed(shared_seed)
        part_weighted_layers = louvain.RBConfigurationVertexPartitionWeightedLayers(
            intraslice, resolution_parameter=gamma, layer_vec=layer_vec)
        opt = louvain.Optimiser()
        opt.optimise_partition(partition=part_weighted_layers)

        louvain.set_rng_seed(shared_seed)
        part_rbc = louvain.RBConfigurationVertexPartition(
            intraslice, resolution_parameter=gamma)
        opt = louvain.Optimiser()
        opt.optimise_partition(partition=part_rbc)

        quality_weighted_layers = part_weighted_layers.quality(
            resolution_parameter=gamma)
        quality_rbc = part_rbc.quality(resolution_parameter=gamma)
        assert isclose(
            quality_weighted_layers, quality_rbc
        ), "Intra-layer optimisation inconsistent with single-layer"
示例#2
0
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 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)
示例#4
0
 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 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")
def test_multilayer_louvain():
    intraslice = ig.Graph.Read_Ncol("multilayer_SBM_intraslice_edges.csv",
                                    directed=False)
    interslice = ig.Graph.Read_Ncol("multilayer_SBM_interslice_edges.csv",
                                    directed=False)
    n_layers = 4
    n = intraslice.vcount() // n_layers
    layer_vec = np.array([i // n for i in range(n * n_layers)])

    intraslice.es['weight'] = 1.0
    intralayer_part = louvain.RBConfigurationVertexPartitionWeightedLayers(
        intraslice,
        resolution_parameter=1.0,
        layer_vec=layer_vec,
        weights='weight')

    for omega in np.linspace(0.5, 1.5, 10):
        interslice.es['weight'] = omega

        interlayer_part = louvain.RBConfigurationVertexPartition(
            interslice, resolution_parameter=0.0, weights='weight')

        opt = louvain.Optimiser()
        opt.optimise_partition_multiplex(
            partitions=[intralayer_part, interlayer_part])

        louvain_mod = intralayer_part.quality(
            resolution_parameter=1.0) + interlayer_part.quality()

        A = np.array(intraslice.get_adjacency()._get_data())
        C = omega * np.array(interslice.get_adjacency()._get_data())
        P = np.zeros((n_layers * n, n_layers * n))
        for i in range(n_layers):
            c_degrees = np.array(
                intraslice.degree(list(range(n * i, n * i + n))))
            c_inds = np.where(layer_vec == i)[0]
            P[np.ix_(c_inds, c_inds)] = np.outer(
                c_degrees, c_degrees.T) / (1.0 * np.sum(c_degrees))

        membership = np.array(intralayer_part.membership)
        true_mod = sum(
            calculate_coefficient(membership, X) for X in (A, -P, C))

        assert isclose(
            louvain_mod, true_mod
        ), "WeightedLayers quality() inconsistent with alternate calculation"
示例#8
0
def run_louvain_multilayer(intralayer_graph,
                           interlayer_graph,
                           layer_vec,
                           weight='weight',
                           resolution=1.0,
                           omega=1.0,
                           nruns=1):
    logging.debug('Shuffling node ids')
    t = time()
    mu = np.sum(intralayer_graph.es[weight]) + interlayer_graph.ecount()

    use_RBCweighted = hasattr(louvain,
                              'RBConfigurationVertexPartitionWeightedLayers')

    outparts = []
    for run in range(nruns):
        rand_perm = list(np.random.permutation(interlayer_graph.vcount()))
        # rand_perm = list(range(interlayer_graph.vcount()))
        rperm = rev_perm(rand_perm)
        interslice_layer_rand = interlayer_graph.permute_vertices(rand_perm)
        rlayer_vec = permute_vector(rand_perm, layer_vec)

        rintralayer_graph = intralayer_graph.permute_vertices(rand_perm)
        #
        if use_RBCweighted:
            rlayers = [
                intralayer_graph
            ]  #  one layer representing all intralayer connections here
        else:
            rlayers = _create_multilayer_igraphs_from_super_adj_igraph(
                rintralayer_graph, layer_vec=rlayer_vec)

        logging.debug('time: {:.4f}'.format(time() - t))

        t = time()

        #create the partition objects
        layer_partition_objs = []

        logging.debug('creating partition objects')
        t = time()

        for i, layer in enumerate(
                rlayers):  #these are the shuffled igraph slice objects
            try:
                res = resolution[i]
            except:
                res = resolution

            if use_RBCweighted:

                cpart = louvain.RBConfigurationVertexPartitionWeightedLayers(
                    layer,
                    layer_vec=rlayer_vec,
                    weights=weight,
                    resolution_parameter=res)
            else:
                #This creates individual VertexPartition for each layer.  Much slower to optimize.
                cpart = louvain.RBConfigurationVertexPartition(
                    layer, weights=weight, resolution_parameter=res)

            layer_partition_objs.append(cpart)

        coupling_partition = louvain.RBConfigurationVertexPartition(
            interslice_layer_rand, weights=weight, resolution_parameter=0)

        all_layer_partobjs = layer_partition_objs + [coupling_partition]

        optimiser = louvain.Optimiser()
        logging.debug('time: {:.4f}'.format(time() - t))
        logging.debug('running optimiser')
        t = time()

        layer_weights = [1] * len(rlayers) + [omega]
        improvement = optimiser.optimise_partition_multiplex(
            all_layer_partobjs, layer_weights=layer_weights)

        #the membership for each of the partitions is tied together.
        finalpartition = permute_vector(rperm,
                                        all_layer_partobjs[0].membership)
        reversed_partobj = []
        #go back and reverse the graphs associated with each of the partobj.  this allows for properly calculating exp edges with partobj
        #This is not ideal.  Could we just reverse the permutation?
        for layer in layer_partition_objs:
            if use_RBCweighted:
                reversed_partobj.append(
                    louvain.RBConfigurationVertexPartitionWeightedLayers(
                        graph=layer.graph.permute_vertices(rperm),
                        initial_membership=finalpartition,
                        weights=weight,
                        layer_vec=layer_vec,
                        resolution_parameter=layer.resolution_parameter))
            else:
                reversed_partobj.append(
                    louvain.RBConfigurationVertexPartition(
                        graph=layer.graph.permute_vertices(rperm),
                        initial_membership=finalpartition,
                        weights=weight,
                        resolution_parameter=layer.resolution_parameter))
        coupling_partition_rev = louvain.RBConfigurationVertexPartition(
            graph=coupling_partition.graph.permute_vertices(rperm),
            initial_membership=finalpartition,
            weights=weight,
            resolution_parameter=0)
        #use only the intralayer part objs
        A = _get_sum_internal_edges_from_partobj_list(reversed_partobj,
                                                      weight=weight)
        if use_RBCweighted:  #should only one partobj here representing all layers
            P = get_expected_edges_ml(reversed_partobj[0],
                                      layer_vec=layer_vec,
                                      weight=weight)
        else:
            P = _get_sum_expected_edges_from_partobj_list(reversed_partobj,
                                                          weight=weight)
        C = get_sum_internal_edges(coupling_partition_rev, weight=weight)
        outparts.append({'partition': np.array(finalpartition),
             'resolution': resolution,
             'coupling':omega,
             'orig_mod': (.5/mu)*(_get_modularity_from_partobj_list(reversed_partobj)\
                   +omega*coupling_partition_rev.quality()),
             'int_edges': A,
             'exp_edges': P,
            'int_inter_edges':C})

    logging.debug('time: {:.4f}'.format(time() - t))
    return outparts