diff --git a/multinet/include/community.h b/multinet/include/community.h index 9f94ef4..5c1004c 100755 --- a/multinet/include/community.h +++ b/multinet/include/community.h @@ -107,9 +107,7 @@ namespace mlnet { double modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& groups, double c); double modularity(const MLNetworkSharedPtr& mnet, const hash_map& groups, double c); // for back-compatibility - hash_map> get_nodes_belonging_coef(const CommunityStructureSharedPtr& communities); - double extended_modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& communities, hash_map> nodes_belonging_coefficients,EdgeBelonigngFunc func); - /* Community comparison functions (external) */ + double extended_modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& communities, double c); double community_jaccard(const CommunitySharedPtr& c1, const CommunitySharedPtr& c2); diff --git a/multinet/include/community/flattening.h b/multinet/include/community/flattening.h index 85f60ea..bd0d04b 100644 --- a/multinet/include/community/flattening.h +++ b/multinet/include/community/flattening.h @@ -3,10 +3,12 @@ namespace mlnet { -enum WeighteningType {ZeroOne=0, NumOfLayers=1, Neighborhood=2}; -enum SingleLayerAlgorithm {LabelPropagation =1}; +enum WeighteningType {ZeroOne=0, NumOfLayers=1, Neighborhood=2, Jaccard=3}; -ActorCommunityStructureSharedPtr flattenAndDetectComs(const MLNetworkSharedPtr& mnet, WeighteningType wType,SingleLayerAlgorithm slAlgo); + +MLNetworkSharedPtr flatten(const MLNetworkSharedPtr& mnet, WeighteningType wType); + +CommunityStructureSharedPtr map_back_to_ml(const CommunityStructureSharedPtr& fComs,const MLNetworkSharedPtr& mnet); } diff --git a/multinet/src/community/community_evaluation.cpp b/multinet/src/community/community_evaluation.cpp index 63179bc..681c74a 100755 --- a/multinet/src/community/community_evaluation.cpp +++ b/multinet/src/community/community_evaluation.cpp @@ -94,7 +94,7 @@ namespace mlnet { double omega_index(const CommunityStructureSharedPtr& partitioning1, const CommunityStructureSharedPtr& partitioning2,const MLNetworkSharedPtr& mnet){ - //Create a map to represent pairs agreement in each input partitioning + //Create a map to represent pairs agreement in each input partitioning //The map is of the form [ key = pair of nodes (node1,node2) and value = number of times they co-occured together] std::map, int> p1_pairs_cooccurance; std::map, int> p2_pairs_cooccurance; @@ -102,22 +102,9 @@ namespace mlnet { //Get the nodes of the multi-net instance NodeListSharedPtr network_nodes = mnet->get_nodes(); - //Initialise both partitioning maps - for (NodeSharedPtr node1:*network_nodes){ - for (NodeSharedPtr node2:*network_nodes){ - if(node1!=node2){ - std::pair key (node1 ,node2); - std::pair key_inversed (node2,node1); - if(p1_pairs_cooccurance.find(key)== p1_pairs_cooccurance.end() & p1_pairs_cooccurance.find(key_inversed)==p1_pairs_cooccurance.end()){ - p1_pairs_cooccurance[key]=0; - p2_pairs_cooccurance[key]=0; - } - } - } - } - vector partitioning1_coms; - vector partitioning2_coms; + std::vector partitioning1_coms; + std::vector partitioning2_coms; //Iterate through the first partitioning communities to set the values for the corresponding map if(partitioning1!=NULL && partitioning1->get_communities().size()!=0){ @@ -140,6 +127,7 @@ namespace mlnet { if(p1_pairs_cooccurance.find(key_inversed)!=p1_pairs_cooccurance.end()){ p1_pairs_cooccurance[key_inversed]=p1_pairs_cooccurance[key_inversed]+1; } + else p1_pairs_cooccurance[key]=1; } } } @@ -169,6 +157,7 @@ namespace mlnet { p2_pairs_cooccurance[key_inversed]=p2_pairs_cooccurance[key_inversed]+1; } + else p2_pairs_cooccurance[key]=1; } } } @@ -178,33 +167,53 @@ namespace mlnet { //Count the agreements between both partitions int max_cooccurance_value=0; - int actual_agreements = 0; - int max_possible_num_of_agreements =0; + int actual_non_zero_agreements = 0; + int actual_zero_agreements = 0; + int total_agreemets=0; + int disagreements =0; + int max_possible_num_of_agreements = (network_nodes->size()*(network_nodes->size()-1))/2; + //Iterate through the keys in the first partitioning map - typedef std::map, int>::const_iterator MapIterator; - for (MapIterator iter = p1_pairs_cooccurance.begin(); iter != p1_pairs_cooccurance.end(); ++iter) + for (auto key_value:p1_pairs_cooccurance) { - max_possible_num_of_agreements++; //Get the current key - std::pair key (iter->first.first ,iter->first.second); + std::pair key (key_value.first.first ,key_value.first.second); + std::pair inversed_key (key_value.first.second,key_value.first.first); //Get the value referred to by the current key - int value_in_p1 = iter->second; + int value_in_p1 = key_value.second; + //check if this pair, key, exists in the other paritioning p2 + int value_in_p2 = -1; + if ( p2_pairs_cooccurance.find(key) != p2_pairs_cooccurance.end()) { + value_in_p2 = p2_pairs_cooccurance[key]; + } + else if (p2_pairs_cooccurance.find(inversed_key) != p2_pairs_cooccurance.end()) { + value_in_p2 = p2_pairs_cooccurance[inversed_key]; + } //Check the value of the same key in the second partitioning map - int value_in_p2 = p2_pairs_cooccurance[key]; if(value_in_p1==value_in_p2){ - actual_agreements++; + actual_non_zero_agreements++; + p1_pairs_cooccurance[key]=0; + if ( p2_pairs_cooccurance.find(key) != p2_pairs_cooccurance.end()) p2_pairs_cooccurance[key]=0; + else p2_pairs_cooccurance[inversed_key]=0; } //Store the maximum value - if(value_in_p2>max_cooccurance_value | value_in_p1>max_cooccurance_value) - max_cooccurance_value=(value_in_p2 > value_in_p1)?value_in_p2:value_in_p1; - - //std::cout << "< " << iter->first.first->actor->name << ", " << iter->first.second->actor->name <<"> = " << iter->second << std::endl; - // std::cout << "< " << iter->first.first->actor->name << ", " << iter->first.second->actor->name <<"> = " << p2_pairs_cooccurance[key] << std::endl; + if(value_in_p2>max_cooccurance_value || value_in_p1>max_cooccurance_value) + max_cooccurance_value=(value_in_p2 > value_in_p1)?value_in_p2:value_in_p1; } + //count the disagreements + for(auto key_value:p1_pairs_cooccurance){ + if(p1_pairs_cooccurance[key_value.first]!=0) disagreements++; + } + for(auto key_value:p2_pairs_cooccurance){ + if(p2_pairs_cooccurance[key_value.first]!=0) disagreements++; + } + actual_zero_agreements = max_possible_num_of_agreements - actual_non_zero_agreements - disagreements; + total_agreemets = actual_zero_agreements + actual_non_zero_agreements; - double unadjusted_omega = ((float)actual_agreements/(float)max_possible_num_of_agreements); + double unadjusted_omega = ((float)total_agreemets/(float)max_possible_num_of_agreements); + //std::cout << "Unadjustd" << unadjusted_omega< #include #include "community/flattening.h" +#include "datastructures.h" namespace mlnet { @@ -24,10 +25,7 @@ double numOfDimentionsWeightening(const MLNetworkSharedPtr& mnet,const ActorShar /*for each layer in the input multi-layer network try to find the corresponding nodes of input actors*/ for(LayerSharedPtr layer: *mnet->get_layers()) { - if(layer->name!="flattened") - { NodeSharedPtr node1 = mnet->get_node(act1,layer); - NodeSharedPtr node2 = mnet->get_node(act2,layer); /*if the corresponding nodes were found, and there is an edge among them, then increase the weight*/ @@ -35,9 +33,8 @@ double numOfDimentionsWeightening(const MLNetworkSharedPtr& mnet,const ActorShar { expectedWeight+=1; } - } } - return expectedWeight; + return expectedWeight/(double) mnet->get_layers()->size(); } @@ -108,69 +105,124 @@ double neighborhoodWeightening(const MLNetworkSharedPtr& mnet,const ActorSharedP } + +/** + * @brief calculate the weigh of an edge in case the weightening type "Jaccard" is chosen. + * The weight for an edge in the flattened layer will be set to the ratio constituted by the number + * of common layers of the two actors as a numerator and the union of layers as a denumerator + * @mnet : the multi-layer network instance + * @act1 : the first actor + * @act2 : the second actor + * @return : a double value representing the corresponding weight + **/ +double jaccardWeghtening(const MLNetworkSharedPtr& mnet,const ActorSharedPtr& act1, const ActorSharedPtr& act2){ + + + std::unordered_set a1_layers ; + std::unordered_set a2_layers ; + + for (LayerSharedPtr layer: *mnet->get_layers()) { + if (mnet->get_node(act1,layer)) { + a1_layers.insert(layer->name); + } + if (mnet->get_node(act2,layer)) { + a2_layers.insert(layer->name); + } + } + + std::vector > arr; + arr.push_back(a1_layers); + arr.push_back(a2_layers); + + + return jaccard_similarity(arr); + +} + /** - * @brief Detect communities in multiplex network. - * This is the implementation of Cocasa's method in detecting multi-dimentional communities using flattening - * https://pdfs.semanticscholar.org/fb9f/ec17f5962ecbc18b49f2057cbce0447e117d.pdf + * @brief Flatten a multiplex network into one single layer weighted network. * @mnet: The input multiplex network instance * @wType: The weightening method to be implemented in the resulted edges in the flattened graph - * @slAlgo:The single layer algorithm to be used to detect the communities - * @return:The resulted communities + * @return:The result of weighted flattening (i.e : a single layer weighted network) **/ -ActorCommunityStructureSharedPtr flattenAndDetectComs(const MLNetworkSharedPtr& mnet, WeighteningType wType,SingleLayerAlgorithm slAlgo){ + + +MLNetworkSharedPtr flatten(const MLNetworkSharedPtr& mnet, WeighteningType wType){ //STEPS: - CommunityStructureSharedPtr singleLayerComs ; - ActorCommunityStructureSharedPtr result ; + MLNetworkSharedPtr fnet = MLNetwork::create("flattened_net"); + //(1) Mapping function : from multi-dimentional(multiplex) to mono-dimentional(flattened layer) - /*add a new layer to the same multi-layer instance*/ - LayerSharedPtr flattenedLayer = mnet->add_layer("flattened",UNDIRECTED); - /*add the wight attribute to the edges of this layer*/ - mnet->edge_features(flattenedLayer,flattenedLayer)->add("_WEIGHT",NUMERIC_TYPE); + + /*add a new layer to the flattened network*/ + LayerSharedPtr flattenedLayer = fnet->add_layer("flattened",UNDIRECTED); + + /*add the weight attribute to the edges of this layer*/ + fnet->edge_features(flattenedLayer,flattenedLayer)->add("_WEIGHT",NUMERIC_TYPE); + + /*add the actors to the flattened network */ + for (ActorSharedPtr act:*mnet->get_actors()){ + fnet->add_actor(act->name); + fnet->add_node(act,flattenedLayer); + } + /*for each edge in the input network*/ for (EdgeSharedPtr edge : *mnet->get_edges()) { /*consider the edge only if is an intra-layer edge*/ if(edge->v1->layer == edge->v2->layer) { - ActorSharedPtr act1 = edge->v1->actor; - ActorSharedPtr act2 = edge->v2->actor; - NodeSharedPtr from = mnet->add_node(act1,flattenedLayer) ; - NodeSharedPtr to = mnet->add_node(act2,flattenedLayer); + ActorSharedPtr act1 = mnet->get_actor(edge->v1->actor->name); + ActorSharedPtr act2 = mnet->get_actor(edge->v2->actor->name); + + NodeSharedPtr from = fnet->get_node(act1,flattenedLayer); + NodeSharedPtr to = fnet->get_node(act2,flattenedLayer); double expectedWeight=0; - //Add the edge if the nodes are retrieved and the edge is not already existent - if(from && to && !mnet->get_edge(from,to)) + //Add the edge if the it is not already existent + if(!fnet->get_edge(from,to)) { - mnet->add_edge(from,to); + fnet->add_edge(from,to); + + //calculate the weight of the edge switch (wType) { - case ZeroOne: expectedWeight =1 ; break; + case ZeroOne: + expectedWeight =1; + break; case NumOfLayers: - expectedWeight = numOfDimentionsWeightening(mnet,act1,act2) ;break; - case Neighborhood: expectedWeight = neighborhoodWeightening(mnet,act1,act2); - break; + expectedWeight = numOfDimentionsWeightening(mnet,act1,act2) ; + break; + case Neighborhood: + expectedWeight = neighborhoodWeightening(mnet,act1,act2); + break; + case Jaccard: + expectedWeight = jaccardWeghtening(mnet,act1,act2); + break; default: expectedWeight =1; } - mnet->set_weight(from,to,expectedWeight); + fnet->set_weight(from,to,expectedWeight); } } - } - - //(2) Perform community detection on the resulted mono-dimentional graph (in other words, the flattened layer) - switch (slAlgo) { - case LabelPropagation: - singleLayerComs = label_propagation_single(mnet, flattenedLayer); - break; - default: - singleLayerComs = label_propagation_single(mnet, flattenedLayer); } + return fnet; + } + + +/** + * @brief maps back the communities found in the flattened network into multi-layer communities in the original multi-layer instance. + * @fComs: The communities of the flattened graph + * @mnet: The input multi-layer network instance + * @return:The mapping as actor communities on the multi-layer instance + **/ + CommunityStructureSharedPtr map_back_to_ml(const CommunityStructureSharedPtr& fComs,const MLNetworkSharedPtr& mnet){ - //(3) Mapping back : from mono-dimentional(flattened layer) to multi-dimentional(multiplex) - result = actor_community_structure::create(); - //for each community in the single layer communities instance - for(CommunitySharedPtr singleCom:singleLayerComs->get_communities()){ + ActorCommunityStructureSharedPtr result = actor_community_structure::create(); + + //for each community in the flattened network + for(CommunitySharedPtr singleCom:fComs->get_communities()){ ActorCommunitySharedPtr actor_com = actor_community::create(); - //for each node in the current single layer community + + //for each node in the current flattened network community for(NodeSharedPtr node:singleCom->get_nodes()){ //find the actor of this node in the mnet instance ActorSharedPtr actor = node->actor; @@ -188,11 +240,8 @@ ActorCommunityStructureSharedPtr flattenAndDetectComs(const MLNetworkSharedPtr& // add this community to the list of communities to be returned result->add_community(actor_com); } - //now, the flattened layer can be safely deleted from the mnet instance - mnet->erase(flattenedLayer); - return result; - } + return to_node_communities(result,mnet); + } } - diff --git a/multinet/src/community/mlp.cpp b/multinet/src/community/mlp.cpp index 7f30ce6..d14abd2 100644 --- a/multinet/src/community/mlp.cpp +++ b/multinet/src/community/mlp.cpp @@ -211,15 +211,14 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ initial_attraction_weights[actor][pair.first]=affinity; } } - /*(2) recover the relevant dimensions (layers) Dv for each actor using equation(8) in the reference*/ for(ActorSharedPtr actor:*mnet->get_actors()){ //retrieve the layers in which the actors has neighbours - vector activ_in_layers; + std::vector activ_in_layers; + if(all_actors_neighbours.find(actor) != all_actors_neighbours.end() && !all_actors_neighbours.at(actor).empty()) for(auto pair:all_actors_neighbours.at(actor)){ activ_in_layers.push_back(pair.first); } - //calculate the sum of w0 for neighbours within all subsets of layers in which the actor is active double max_sum_of_w0=0; vector subset; @@ -241,9 +240,8 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ } } } - actors_relevant_dimensions[actor] =actor_relevant_dimentions; + actors_relevant_dimensions[actor] = actor_relevant_dimentions; } - /*(3) calculate the new attraction weights w for actors (equation (7) in the article)*/ for(auto pair:initial_attraction_weights){ for(auto sub_pair: pair.second){ @@ -254,7 +252,6 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ updated_attraction_weights[act1][act2]=initial_attraction_weights[act1][act2]*((double)intersect.size()/union_.size()); } } - /*(4)The labeling step*/ ActorListSharedPtr actors = mnet->get_actors(); hash_map membership; // community membership @@ -273,9 +270,11 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ /* shuffle the order of the actors */ unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::shuffle(order.begin(), order.end(), std::default_random_engine(seed)); - /* re-assign labels */ for (ActorSharedPtr actor: order) { + //if the actor has neighbours in any layer + if(all_actors_neighbours.find(actor) != all_actors_neighbours.end() && !all_actors_neighbours.at(actor).empty()) + { //group the neighbours according to their labels calculate the sum of w within each group hash_map neigh_groups_wieghts; for(auto pair:updated_attraction_weights[actor]){ @@ -311,11 +310,13 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ vector union_= get_union(actors_relevant_dimensions[actor],actors_shared_layers[actor][neighbour]); updated_attraction_weights[neighbour][actor]=updated_attraction_weights[neighbour][actor]*((double)intersect.size()/union_.size()); } - + } } - //check the stopping condition for (ActorSharedPtr actor: order) { + //if the actor has neighbours in any layer + if(all_actors_neighbours.find(actor) != all_actors_neighbours.end() && !all_actors_neighbours.at(actor).empty()) + { //group the neighbours according to their labels calculate the sum of w within each group hash_map neigh_groups_wieghts; for(auto pair:updated_attraction_weights[actor]){ @@ -340,9 +341,9 @@ ActorCommunityStructureSharedPtr mlp(const MLNetworkSharedPtr& mnet){ continue; } } + } break; } - return to_community_structure(membership,actors_relevant_dimensions); } diff --git a/multinet/src/community/modularity.cpp b/multinet/src/community/modularity.cpp index b98d182..a306107 100644 --- a/multinet/src/community/modularity.cpp +++ b/multinet/src/community/modularity.cpp @@ -14,128 +14,15 @@ namespace mlnet { -double modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& communities, double c) { - - double res = 0; - double mu = 0; - hash_map m_s; - for (LayerSharedPtr s: *mnet->get_layers()) { - double m = mnet->get_edges(s,s)->size(); - if (!mnet->is_directed(s,s)) - m *= 2; - // FIX TO THE ORIGINAL EQUATION WHEN THERE ARE NO EDGES - if (m == 0) - m = 1; // no effect on the formula - m_s[s] = m; - mu += m; - } - - for (CommunitySharedPtr community: communities->get_communities()) { - for (NodeSharedPtr i: community->get_nodes()) { - for (NodeSharedPtr j: community->get_nodes()) { - if (i==j) continue; // not in the original definition - we do this assuming to deal with simple graphs - //std::cout << i->to_string() << " " << groups.count(i) << std::endl; - //std::cout << j->to_string() << " " << groups.count(j) << std::endl; - - if (i->layer==j->layer) { - //std::cout << "Same group!" << std::endl; - //if (mnet.getNetwork(net)->containsEdge(*v_i,*v_j)) - // std::cout << "Edge" << std::endl; - long k_i = mnet->neighbors(i,OUT)->size(); - long k_j = mnet->neighbors(j,IN)->size(); - int a_ij = mnet->get_edge(i,j)? 1.0 : 0.0; - res += a_ij - (double)k_i * k_j / (m_s.at(i->layer)); - //std::cout << i->actor->name << " " << j->actor->name << " " << i->layer->name << " "<< k_i << " " << k_j << " " << m_s.at(i->layer) << std::endl; - //std::cout << "->" << res << std::endl; - } - if (i->actor==j->actor) { - res += c; - } - } - } - //std::cout << "->" << m_net << std::endl; - } - //std::cout << "same" << std::endl; - - //std::cout << mu << std::endl; - for (ActorSharedPtr actor: *mnet->get_actors()) { - int num_nodes = mnet->get_nodes(actor)->size(); - mu+=num_nodes*(num_nodes-1)*c; // unclear if we should multiply by c - } - //std::cout << mu << std::endl; - - return 1 / mu * res; -} - -double modularity(const MLNetworkSharedPtr& mnet, const hash_map& membership, double c) { - // partition the nodes by group - hash_map > groups; - for (auto pair: membership) { - groups[pair.second].insert(pair.first); - } - // start computing the modularity - double res = 0; - double mu = 0; - hash_map m_s; - for (LayerSharedPtr s: *mnet->get_layers()) { - double m = mnet->get_edges(s,s)->size(); - if (!mnet->is_directed(s,s)) - m *= 2; - // FIX TO THE ORIGINAL EQUATION WHEN THERE ARE NO EDGES - if (m == 0) - m = 1; // no effect on the formula - m_s[s] = m; - mu += m; - } - - for (auto pair: groups) { - for (NodeSharedPtr i: pair.second) { - for (NodeSharedPtr j: pair.second) { - if (i==j) continue; // not in the original definition - we do this assuming to deal with simple graphs - //std::cout << i->to_string() << " " << groups.count(i) << std::endl; - //std::cout << j->to_string() << " " << groups.count(j) << std::endl; - - if (i->layer==j->layer) { - //std::cout << "Same group!" << std::endl; - //if (mnet.getNetwork(net)->containsEdge(*v_i,*v_j)) - // std::cout << "Edge" << std::endl; - long k_i = mnet->neighbors(i,OUT)->size(); - long k_j = mnet->neighbors(j,IN)->size(); - int a_ij = mnet->get_edge(i,j)? 1.0 : 0.0; - res += a_ij - (double)k_i * k_j / (m_s.at(i->layer)); - //std::cout << i->actor->name << " " << j->actor->name << " " << i->layer->name << " "<< k_i << " " << k_j << " " << m_s.at(i->layer) << std::endl; - //std::cout << "->" << res << std::endl; - } - if (i->actor==j->actor) { - res += c; - } - } - } - //std::cout << "->" << m_net << std::endl; - } - //std::cout << "same" << std::endl; - - //std::cout << mu << std::endl; - for (ActorSharedPtr actor: *mnet->get_actors()) { - int num_nodes = mnet->get_nodes(actor)->size(); - mu+=num_nodes*(num_nodes-1)*c; // unclear if we should multiply by c - } - //std::cout << mu << std::endl; - - return 1 / mu * res; - } - - - /** -* calculates the belonging coefficients for nodes to their communities by setting it equal to 1 if the node is a non-overlapping node, +* calculates the belonging factors for nodes to their communities by setting it equal to 1 if the node is a non-overlapping node, * or 1/#communities_for_the_node when the node is an overlapping node * @param communities: set of communities (groups of nodes that belong to mnet) */ -hash_map> get_nodes_belonging_coef(const CommunityStructureSharedPtr& communities){ +hash_map> get_nodes_belonging_factors(const CommunityStructureSharedPtr& communities){ //we define node_frequecy here as the number of communities the node is part of - //std::cout<< "get_nodes_belonging_coef" << std::endl; + //std::cout<< "get_nodes_belonging_factors" << std::endl; hash_map nodes_frequencies; vector communities_list = communities->get_communities(); for (CommunitySharedPtr com:communities_list){ @@ -153,134 +40,160 @@ hash_map> get_nodes_belonging_ //set equal contributions for the node in each community it is part of for (CommunitySharedPtr com:communities_list){ for(NodeSharedPtr node:com->get_nodes()){ - nodes_belonging_coef[com][node]=1/nodes_frequencies[node]; + nodes_belonging_coef[com][node]= (double)1/nodes_frequencies[node]; } } - //std::cout<< "end get_nodes_belonging_coef" << std::endl; + //std::cout<< "end get_nodes_belonging_factors" << std::endl; return nodes_belonging_coef; } /** -* calculates the edge belonging co_efficient to its community as a function of its corresponding nodes belonging coefficients -* @param node1_belonging_co: first node belonging coefficient -* @param node2_belonging_co: second node belonging coefficient +* calculates the edge belonging factors to its community as a function of its corresponding nodes belonging factors +* @param node1_belonging_co: first node belonging factor +* @param node2_belonging_co: second node belonging factor * @param func could be one of the following values (Sum,Multiply,Max,Average). */ -double get_edge_belonging_coefficient(double node1_belonging_co,double node2_belonging_co, EdgeBelonigngFunc func){ - double edge_belonging =0; +double F(double node1_belonging_co,double node2_belonging_co, EdgeBelonigngFunc func){ + double belonging =0; switch (func) { case Sum: - edge_belonging=node1_belonging_co+node2_belonging_co; + belonging=node1_belonging_co+node2_belonging_co; break; case Multiply: - edge_belonging=node1_belonging_co*node2_belonging_co; + belonging=node1_belonging_co*node2_belonging_co; break; case Max: - edge_belonging= (node1_belonging_co>node2_belonging_co)? node1_belonging_co:node2_belonging_co; + belonging= (node1_belonging_co>node2_belonging_co)? node1_belonging_co:node2_belonging_co; break; case Average: - edge_belonging=(node1_belonging_co+node2_belonging_co)/2; + belonging=(node1_belonging_co+node2_belonging_co)/2; break; default: - edge_belonging=node1_belonging_co*node2_belonging_co; + belonging=node1_belonging_co*node2_belonging_co; } - return edge_belonging; + return belonging; } - /** -* calculate the modularity for directed graphs with overlapping communities where in this implementation, two communities are overlapping if they share nodes -* reference : http://iopscience.iop.org/article/10.1088/1742-5468/2009/03/P03024/meta +# Implementation of multislice modularity defined in : +# Mucha, P. J., Richardson, T., Macon, K., Porter, M. A. & Onnela, J.-P. +# Community structure in time - dependent, multiscale, and multiplex networks. +# Science 328, 876–8(2010) * @param mnet : the multi-layer instance -* @param communities: set of communities (groups of nodes that belong to mnet) -* @param nodes_belonging_coefficients (weights given for the nodes that are in the overlapping communities and it express how strongly a node belong to each community it is part of) -* @param func could be one of the following values (Sum,Multiply,Max,Average) and it specifies how the edge belonging to a cummunity should be calculated as a function of the belonging coefficients of the corresponding nodes -*/ -double extended_modularity(const MLNetworkSharedPtr& mnet, - const CommunityStructureSharedPtr& communities, - hash_map> nodes_belonging_coefficients, - EdgeBelonigngFunc func){ - - - double sum_of_differences =0; - double modularity =0; +* @param communities: a clustering +* @param interlayer_weight: 0-1 + */ +double modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& communities, double interlayer_weight){ - //some auxiliary variables + double gamma=1.0; - double first_node_belonging_co=1; - double second_node_belonging_co= 1; - double out_neighbour_belonging_co =1; - double in_neighbour_belonging_co =1; + hash_map assignments; + int c_num = 0; + for (CommunitySharedPtr com: communities->get_communities()) { + for (NodeSharedPtr node: com->get_nodes()) { + assignments[node]=c_num; + } + c_num++; + } + double mu =0.0; + double Q = 0.0; + //For each layer + for (LayerSharedPtr layer: *mnet->get_layers()) { + //For each node in that layer + for (NodeSharedPtr node:*mnet->get_nodes(layer)){ + double sum_i = 0.0; + //store the degree of this node + int deg_node = mnet->neighbors(node,INOUT)->size(); + //for each neighbour pf this node + for (NodeSharedPtr nei: *mnet->neighbors(node,INOUT)){ + //check if they both (the node and its neighbour) are assigned to a community + if(assignments.find(node)!=assignments.end() && assignments.find(nei) != assignments.end()){ + //check if they both (the node and its neighbour) are in the same community + if(assignments[node] == assignments[nei]){ + //store the degree of the neighbour + int deg_nei = mnet->neighbors(nei,INOUT)->size(); + //calculate the contribution of this edge to the modularity + sum_i+= 1- gamma*((deg_node*deg_nei)/(2*mnet->get_edges(layer,layer)->size())); + } + } + } + sum_i+=interlayer_weight; + Q+=sum_i; + mu+=deg_node+(mnet->get_layers()->size()-1)*interlayer_weight; + } + } - double sum_out_edge_belonging_co = 0; - double sum_in_edge_belonging_co = 0; + Q=(double) Q/(2*mu); + return Q; - double actual_edge_beloning_co = 0; - double expected_out_edge_belonging_co =0; - double expected_in_edge_belonging_co =0; +} +/** +* calculate the modularity for graphs with overlapping communities where in this implementation, two communities are overlapping if they share nodes +* reference : http://iopscience.iop.org/article/10.1088/1742-5468/2009/03/P03024/meta +* @param mnet : the multi-layer instance +* @param communities: a clustering +* @param interlayer_weight: 0-1 +*/ +double extended_modularity(const MLNetworkSharedPtr& mnet, const CommunityStructureSharedPtr& communities, double interlayer_weight){ - //iterate through communities - for(CommunitySharedPtr com:communities->get_communities()){ - for(NodeSharedPtr node1:com->get_nodes()){ - for(NodeSharedPtr node2:com->get_nodes()){ - //if it is not the same node and there is an edge between the corresponding two actors - if(node1 != node2 & mnet->get_edge(node1,node2)!=NULL){ - //a-calculate the actual belonging co_efficient of the this link (referred to as r(i,j,c) in the paper) - first_node_belonging_co=1; - second_node_belonging_co= 1; - //if the nodes belonging coefficient is already given in the input variable nodes_belonging_coefficients - if(nodes_belonging_coefficients[com].find(node1)!=nodes_belonging_coefficients[com].end()) - first_node_belonging_co=nodes_belonging_coefficients[com][node1]; - if(nodes_belonging_coefficients[com].find(node2)!=nodes_belonging_coefficients[com].end()) - second_node_belonging_co=nodes_belonging_coefficients[com][node2]; + double gamma=1.0; - actual_edge_beloning_co= get_edge_belonging_coefficient(first_node_belonging_co,second_node_belonging_co,func); + hash_map> n_bc = get_nodes_belonging_factors(communities);//nodes belonging co-effiecinets to communities + hash_map> assignments; + for (CommunitySharedPtr com: communities->get_communities()) { + for (NodeSharedPtr node: com->get_nodes()) { + assignments[node].insert(com); + } + } - //b-calculate the expected belonging co_efficient of the this link to this community (referred to as s(i,j,c) in the paper) - //b.1- expected belonging co_efficient of any link starting from the node 1 (referred to as B_out_(i,j),c) - NodeListSharedPtr out_neighbours = mnet->neighbors(node1,OUT); - sum_out_edge_belonging_co = 0; - expected_out_edge_belonging_co =0; - //if the node has out_neighbours - if(out_neighbours->size()!=0){ - for(NodeSharedPtr out_neighbour:*out_neighbours){ - if(nodes_belonging_coefficients[com].find(out_neighbour)!=nodes_belonging_coefficients[com].end()) - out_neighbour_belonging_co=nodes_belonging_coefficients[com][out_neighbour]; - else out_neighbour_belonging_co=1; - sum_out_edge_belonging_co+=get_edge_belonging_coefficient(first_node_belonging_co,out_neighbour_belonging_co,func);; - } - expected_out_edge_belonging_co=sum_out_edge_belonging_co/mnet->get_edges()->size(); + double mu =0.0; + double Q = 0.0; + //For each layer + for (LayerSharedPtr layer: *mnet->get_layers()) { + //For each node in that layer + for (NodeSharedPtr node:*mnet->get_nodes(layer)){ + double sum_i = 0.0; + //Store the degree of this node + int deg_node = mnet->neighbors(node,INOUT)->size(); + //For each neighbour of this node + for (NodeSharedPtr nei: *mnet->neighbors(node,INOUT)){ + //If they both are assigned to a community + if(assignments.find(node)!=assignments.end() && assignments.find(nei) != assignments.end()){ + //Store the intersection of community assignments for both (the node and its neighcour) + hash_set same_coms = s_intersection(assignments[node] ,assignments[nei]); + //Get the degree of the neighbour + int deg_nei = mnet->neighbors(nei,INOUT)->size(); + //For each common community assignment for both (the node and its neighbour) + for(CommunitySharedPtr shared_com:same_coms){ + + long bc_i_c = 0; //expected belonging factor of any link starting from "node" to "shaed_com" (in a null model) + for (NodeSharedPtr out_node: shared_com->get_nodes()) { + bc_i_c += F(n_bc[shared_com][node],n_bc[shared_com][out_node],Max)/mnet->get_nodes()->size(); } - else expected_out_edge_belonging_co=0; - //b.2- expected belonging co_efficient of any link ending at node 2 (referred to as B_in_(i,j),c) - NodeListSharedPtr in_neighbours = mnet->neighbors(node2,IN); - sum_in_edge_belonging_co = 0; - expected_in_edge_belonging_co =0; - //if the node has in_neighbours - if(in_neighbours->size()!=0){ - for(NodeSharedPtr in_neighbour:*in_neighbours){ - if(nodes_belonging_coefficients[com].find(in_neighbour)!=nodes_belonging_coefficients[com].end()) - in_neighbour_belonging_co=nodes_belonging_coefficients[com][in_neighbour]; - else in_neighbour_belonging_co=1; - sum_in_edge_belonging_co+=get_edge_belonging_coefficient(in_neighbour_belonging_co,second_node_belonging_co,func);; - } - expected_in_edge_belonging_co=sum_in_edge_belonging_co/mnet->get_edges()->size(); + long bc_j_c = 0; //expected belonging factor of any link ending at "nei" to "shaed_com" (in a null model) + for (NodeSharedPtr in_node: shared_com->get_nodes()) { + bc_j_c += F(n_bc[shared_com][in_node],n_bc[shared_com][nei],Max)/mnet->get_nodes()->size(); } - else expected_in_edge_belonging_co=0; - //c-add the difference between expected and actual to the sum of differences - sum_of_differences+= actual_edge_beloning_co - (expected_out_edge_belonging_co*out_neighbours->size())*(expected_in_edge_belonging_co*in_neighbours->size())/mnet->get_edges()->size(); + long bc_ij_c = F(n_bc[shared_com][node],n_bc[shared_com][nei],Max);// calculates the belonging factor of an edge to a community as a function of the belonging factors of the nodes + + sum_i+= bc_ij_c- gamma*((bc_i_c*deg_node*bc_j_c*deg_nei)/(2*mnet->get_edges(layer,layer)->size())); } } } + sum_i+=interlayer_weight; + Q+=sum_i; + mu+=deg_node+(mnet->get_layers()->size()-1)*interlayer_weight; } - modularity=sum_of_differences/mnet->get_edges()->size(); - return modularity; + } + Q=(double) Q/(2*mu); + return Q; + } } diff --git a/multinet/test/test_community.cpp b/multinet/test/test_community.cpp index 36d70da..485bda3 100644 --- a/multinet/test/test_community.cpp +++ b/multinet/test/test_community.cpp @@ -127,8 +127,10 @@ void test_community() { std::cout << "=====================" << std::endl; std::cout << "Flattening" << std::endl; - communities = flattenAndDetectComs(cpm,ZeroOne,LabelPropagation); - std::cout << communities->to_string(); + MLNetworkSharedPtr fnet = flatten(mnet,ZeroOne); + LayerSharedPtr fLayer = fnet->get_layer("flattened"); + CommunityStructureSharedPtr coms = label_propagation_single(fnet, fLayer); + std::cout << coms->to_string(); //std::cout << "modularity (NOTE: not defined for overlapping communities): "; //std::cout << modularity(toy,n_communities,1) << std::endl; diff --git a/multinet/test/test_flattening.cpp b/multinet/test/test_flattening.cpp index d1fd8f7..edd5093 100644 --- a/multinet/test/test_flattening.cpp +++ b/multinet/test/test_flattening.cpp @@ -1,6 +1,7 @@ #include "test.h" #include "../include/community/flattening.h" + using namespace mlnet; void test_flattening() { @@ -9,11 +10,27 @@ void test_flattening() { MLNetworkSharedPtr mnet = read_multilayer("dkpol.mpx","dkpol",','); + std::cout << "before flattening : " <to_string() <to_string() <get_layer("flattened"); + CommunityStructureSharedPtr coms = label_propagation_single(fnet, fLayer); + std::cout << "communities using label propagation : " <to_string() <to_string() << std::endl; + std::cout <to_string() <> node_belonging_co = get_nodes_belonging_coef(cmp); std::cout<< "modularity = " << modularity(mnet2,cmp,1) << std::endl; - std::cout<< "extnded_modularity (func = multiply) = " << extended_modularity(mnet2,cmp,node_belonging_co,Multiply) << std::endl; - std::cout<< "extnded_modularity (func = sum) = " << extended_modularity(mnet2,cmp,node_belonging_co,Sum) << std::endl; - std::cout<< "extnded_modularity (func = average) = " << extended_modularity(mnet2,cmp,node_belonging_co,Average) << std::endl; - std::cout<< "extnded_modularity (func = max) = " << extended_modularity(mnet2,cmp,node_belonging_co,Max) << std::endl; + std::cout<< "extnded_modularity (func = multiply) = " << extended_modularity(mnet2,cmp,1) << std::endl; test_begin("End testing Modularity"); //log("TEST SUCCESSFULLY COMPLETED (modularity)");