static int igraph_i_realize_directed_degree_sequence( igraph_t *graph, const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_realize_degseq_t method) { long node_count = igraph_vector_size(outdeg); long edge_count = long(igraph_vector_sum(outdeg)); if (igraph_vector_size(indeg) != node_count) IGRAPH_ERROR("In- and out-degree sequences must have the same length", IGRAPH_EINVAL); if (igraph_vector_sum(indeg) != edge_count) IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value", IGRAPH_EINVAL); igraph_vector_t edges; IGRAPH_CHECK(igraph_vector_init(&edges, 2*edge_count)); IGRAPH_FINALLY(igraph_vector_destroy, &edges); switch (method) { case IGRAPH_REALIZE_DEGSEQ_SMALLEST: IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true)); break; case IGRAPH_REALIZE_DEGSEQ_LARGEST: IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, false)); break; case IGRAPH_REALIZE_DEGSEQ_INDEX: IGRAPH_CHECK(igraph_i_kleitman_wang_index(outdeg, indeg, &edges)); break; default: IGRAPH_ERROR("Invalid bi-degree sequence realization method", IGRAPH_EINVAL); } igraph_create(graph, &edges, igraph_integer_t(node_count), true); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; }
static int igraph_i_realize_undirected_degree_sequence( igraph_t *graph, const igraph_vector_t *deg, igraph_realize_degseq_t method) { long node_count = igraph_vector_size(deg); long deg_sum = long(igraph_vector_sum(deg)); if (deg_sum % 2 != 0) IGRAPH_ERROR("The sum of degrees must be even for an undirected graph", IGRAPH_EINVAL); igraph_vector_t edges; IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum)); IGRAPH_FINALLY(igraph_vector_destroy, &edges); switch (method) { case IGRAPH_REALIZE_DEGSEQ_SMALLEST: IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, false)); break; case IGRAPH_REALIZE_DEGSEQ_LARGEST: IGRAPH_CHECK(igraph_i_havel_hakimi(deg, &edges, true)); break; case IGRAPH_REALIZE_DEGSEQ_INDEX: IGRAPH_CHECK(igraph_i_havel_hakimi_index(deg, &edges)); break; default: IGRAPH_ERROR("Invalid degree sequence realization method", IGRAPH_EINVAL); } igraph_create(graph, &edges, igraph_integer_t(node_count), false); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; }
/*__________________________________________________________________________ MAIN CYCLE (FLTK CYCLE) ____*/ void mainidle_cb(void*){ //this routine updates the program. //thus, it computes the EVOLUTION double shooted; double dens, err; double totdens, toterr, totimerr; char s[100]; // ---- running controls AND PRINTING if( (amstepping==0 && runningcontrol==1 && graphisloaded==1 && ticks<=maxtime ) || (amstepping==1 && runningcontrol==1 && graphisloaded==1 && ticks<=maxtime && tickstep<=step-1) ) { Evolution(deltat); //PRINTS if((int)printdatabutton->value()==1){ //if have steady state if(usesteady==1){ igraph_matrix_t activation; igraph_matrix_init(&activation,nodesnumber,totrun); igraph_matrix_null(&activation); igraph_vector_t correlation; igraph_vector_init(&correlation,(nodesnumber*nodesnumber)); igraph_vector_null(&correlation); fprintf(output1,"%i ", ticks); fprintf(output2,"%i ", ticks); fprintf(output5,"%i ", ticks); fprintf(output6,"%i ", ticks); totdens=0; toterr=0; for(int i=0;i<nodesnumber;++i){ shooted=0; dens=0; err=0; for(int j=0; j<totrun; ++j){ dens=dens+MATRIX(density,i,j); err=err+((VECTOR(statstate)[i]-MATRIX(density,i,j))*(VECTOR(statstate)[i]-MATRIX(density,i,j))); shooted=shooted+MATRIX(loss,i,j); if(MATRIX(loss,i,j)!=0){++MATRIX(activation,i,j);} } dens=dens/totrun; err=sqrt(err)/totrun; shooted=shooted/totrun; totdens=totdens+dens; toterr=toterr+err; fprintf(output1,"%f ",dens); fprintf(output2,"%f ",err); fprintf(output5,"%f ",shooted); } totdens=totdens/nodesnumber; toterr=toterr/nodesnumber; fprintf(output1,"%f ",totdens); fprintf(output2,"%f ",toterr); // printf("\n\n ACTIVATION MATRIX \n \n"); print_matrix_ur(&activation,stdout); printf("\n\n"); // ---- CORRELATION --- igraph_vector_t meanactivation; igraph_vector_init(&meanactivation,nodesnumber); igraph_vector_null(&meanactivation); //calculate mean activation for (int j=0; j<totrun; ++j) { for (int i=0; i<nodesnumber; ++i) { VECTOR(meanactivation)[i]=VECTOR(meanactivation)[i]+MATRIX(activation,i,j); } } igraph_vector_scale(&meanactivation,1./totrun); //calculate actual correlation for (int x=0; x<nodesnumber ; ++x) { for(int y=0; y<nodesnumber; ++y){ double prod=0; for (int j=0; j<totrun; ++j) { prod=prod+ ( MATRIX(activation,x,j)*MATRIX(activation,y,j) ); } prod=prod/totrun; VECTOR(correlation)[(x*nodesnumber+y)] = prod - (VECTOR(meanactivation)[x]*VECTOR(meanactivation)[y]); } } igraph_vector_destroy(&meanactivation); for (int i=0; i<(nodesnumber*nodesnumber); ++i) { fprintf(output6,"%f ",VECTOR(correlation)[i]); } //calculate error on run igraph_matrix_t distl1; igraph_matrix_t distimel1; igraph_matrix_init(&distl1,nodesnumber,totrun); igraph_matrix_init(&distimel1,nodesnumber,totrun); igraph_matrix_null(&distl1); igraph_matrix_null(&distimel1); igraph_vector_t rundistl1; igraph_vector_t rundistimel1; igraph_vector_init(&rundistl1,totrun); igraph_vector_init(&rundistimel1,totrun); igraph_vector_null(&rundistl1); igraph_vector_null(&rundistimel1); toterr=0; totimerr=0; //for every run for(int j=0;j<totrun;++j){ //i evaluate the distance between the state and the stationary state (toterr) and the distance between old and new density (totimerr) for(int i=0; i<nodesnumber; ++i) { //L1 DISTANCE WRT STATSTATE & DENSITY MATRIX(distl1,i,j)=fabs(VECTOR(statstate)[i]-MATRIX(density,i,j)); //L1 DISTANCE WRT OLD DENSITY & DENSITY MATRIX(distimel1,i,j)=fabs(MATRIX(densityold,i,j)-MATRIX(density,i,j)); } } igraph_matrix_rowsum(&distl1,&rundistl1); igraph_matrix_rowsum(&distimel1,&rundistimel1); igraph_vector_scale(&rundistl1,(1./nodesnumber)); igraph_vector_scale(&rundistimel1,(1./nodesnumber)); toterr= (double)( igraph_vector_sum(&rundistl1) ) / (double)totrun ; totimerr= (double)( igraph_vector_sum(&rundistimel1)) / (double)totrun; igraph_vector_destroy(&rundistl1); igraph_vector_destroy(&rundistimel1); igraph_matrix_destroy(&distl1); igraph_matrix_destroy(&distimel1); fprintf(output2,"%f %f",toterr, totimerr); fprintf(output1,"\n"); fprintf(output2,"\n"); fprintf(output5,"\n"); fprintf(output6,"\n"); //if i have BRIDGES ("clustered" graph), I print the traffic on the BRIDGES, using "output3" file if(isclustered==1){ //for each bridge fprintf(output3,"%i ",ticks); for(int nbri=0; nbri<igraph_matrix_ncol(&bridgeslinks); ++nbri){ for(int i=0; i<igraph_matrix_nrow(&bridgeslinks);++i){ double tfl=0; for(int j=0; j<totrun; ++j){ int beid; beid=(int)MATRIX(bridgeslinks,i,nbri); tfl=tfl+MATRIX(flux,beid,j); } fprintf(output3,"%f ",tfl); } } fprintf(output3,"\n"); } igraph_matrix_destroy(&activation); igraph_vector_destroy(&correlation); } //if i HAVENT STEADY STATE else { fprintf(output1,"%i ", ticks); fprintf(output5,"%i ", ticks); for(int i=0;i<nodesnumber;++i){ shooted=0; dens=0; for(int j=0; j<totrun; ++j){ dens=dens+MATRIX(density,i,j); shooted=shooted+MATRIX(loss,i,j); } dens=dens/totrun; shooted=shooted/totrun; fprintf(output1,"%f " ,dens); fprintf(output5,"%f " ,shooted); } fprintf(output1,"\n"); fprintf(output5,"\n"); } } } if((ticks==maxtime || tickstep==step) && runningcontrol==1){ run(); } // ---- no graph loaded if(graphisloaded==0 && rewrite==1) { runbutton->deactivate(); sprintf(s,"No\nnetwork\nloaded"); databuff->text(s); } // ---- graph loaded else { runbutton->activate(); if(islattice==1 && rewrite==1){ if(istoro==1){ sprintf(s,"Nodes=%i\nToroidal\nLattice\n%iD Side=%i",nodesnumber, latticedim, latticeside); } else{ sprintf(s,"Nodes=%i\nLattice\n%iD Side=%i",nodesnumber, latticedim, latticeside); } databuff->text(s); } else if(rewrite==1){ sprintf(s,"Nodes=%i",nodesnumber); databuff->text(s); } } //have path if(havepath==1 && rewrite==1){pathbuff->text(path); } else if(havepath==0 && rewrite==1){pathbuff->text("No Path");} if(error==1 && rewrite==1){ sprintf(s,errorstring); databuff->text(s); } if (ticks<=maxtime){ scene->redraw(); datascene->redraw(); } rewrite=0; //Fl::repeat_timeout(1.0, mainidle_cb); }
int main() { igraph_t g; igraph_vector_t v, v2; int i, ret; igraph_barabasi_game(&g, 10, 2, 0, 0, 1); if (igraph_ecount(&g) != 18) { return 1; } if (igraph_vcount(&g) != 10) { return 2; } if (!igraph_is_directed(&g)) { return 3; } igraph_vector_init(&v, 0); igraph_get_edgelist(&g, &v, 0); for (i=0; i<igraph_ecount(&g); i++) { if (VECTOR(v)[2*i] <= VECTOR(v)[2*i+1]) { return 4; } } igraph_destroy(&g); /* out degree sequence */ igraph_vector_resize(&v, 10); VECTOR(v)[0]=0; VECTOR(v)[1]=1; VECTOR(v)[2]=3; VECTOR(v)[3]=3; VECTOR(v)[4]=4; VECTOR(v)[5]=5; VECTOR(v)[6]=6; VECTOR(v)[7]=7; VECTOR(v)[8]=8; VECTOR(v)[9]=9; igraph_barabasi_game(&g, 10, 0, &v, 0, 1); if (igraph_ecount(&g) != igraph_vector_sum(&v)) { return 5; } igraph_vector_init(&v2, 0); igraph_degree(&g, &v2, igraph_vss_all(), IGRAPH_OUT, 1); for (i=0; i<igraph_vcount(&g); i++) { if (VECTOR(v)[i] != VECTOR(v2)[i]) { return 6; } } igraph_vector_destroy(&v); igraph_vector_destroy(&v2); igraph_destroy(&g); /* outpref, we cannot really test this quantitatively, would need to set random seed */ igraph_barabasi_game(&g, 10, 2, 0, 1, 1); igraph_vector_init(&v, 0); igraph_get_edgelist(&g, &v, 0); for (i=0; i<igraph_ecount(&g); i++) { if (VECTOR(v)[2*i] <= VECTOR(v)[2*i+1]) { return 7; } } if (!igraph_is_directed(&g)) { return 8; } igraph_vector_destroy(&v); igraph_destroy(&g); /* Error tests */ igraph_set_error_handler(igraph_error_handler_ignore); ret=igraph_barabasi_game(&g, -10, 1, 0, 0, 0); if (ret != IGRAPH_EINVAL) { return 9; } ret=igraph_barabasi_game(&g, 10, -2, 0, 0, 0); if (ret != IGRAPH_EINVAL) { return 10; } igraph_vector_init(&v, 9); ret=igraph_barabasi_game(&g, 10, 0, &v, 0, 0); if (ret != IGRAPH_EINVAL) { return 11; } igraph_vector_destroy(&v); return 0; }
/** * \ingroup communities * \function igraph_i_community_multilevel_step * \brief Performs a single step of the multi-level modularity optimization method * * This function implements a single step of the multi-level modularity optimization * algorithm for finding community structure, see VD Blondel, J-L Guillaume, * R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large * networks, http://arxiv.org/abs/0803.0476 for the details. * * This function was contributed by Tom Gregorovic. * * \param graph The input graph. It must be an undirected graph. * \param weights Numeric vector containing edge weights. If \c NULL, every edge * has equal weight. The weights are expected to be non-negative. * \param membership The membership vector, the result is returned here. * For each vertex it gives the ID of its community. * \param modularity The modularity of the partition is returned here. * \c NULL means that the modularity is not needed. * \return Error code. * * Time complexity: in average near linear on sparse graphs. */ int igraph_i_community_multilevel_step(igraph_t *graph, igraph_vector_t *weights, igraph_vector_t *membership, igraph_real_t *modularity) { long int i, j; long int vcount = igraph_vcount(graph); long int ecount = igraph_ecount(graph); igraph_integer_t ffrom, fto; igraph_real_t q, pass_q; int pass; igraph_bool_t changed = 0; igraph_vector_t links_community; igraph_vector_t links_weight; igraph_vector_t edges; igraph_vector_t temp_membership; igraph_i_multilevel_community_list communities; /* Initial sanity checks on the input parameters */ if (igraph_is_directed(graph)) { IGRAPH_ERROR("multi-level community detection works for undirected graphs only", IGRAPH_UNIMPLEMENTED); } if (igraph_vector_size(weights) < igraph_ecount(graph)) IGRAPH_ERROR("multi-level community detection: weight vector too short", IGRAPH_EINVAL); if (igraph_vector_any_smaller(weights, 0)) IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL); /* Initialize data structures */ IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0); IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&temp_membership, vcount); IGRAPH_CHECK(igraph_vector_resize(membership, vcount)); /* Initialize list of communities from graph vertices */ communities.vertices_no = vcount; communities.communities_no = vcount; communities.weights = weights; communities.weight_sum = 2 * igraph_vector_sum(weights); communities.membership = membership; communities.item = igraph_Calloc(vcount, igraph_i_multilevel_community); if (communities.item == 0) { IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, communities.item); /* Still initializing the communities data structure */ for (i=0; i < vcount; i++) { VECTOR(*communities.membership)[i] = i; communities.item[i].size = 1; communities.item[i].weight_inside = 0; communities.item[i].weight_all = 0; } /* Some more initialization :) */ for (i = 0; i < ecount; i++) { igraph_real_t weight = 1; igraph_edge(graph, (igraph_integer_t) i, &ffrom, &fto); weight = VECTOR(*weights)[i]; communities.item[(long int) ffrom].weight_all += weight; communities.item[(long int) fto].weight_all += weight; if (ffrom == fto) communities.item[(long int) ffrom].weight_inside += 2*weight; } q = igraph_i_multilevel_community_modularity(&communities); pass = 1; do { /* Pass begin */ long int temp_communities_no = communities.communities_no; pass_q = q; changed = 0; /* Save the current membership, it will be restored in case of worse result */ IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership)); for (i = 0; i < vcount; i++) { /* Exclude vertex from its current community */ igraph_real_t weight_all = 0; igraph_real_t weight_inside = 0; igraph_real_t weight_loop = 0; igraph_real_t max_q_gain = 0; igraph_real_t max_weight; long int old_id, new_id, n; igraph_i_multilevel_community_links(graph, &communities, (igraph_integer_t) i, &edges, &weight_all, &weight_inside, &weight_loop, &links_community, &links_weight); old_id = (long int)VECTOR(*(communities.membership))[i]; new_id = old_id; /* Update old community */ igraph_vector_set(communities.membership, i, -1); communities.item[old_id].size--; if (communities.item[old_id].size == 0) {communities.communities_no--;} communities.item[old_id].weight_all -= weight_all; communities.item[old_id].weight_inside -= 2*weight_inside + weight_loop; /* debug("Remove %ld all: %lf Inside: %lf\n", i, -weight_all, -2*weight_inside + weight_loop); */ /* Find new community to join with the best modification gain */ max_q_gain = 0; max_weight = weight_inside; n = igraph_vector_size(&links_community); igraph_vector_sort(&links_community); for (j = 0; j < n; j++) { long int c = (long int) VECTOR(links_community)[j]; igraph_real_t w = VECTOR(links_weight)[j]; igraph_real_t q_gain = igraph_i_multilevel_community_modularity_gain(&communities, (igraph_integer_t) c, (igraph_integer_t) i, weight_all, w); /* debug("Link %ld -> %ld weight: %lf gain: %lf\n", i, c, (double) w, (double) q_gain); */ if (q_gain > max_q_gain) { new_id = c; max_q_gain = q_gain; max_weight = w; } } /* debug("Added vertex %ld to community %ld (gain %lf).\n", i, new_id, (double) max_q_gain); */ /* Add vertex to "new" community and update it */ igraph_vector_set(communities.membership, i, new_id); if (communities.item[new_id].size == 0) {communities.communities_no++;} communities.item[new_id].size++; communities.item[new_id].weight_all += weight_all; communities.item[new_id].weight_inside += 2*max_weight + weight_loop; if (new_id != old_id) { changed++; } } q = igraph_i_multilevel_community_modularity(&communities); if (changed && (q > pass_q)) { /* debug("Pass %d (changed: %d) Communities: %ld Modularity from %lf to %lf\n", pass, changed, communities.communities_no, (double) pass_q, (double) q); */ pass++; } else { /* No changes or the modularity became worse, restore last membership */ IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership)); communities.communities_no = temp_communities_no; break; } IGRAPH_ALLOW_INTERRUPTION(); } while (changed && (q > pass_q)); /* Pass end */ if (modularity) { *modularity = q; } /* debug("Result Communities: %ld Modularity: %lf\n", communities.communities_no, (double) q); */ IGRAPH_CHECK(igraph_reindex_membership(membership, 0)); /* Shrink the nodes of the graph according to the present community structure * and simplify the resulting graph */ /* TODO: check if we really need to copy temp_membership */ IGRAPH_CHECK(igraph_vector_update(&temp_membership, membership)); IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); igraph_vector_destroy(&temp_membership); IGRAPH_FINALLY_CLEAN(1); /* Update edge weights after shrinking and simplification */ /* Here we reuse the edges vector as we don't need the previous contents anymore */ /* TODO: can we use igraph_simplify here? */ IGRAPH_CHECK(igraph_i_multilevel_simplify_multiple(graph, &edges)); /* We reuse the links_weight vector to store the old edge weights */ IGRAPH_CHECK(igraph_vector_update(&links_weight, weights)); igraph_vector_fill(weights, 0); for (i = 0; i < ecount; i++) { VECTOR(*weights)[(long int)VECTOR(edges)[i]] += VECTOR(links_weight)[i]; } igraph_free(communities.item); igraph_vector_destroy(&links_community); igraph_vector_destroy(&links_weight); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(4); return 0; }
/** * \function igraph_community_fastgreedy * \brief Finding community structure by greedy optimization of modularity * * This function implements the fast greedy modularity optimization * algorithm for finding community structure, see * A Clauset, MEJ Newman, C Moore: Finding community structure in very * large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the * details. * * </para><para> * Some improvements proposed in K Wakita, T Tsurumi: Finding community * structure in mega-scale social networks, * http://www.arxiv.org/abs/cs.CY/0702048v1 have also been implemented. * * \param graph The input graph. It must be a simple graph, i.e. a graph * without multiple and without loop edges. This is checked and an * error message is given for non-simple graphs. * \param weights Potentially a numeric vector containing edge * weights. Supply a null pointer here for unweighted graphs. The * weights are expected to be non-negative. * \param merges Pointer to an initialized matrix or NULL, the result of the * computation is stored here. The matrix has two columns and each * merge corresponds to one merge, the ids of the two merged * components are stored. The component ids are numbered from zero and * the first \c n components are the individual vertices, \c n is * the number of vertices in the graph. Component \c n is created * in the first merge, component \c n+1 in the second merge, etc. * The matrix will be resized as needed. If this argument is NULL * then it is ignored completely. * \param modularity Pointer to an initialized matrix or NULL pointer, * in the former case the modularity scores along the stages of the * computation are recorded here. The vector will be resized as * needed. * \return Error code. * * \sa \ref igraph_community_walktrap(), \ref * igraph_community_edge_betweenness() for other community detection * algorithms, \ref igraph_community_to_membership() to convert the * dendrogram to a membership vector. * * Time complexity: O(|E||V|log|V|) in the worst case, * O(|E|+|V|log^2|V|) typically, |V| is the number of vertices, |E| is * the number of edges. */ int igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, igraph_matrix_t *merges, igraph_vector_t *modularity) { long int no_of_edges, no_of_nodes, no_of_joins, total_joins; long int i, j, k, n, m, from, to, dummy; igraph_integer_t ffrom, fto; igraph_eit_t edgeit; igraph_i_fastgreedy_commpair *pairs, *p1, *p2; igraph_i_fastgreedy_community_list communities; igraph_vector_t a; igraph_real_t q, maxq, *dq, weight_sum; igraph_bool_t simple; /*long int join_order[] = { 16,5, 5,6, 6,0, 4,0, 10,0, 26,29, 29,33, 23,33, 27,33, 25,24, 24,31, 12,3, 21,1, 30,8, 8,32, 9,2, 17,1, 11,0, 7,3, 3,2, 13,2, 1,2, 28,31, 31,33, 22,32, 18,32, 20,32, 32,33, 15,33, 14,33, 0,19, 19,2, -1,-1 };*/ /*long int join_order[] = { 43,42, 42,41, 44,41, 41,36, 35,36, 37,36, 36,29, 38,29, 34,29, 39,29, 33,29, 40,29, 32,29, 14,29, 30,29, 31,29, 6,18, 18,4, 23,4, 21,4, 19,4, 27,4, 20,4, 22,4, 26,4, 25,4, 24,4, 17,4, 0,13, 13,2, 1,2, 11,2, 8,2, 5,2, 3,2, 10,2, 9,2, 7,2, 2,28, 28,15, 12,15, 29,16, 4,15, -1,-1 };*/ no_of_nodes = igraph_vcount(graph); no_of_edges = igraph_ecount(graph); if (igraph_is_directed(graph)) { IGRAPH_ERROR("fast greedy community detection works for undirected graphs only", IGRAPH_UNIMPLEMENTED); } total_joins=no_of_nodes-1; if (weights != 0) { if (igraph_vector_size(weights) < igraph_ecount(graph)) IGRAPH_ERROR("fast greedy community detection: weight vector too short", IGRAPH_EINVAL); if (igraph_vector_any_smaller(weights, 0)) IGRAPH_ERROR("weights must be positive", IGRAPH_EINVAL); weight_sum = igraph_vector_sum(weights); } else weight_sum = no_of_edges; IGRAPH_CHECK(igraph_is_simple(graph, &simple)); if (!simple) { IGRAPH_ERROR("fast-greedy community finding works only on simple graphs", IGRAPH_EINVAL); } if (merges != 0) { IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2)); igraph_matrix_null(merges); } if (modularity != 0) { IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins+1)); } /* Create degree vector */ IGRAPH_VECTOR_INIT_FINALLY(&a, no_of_nodes); if (weights) { debug("Calculating weighted degrees\n"); for (i=0; i < no_of_edges; i++) { VECTOR(a)[(long int)IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; VECTOR(a)[(long int)IGRAPH_TO(graph, i)] += VECTOR(*weights)[i]; } } else { debug("Calculating degrees\n"); IGRAPH_CHECK(igraph_degree(graph, &a, igraph_vss_all(), IGRAPH_ALL, 0)); } /* Create list of communities */ debug("Creating community list\n"); communities.n = no_of_nodes; communities.no_of_communities = no_of_nodes; communities.e = (igraph_i_fastgreedy_community*)calloc(no_of_nodes, sizeof(igraph_i_fastgreedy_community)); if (communities.e == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, communities.e); communities.heap = (igraph_i_fastgreedy_community**)calloc(no_of_nodes, sizeof(igraph_i_fastgreedy_community*)); if (communities.heap == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, communities.heap); communities.heapindex = (igraph_integer_t*)calloc(no_of_nodes, sizeof(igraph_integer_t)); if (communities.heapindex == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY_CLEAN(2); IGRAPH_FINALLY(igraph_i_fastgreedy_community_list_destroy, &communities); for (i=0; i<no_of_nodes; i++) { igraph_vector_ptr_init(&communities.e[i].neis, 0); communities.e[i].id = i; communities.e[i].size = 1; } /* Create list of community pairs from edges */ debug("Allocating dq vector\n"); dq = (igraph_real_t*)calloc(no_of_edges, sizeof(igraph_real_t)); if (dq == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, dq); debug("Creating community pair list\n"); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); pairs = (igraph_i_fastgreedy_commpair*)calloc(2*no_of_edges, sizeof(igraph_i_fastgreedy_commpair)); if (pairs == 0) { IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); } IGRAPH_FINALLY(free, pairs); i=j=0; while (!IGRAPH_EIT_END(edgeit)) { long int eidx = IGRAPH_EIT_GET(edgeit); igraph_edge(graph, eidx, &ffrom, &fto); /* Create the pairs themselves */ from = (long int)ffrom; to = (long int)fto; if (from == to) { IGRAPH_ERROR("loop edge detected, simplify the graph before starting community detection", IGRAPH_EINVAL); } if (from>to) { dummy=from; from=to; to=dummy; } if (weights) { dq[j]=2*(VECTOR(*weights)[eidx]/(weight_sum*2.0) - VECTOR(a)[from]*VECTOR(a)[to]/(4.0*weight_sum*weight_sum)); } else { dq[j]=2*(1.0/(no_of_edges*2.0) - VECTOR(a)[from]*VECTOR(a)[to]/(4.0*no_of_edges*no_of_edges)); } pairs[i].first = from; pairs[i].second = to; pairs[i].dq = &dq[j]; pairs[i].opposite = &pairs[i+1]; pairs[i+1].first = to; pairs[i+1].second = from; pairs[i+1].dq = pairs[i].dq; pairs[i+1].opposite = &pairs[i]; /* Link the pair to the communities */ igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i]); igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i+1]); /* Update maximums */ if (communities.e[from].maxdq==0 || *communities.e[from].maxdq->dq < *pairs[i].dq) communities.e[from].maxdq = &pairs[i]; if (communities.e[to].maxdq==0 || *communities.e[to].maxdq->dq < *pairs[i+1].dq) communities.e[to].maxdq = &pairs[i+1]; /* Iterate */ i+=2; j++; IGRAPH_EIT_NEXT(edgeit); } igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); /* Sorting community neighbor lists by community IDs */ debug("Sorting community neighbor lists\n"); for (i=0, j=0; i<no_of_nodes; i++) { igraph_vector_ptr_sort(&communities.e[i].neis, igraph_i_fastgreedy_commpair_cmp); /* Isolated vertices won't be stored in the heap (to avoid maxdq == 0) */ if (VECTOR(a)[i] > 0) { communities.heap[j] = &communities.e[i]; communities.heapindex[i] = j; j++; } else { communities.heapindex[i] = -1; } } communities.no_of_communities = j; /* Calculate proper vector a (see paper) and initial modularity */ q=0; igraph_vector_scale(&a, 1.0/(2.0 * (weights ? weight_sum : no_of_edges))); for (i=0; i<no_of_nodes; i++) q -= VECTOR(a)[i]*VECTOR(a)[i]; maxq=q; /* Initializing community heap */ debug("Initializing community heap\n"); igraph_i_fastgreedy_community_list_build_heap(&communities); debug("Initial modularity: %.4f\n", q); /* Let's rock ;) */ no_of_joins=0; while (no_of_joins<total_joins) { IGRAPH_ALLOW_INTERRUPTION(); IGRAPH_PROGRESS("fast greedy community detection", no_of_joins*100.0/total_joins, 0); /* Store the modularity */ if (modularity) VECTOR(*modularity)[no_of_joins] = q; /* Some debug info if needed */ /* igraph_i_fastgreedy_community_list_check_heap(&communities); */ #ifdef DEBUG debug("===========================================\n"); for (i=0; i<communities.n; i++) { if (communities.e[i].maxdq == 0) { debug("Community #%ld: PASSIVE\n", i); continue; } debug("Community #%ld\n ", i); for (j=0; j<igraph_vector_ptr_size(&communities.e[i].neis); j++) { p1=(igraph_i_fastgreedy_commpair*)VECTOR(communities.e[i].neis)[j]; debug(" (%ld,%ld,%.4f)", p1->first, p1->second, *p1->dq); } p1=communities.e[i].maxdq; debug("\n Maxdq: (%ld,%ld,%.4f)\n", p1->first, p1->second, *p1->dq); } debug("Global maxdq is: (%ld,%ld,%.4f)\n", communities.heap[0]->maxdq->first, communities.heap[0]->maxdq->second, *communities.heap[0]->maxdq->dq); for (i=0; i<communities.no_of_communities; i++) debug("(%ld,%ld,%.4f) ", communities.heap[i]->maxdq->first, communities.heap[i]->maxdq->second, *communities.heap[0]->maxdq->dq); debug("\n"); #endif if (communities.heap[0] == 0) break; /* no more communities */ if (communities.heap[0]->maxdq == 0) break; /* there are only isolated comms */ to=communities.heap[0]->maxdq->second; from=communities.heap[0]->maxdq->first; debug("Q[%ld] = %.7f\tdQ = %.7f\t |H| = %ld\n", no_of_joins, q, *communities.heap[0]->maxdq->dq, no_of_nodes-no_of_joins-1); /* DEBUG */ /* from=join_order[no_of_joins*2]; to=join_order[no_of_joins*2+1]; if (to == -1) break; for (i=0; i<igraph_vector_ptr_size(&communities.e[to].neis); i++) { p1=(igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; if (p1->second == from) communities.maxdq = p1; } */ n = igraph_vector_ptr_size(&communities.e[to].neis); m = igraph_vector_ptr_size(&communities.e[from].neis); /*if (n>m) { dummy=n; n=m; m=dummy; dummy=to; to=from; from=dummy; }*/ debug(" joining: %ld <- %ld\n", to, from); q += *communities.heap[0]->maxdq->dq; /* Merge the second community into the first */ i = j = 0; while (i<n && j<m) { p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j]; debug("Pairs: %ld-%ld and %ld-%ld\n", p1->first, p1->second, p2->first, p2->second); if (p1->second < p2->second) { /* Considering p1 from now on */ debug(" Considering: %ld-%ld\n", p1->first, p1->second); if (p1->second == from) { debug(" WILL REMOVE: %ld-%ld\n", to, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, -2*VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq-2*VECTOR(a)[from]*VECTOR(a)[p1->second]); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2*VECTOR(a)[from]*VECTOR(a)[p1->second]); } i++; } else if (p1->second == p2->second) { /* p1->first, p1->second and p2->first form a triangle */ debug(" Considering: %ld-%ld and %ld-%ld\n", p1->first, p1->second, p2->first, p2->second); /* Update dq value */ debug(" TRIANGLE: %ld-%ld-%ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, *p2->dq, p1->first, p1->second, *p1->dq+*p2->dq); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq + *p2->dq); igraph_i_fastgreedy_community_remove_nei(&communities, p1->second, from); i++; j++; } else { debug(" Considering: %ld-%ld\n", p2->first, p2->second); if (p2->second == to) { debug(" WILL REMOVE: %ld-%ld\n", p2->second, p2->first); } else { /* chain, case 2 */ debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", to, p2->second, from, to, p2->second, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); p2->opposite->second=to; /* need to re-sort community nei list `p2->second` */ /* TODO: quicksort is O(n*logn), although we could do a deletion and * insertion which can be done in O(logn) if deletion is O(1) */ debug(" Re-sorting community %ld\n", p2->second); igraph_vector_ptr_sort(&communities.e[p2->second].neis, igraph_i_fastgreedy_commpair_cmp); /* link from.neis[j] to the current place in to.neis if * from.neis[j] != to */ p2->first=to; IGRAPH_CHECK(igraph_vector_ptr_insert(&communities.e[to].neis,i,p2)); n++; i++; if (*p2->dq > *communities.e[to].maxdq->dq) { communities.e[to].maxdq = p2; k=igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); igraph_i_fastgreedy_community_list_sift_up(&communities, k); } igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq - 2*VECTOR(a)[to]*VECTOR(a)[p2->second]); } j++; } } while (i<n) { p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; if (p1->second == from) { debug(" WILL REMOVE: %ld-%ld\n", p1->first, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", to, p1->second, from, *p1->dq, -2*VECTOR(a)[from]*VECTOR(a)[p1->second], p1->first, p1->second, *p1->dq-2*VECTOR(a)[from]*VECTOR(a)[p1->second]); igraph_i_fastgreedy_community_update_dq(&communities, p1, *p1->dq - 2*VECTOR(a)[from]*VECTOR(a)[p1->second]); } i++; } while (j<m) { p2 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[from].neis)[j]; if (to == p2->second) { j++; continue; } /* chain, case 2 */ debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", to, p2->second, from, p1->first, p2->second, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); p2->opposite->second=to; /* need to re-sort community nei list `p2->second` */ /* TODO: quicksort is O(n*logn), although we could do a deletion and * insertion which can be done in O(logn) if deletion is O(1) */ debug(" Re-sorting community %ld\n", p2->second); igraph_vector_ptr_sort(&communities.e[p2->second].neis, igraph_i_fastgreedy_commpair_cmp); /* link from.neis[j] to the current place in to.neis if * from.neis[j] != to */ p2->first=to; IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis,p2)); if (*p2->dq > *communities.e[to].maxdq->dq) { communities.e[to].maxdq = p2; k=igraph_i_fastgreedy_community_list_find_in_heap(&communities, to); igraph_i_fastgreedy_community_list_sift_up(&communities, k); } igraph_i_fastgreedy_community_update_dq(&communities, p2, *p2->dq-2*VECTOR(a)[to]*VECTOR(a)[p2->second]); j++; } /* Now, remove community `from` from the neighbors of community `to` */ if (communities.no_of_communities > 2) { debug(" REMOVING: %ld-%ld\n", to, from); igraph_i_fastgreedy_community_remove_nei(&communities, to, from); i=igraph_i_fastgreedy_community_list_find_in_heap(&communities, from); igraph_i_fastgreedy_community_list_remove(&communities, i); } communities.e[from].maxdq=0; /* Update community sizes */ communities.e[to].size += communities.e[from].size; communities.e[from].size = 0; /* record what has been merged */ /* igraph_vector_ptr_clear is not enough here as it won't free * the memory consumed by communities.e[from].neis. Thanks * to Tom Gregorovic for pointing that out. */ igraph_vector_ptr_destroy(&communities.e[from].neis); if (merges) { MATRIX(*merges, no_of_joins, 0) = communities.e[to].id; MATRIX(*merges, no_of_joins, 1) = communities.e[from].id; communities.e[to].id = no_of_nodes+no_of_joins; } /* Update vector a */ VECTOR(a)[to] += VECTOR(a)[from]; VECTOR(a)[from] = 0.0; no_of_joins++; } /* TODO: continue merging when some isolated communities remained. Always * joining the communities with the least number of nodes results in the * smallest decrease in modularity every step. Now we're simply deleting * the excess rows from the merge matrix */ if (no_of_joins < total_joins) { long int *ivec; ivec=igraph_Calloc(igraph_matrix_nrow(merges), long int); if (ivec == 0) IGRAPH_ERROR("can't run fast greedy community detection", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, ivec); for (i=0; i<no_of_joins; i++) ivec[i] = i+1; igraph_matrix_permdelete_rows(merges, ivec, total_joins-no_of_joins); free(ivec); IGRAPH_FINALLY_CLEAN(1); }