int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t fw_prob, igraph_real_t bw_factor, igraph_integer_t pambs, igraph_bool_t directed) { igraph_vector_long_t visited; long int no_of_nodes=nodes, actnode, i; igraph_vector_t edges; igraph_vector_t *inneis, *outneis; igraph_i_forest_fire_data_t data; igraph_dqueue_t neiq; long int ambs=pambs; igraph_real_t param_geom_out=1-fw_prob; igraph_real_t param_geom_in=1-fw_prob*bw_factor; if (fw_prob < 0) { IGRAPH_ERROR("Forest fire model: 'fw_prob' should be between non-negative", IGRAPH_EINVAL); } if (bw_factor < 0) { IGRAPH_ERROR("Forest fire model: 'bw_factor' should be non-negative", IGRAPH_EINVAL); } if (ambs < 0) { IGRAPH_ERROR("Number of ambassadors ('ambs') should be non-negative", IGRAPH_EINVAL); } if (fw_prob == 0 || ambs == 0) { IGRAPH_WARNING("'fw_prob or ambs is zero, creating empty graph"); IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); return 0; } IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); inneis=igraph_Calloc(no_of_nodes, igraph_vector_t); if (!inneis) { IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, inneis); outneis=igraph_Calloc(no_of_nodes, igraph_vector_t); if (!outneis) { IGRAPH_ERROR("Cannot run forest fire model", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, outneis); data.inneis=inneis; data.outneis=outneis; data.no_of_nodes=no_of_nodes; IGRAPH_FINALLY(igraph_i_forest_fire_free, &data); for (i=0; i<no_of_nodes; i++) { IGRAPH_CHECK(igraph_vector_init(inneis+i, 0)); IGRAPH_CHECK(igraph_vector_init(outneis+i, 0)); } IGRAPH_CHECK(igraph_vector_long_init(&visited, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &visited); IGRAPH_DQUEUE_INIT_FINALLY(&neiq, 10); RNG_BEGIN(); #define ADD_EDGE_TO(nei) \ if (VECTOR(visited)[(nei)] != actnode+1) { \ VECTOR(visited)[(nei)] = actnode+1; \ IGRAPH_CHECK(igraph_dqueue_push(&neiq, nei)); \ IGRAPH_CHECK(igraph_vector_push_back(&edges, actnode)); \ IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); \ IGRAPH_CHECK(igraph_vector_push_back(outneis+actnode, nei)); \ IGRAPH_CHECK(igraph_vector_push_back(inneis+nei, actnode)); \ } IGRAPH_PROGRESS("Forest fire: ", 0.0, NULL); for (actnode=1; actnode < no_of_nodes; actnode++) { IGRAPH_PROGRESS("Forest fire: ", 100.0*actnode/no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); /* We don't want to visit the current vertex */ VECTOR(visited)[actnode] = actnode+1; /* Choose ambassador(s) */ for (i=0; i<ambs; i++) { long int a=RNG_INTEGER(0, actnode-1); ADD_EDGE_TO(a); } while (!igraph_dqueue_empty(&neiq)) { long int actamb=(long int) igraph_dqueue_pop(&neiq); igraph_vector_t *outv=outneis+actamb; igraph_vector_t *inv=inneis+actamb; long int no_in=igraph_vector_size(inv); long int no_out=igraph_vector_size(outv); long int neis_out=(long int) RNG_GEOM(param_geom_out); long int neis_in=(long int) RNG_GEOM(param_geom_in); /* outgoing neighbors */ if (neis_out >= no_out) { for (i=0; i<no_out; i++) { long int nei=(long int) VECTOR(*outv)[i]; ADD_EDGE_TO(nei); } } else { long int oleft=no_out; for (i=0; i<neis_out && oleft > 0; ) { long int which=RNG_INTEGER(0, oleft-1); long int nei=(long int) VECTOR(*outv)[which]; VECTOR(*outv)[which] = VECTOR(*outv)[oleft-1]; VECTOR(*outv)[oleft-1] = nei; if (VECTOR(visited)[nei] != actnode+1) { ADD_EDGE_TO(nei); i++; } oleft--; } } /* incoming neighbors */ if (neis_in >= no_in) { for (i=0; i<no_in; i++) { long int nei=(long int) VECTOR(*inv)[i]; ADD_EDGE_TO(nei); } } else { long int ileft=no_in; for (i=0; i<neis_in && ileft > 0; ) { long int which=RNG_INTEGER(0, ileft-1); long int nei=(long int) VECTOR(*inv)[which]; VECTOR(*inv)[which] = VECTOR(*inv)[ileft-1]; VECTOR(*inv)[ileft-1] = nei; if (VECTOR(visited)[nei] != actnode+1) { ADD_EDGE_TO(nei); i++; } ileft--; } } } /* while neiq not empty */ } /* actnode < no_of_nodes */ #undef ADD_EDGE_TO RNG_END(); IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL); igraph_dqueue_destroy(&neiq); igraph_vector_long_destroy(&visited); igraph_i_forest_fire_free(&data); igraph_free(outneis); igraph_free(inneis); IGRAPH_FINALLY_CLEAN(5); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return 0; }
int igraph_i_maximal_cliques(const igraph_t *graph, igraph_i_maximal_clique_func_t func, void* data) { int directed=igraph_is_directed(graph); long int i, j, k, l; igraph_integer_t no_of_nodes, nodes_to_check, nodes_done; igraph_integer_t best_cand = 0, best_cand_degree = 0, best_fini_cand_degree; igraph_adjlist_t adj_list; igraph_stack_ptr_t stack; igraph_i_maximal_cliques_stack_frame frame, *new_frame_ptr; igraph_vector_t clique, new_cand, new_fini, cn, best_cand_nbrs, best_fini_cand_nbrs; igraph_bool_t cont = 1; int assret; if (directed) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); no_of_nodes = igraph_vcount(graph); if (no_of_nodes == 0) return IGRAPH_SUCCESS; /* Construct an adjacency list representation */ IGRAPH_CHECK(igraph_adjlist_init(graph, &adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_list); IGRAPH_CHECK(igraph_adjlist_simplify(&adj_list)); igraph_adjlist_sort(&adj_list); /* Initialize stack */ IGRAPH_CHECK(igraph_stack_ptr_init(&stack, 0)); IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_destroy, &stack); /* Create the initial (empty) clique */ IGRAPH_VECTOR_INIT_FINALLY(&clique, 0); /* Initialize new_cand, new_fini, cn, best_cand_nbrs and best_fini_cand_nbrs (will be used later) */ IGRAPH_VECTOR_INIT_FINALLY(&new_cand, 0); IGRAPH_VECTOR_INIT_FINALLY(&new_fini, 0); IGRAPH_VECTOR_INIT_FINALLY(&cn, 0); IGRAPH_VECTOR_INIT_FINALLY(&best_cand_nbrs, 0); IGRAPH_VECTOR_INIT_FINALLY(&best_fini_cand_nbrs, 0); /* Find the vertex with the highest degree */ best_cand = 0; best_cand_degree = igraph_vector_size(igraph_adjlist_get(&adj_list, 0)); for (i = 1; i < no_of_nodes; i++) { j = igraph_vector_size(igraph_adjlist_get(&adj_list, i)); if (j > best_cand_degree) { best_cand = i; best_cand_degree = j; } } /* Create the initial stack frame */ IGRAPH_CHECK(igraph_vector_init_seq(&frame.cand, 0, no_of_nodes-1)); IGRAPH_FINALLY(igraph_vector_destroy, &frame.cand); IGRAPH_CHECK(igraph_vector_init(&frame.fini, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &frame.fini); IGRAPH_CHECK(igraph_vector_init(&frame.cand_filtered, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &frame.cand_filtered); IGRAPH_CHECK(igraph_vector_difference_sorted(&frame.cand, igraph_adjlist_get(&adj_list, best_cand), &frame.cand_filtered)); IGRAPH_FINALLY_CLEAN(3); IGRAPH_FINALLY(igraph_i_maximal_cliques_stack_frame_destroy, &frame); /* TODO: frame.cand and frame.fini should be a set instead of a vector */ /* Main loop starts here */ nodes_to_check = igraph_vector_size(&frame.cand_filtered); nodes_done = 0; while (!igraph_vector_empty(&frame.cand_filtered) || !igraph_stack_ptr_empty(&stack)) { if (igraph_vector_empty(&frame.cand_filtered)) { /* No candidates left to check in this stack frame, pop out the previous stack frame */ igraph_i_maximal_cliques_stack_frame *newframe = igraph_stack_ptr_pop(&stack); igraph_i_maximal_cliques_stack_frame_destroy(&frame); frame = *newframe; free(newframe); if (igraph_stack_ptr_size(&stack) == 1) { /* We will be using the next candidate node in the next iteration, so we can increase * nodes_done by 1 */ nodes_done++; } /* For efficiency reasons, we only check for interruption and show progress here */ IGRAPH_PROGRESS("Maximal cliques: ", 100.0 * nodes_done / nodes_to_check, NULL); IGRAPH_ALLOW_INTERRUPTION(); igraph_vector_pop_back(&clique); continue; } /* Try the next node in the clique */ i = igraph_vector_pop_back(&frame.cand_filtered); IGRAPH_CHECK(igraph_vector_push_back(&clique, i)); /* Remove the node from the candidate list */ assret=igraph_vector_binsearch(&frame.cand, i, &j); assert(assret); igraph_vector_remove(&frame.cand, j); /* Add the node to the finished list */ assret = !igraph_vector_binsearch(&frame.fini, i, &j); assert(assret); IGRAPH_CHECK(igraph_vector_insert(&frame.fini, j, i)); /* Create new_cand and new_fini */ IGRAPH_CHECK(igraph_vector_intersect_sorted(&frame.cand, igraph_adjlist_get(&adj_list, i), &new_cand)); IGRAPH_CHECK(igraph_vector_intersect_sorted(&frame.fini, igraph_adjlist_get(&adj_list, i), &new_fini)); /* Do we have anything more to search? */ if (igraph_vector_empty(&new_cand)) { if (igraph_vector_empty(&new_fini)) { /* We have a maximal clique here */ IGRAPH_CHECK(func(&clique, data, &cont)); if (!cont) { /* The callback function requested to stop the search */ break; } } igraph_vector_pop_back(&clique); continue; } if (igraph_vector_empty(&new_fini) && igraph_vector_size(&new_cand) == 1) { /* Shortcut: only one node left */ IGRAPH_CHECK(igraph_vector_push_back(&clique, VECTOR(new_cand)[0])); IGRAPH_CHECK(func(&clique, data, &cont)); if (!cont) { /* The callback function requested to stop the search */ break; } igraph_vector_pop_back(&clique); igraph_vector_pop_back(&clique); continue; } /* Find the next best candidate node in new_fini */ l = igraph_vector_size(&new_cand); best_cand_degree = -1; j = igraph_vector_size(&new_fini); for (i = 0; i < j; i++) { k = (long int)VECTOR(new_fini)[i]; IGRAPH_CHECK(igraph_vector_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn)); if (igraph_vector_size(&cn) > best_cand_degree) { best_cand_degree = igraph_vector_size(&cn); IGRAPH_CHECK(igraph_vector_update(&best_fini_cand_nbrs, &cn)); if (best_cand_degree == l) { /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */ break; } } } /* Shortcut here: we don't have to examine new_cand */ if (best_cand_degree == l) { igraph_vector_pop_back(&clique); continue; } /* Still finding best candidate node */ best_fini_cand_degree = best_cand_degree; best_cand_degree = -1; j = igraph_vector_size(&new_cand); l = l - 1; for (i = 0; i < j; i++) { k = (long int)VECTOR(new_cand)[i]; IGRAPH_CHECK(igraph_vector_intersect_sorted(&new_cand, igraph_adjlist_get(&adj_list, k), &cn)); if (igraph_vector_size(&cn) > best_cand_degree) { best_cand_degree = igraph_vector_size(&cn); IGRAPH_CHECK(igraph_vector_update(&best_cand_nbrs, &cn)); if (best_cand_degree == l) { /* Cool, we surely have the best candidate node here as best_cand_degree can't get any better */ break; } } } /* Create a new stack frame in case we back out later */ new_frame_ptr = igraph_Calloc(1, igraph_i_maximal_cliques_stack_frame); if (new_frame_ptr == 0) { IGRAPH_ERROR("cannot allocate new stack frame", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, new_frame_ptr); *new_frame_ptr = frame; memset(&frame, 0, sizeof(frame)); IGRAPH_CHECK(igraph_stack_ptr_push(&stack, new_frame_ptr)); IGRAPH_FINALLY_CLEAN(1); /* ownership of new_frame_ptr taken by the stack */ /* Ownership of the current frame and its vectors (frame.cand, frame.done, frame.cand_filtered) * is taken by the stack from now on. Vectors in frame must be re-initialized with new_cand, * new_fini and stuff. The old frame.cand and frame.fini won't be leaked because they are * managed by the stack now. */ frame.cand = new_cand; frame.fini = new_fini; IGRAPH_CHECK(igraph_vector_init(&new_cand, 0)); IGRAPH_CHECK(igraph_vector_init(&new_fini, 0)); IGRAPH_CHECK(igraph_vector_init(&frame.cand_filtered, 0)); /* Adjust frame.cand_filtered */ if (best_cand_degree < best_fini_cand_degree) { IGRAPH_CHECK(igraph_vector_difference_sorted(&frame.cand, &best_fini_cand_nbrs, &frame.cand_filtered)); } else { IGRAPH_CHECK(igraph_vector_difference_sorted(&frame.cand, &best_cand_nbrs, &frame.cand_filtered)); } } IGRAPH_PROGRESS("Maximal cliques: ", 100.0, NULL); igraph_adjlist_destroy(&adj_list); igraph_vector_destroy(&clique); igraph_vector_destroy(&new_cand); igraph_vector_destroy(&new_fini); igraph_vector_destroy(&cn); igraph_vector_destroy(&best_cand_nbrs); igraph_vector_destroy(&best_fini_cand_nbrs); igraph_i_maximal_cliques_stack_frame_destroy(&frame); igraph_i_maximal_cliques_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; }
int igraph_revolver_d_d(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *vtime, const igraph_vector_t *etime, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres) { igraph_integer_t no_of_events, vnoev, enoev; igraph_vector_t st; long int i; igraph_integer_t maxdegree; igraph_vector_t vtimeidx, etimeidx; igraph_lazy_inclist_t inclist; if (igraph_vector_size(vtime) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vtime length", IGRAPH_EINVAL); } if (igraph_vector_size(etime) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid etime length", IGRAPH_EINVAL); } vnoev=(igraph_integer_t) igraph_vector_max(vtime)+1; enoev=(igraph_integer_t) igraph_vector_max(etime)+1; no_of_events= vnoev > enoev ? vnoev : enoev; IGRAPH_VECTOR_INIT_FINALLY(&st, no_of_events); for (i=0; i<no_of_events; i++) { VECTOR(st)[i]=1; } IGRAPH_CHECK(igraph_maxdegree(graph, &maxdegree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); IGRAPH_VECTOR_INIT_FINALLY(&vtimeidx, 0); IGRAPH_VECTOR_INIT_FINALLY(&etimeidx, 0); IGRAPH_CHECK(igraph_vector_order1(vtime, &vtimeidx, no_of_events)); IGRAPH_CHECK(igraph_vector_order1(etime, &etimeidx, no_of_events)); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_PROGRESS("Revolver d-d", 0, NULL); for (i=0; i<niter; i++) { IGRAPH_ALLOW_INTERRUPTION(); if (i+1 != niter) { /* not the last iteration */ /* measure */ IGRAPH_CHECK(igraph_revolver_mes_d_d(graph, &inclist, kernel, 0 /*sd*/, 0 /*norm*/, 0/*cites*/, 0/*debug*/, 0 /*debugres*/, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, maxdegree)); /* normalize */ igraph_matrix_scale(kernel, 1/igraph_matrix_sum(kernel)); /* update st */ IGRAPH_CHECK(igraph_revolver_st_d_d(graph, &inclist, &st, kernel, vtime, &vtimeidx, etime, &etimeidx, no_of_events)); } else { /* measure */ IGRAPH_CHECK(igraph_revolver_mes_d_d(graph, &inclist, kernel, sd, norm, cites, debug, debugres, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, maxdegree)); /* normalize */ igraph_matrix_scale(kernel, 1/igraph_matrix_sum(kernel)); /* update st */ IGRAPH_CHECK(igraph_revolver_st_d_d(graph, &inclist, &st, kernel, vtime, &vtimeidx, etime, &etimeidx, no_of_events)); /* expected number of citations */ if (expected) { IGRAPH_CHECK(igraph_revolver_exp_d_d(graph, &inclist, expected, kernel, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, maxdegree)); } /* error calculation */ if (logprob || lognull) { IGRAPH_CHECK(igraph_revolver_error_d_d(graph, &inclist, kernel, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, maxdegree, logprob, lognull)); } } IGRAPH_PROGRESS("Revolver d-d", 100.0*(i+1)/niter, NULL); } igraph_lazy_inclist_destroy(&inclist); igraph_vector_destroy(&etimeidx); igraph_vector_destroy(&vtimeidx); igraph_vector_destroy(&st); IGRAPH_FINALLY_CLEAN(4); return 0; }
int igraph_revolver_p_p(const igraph_t *graph, igraph_integer_t niter, const igraph_vector_t *vtime, const igraph_vector_t *etime, const igraph_vector_t *authors, const igraph_vector_t *eventsizes, igraph_matrix_t *kernel, igraph_matrix_t *sd, igraph_matrix_t *norm, igraph_matrix_t *cites, igraph_matrix_t *expected, igraph_real_t *logprob, igraph_real_t *lognull, const igraph_matrix_t *debug, igraph_vector_ptr_t *debugres) { igraph_integer_t no_of_events; igraph_vector_t st; long int i; igraph_integer_t maxpapers=0; igraph_vector_t vtimeidx, etimeidx; igraph_lazy_inclist_t inclist; igraph_vector_long_t papers; if (igraph_vector_size(vtime) != igraph_vcount(graph)) { IGRAPH_ERROR("Invalid vtime length", IGRAPH_EINVAL); } if (igraph_vector_size(etime) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid etime length", IGRAPH_EINVAL); } no_of_events=(igraph_integer_t) igraph_vector_size(eventsizes); IGRAPH_VECTOR_INIT_FINALLY(&st, no_of_events); for (i=0; i<no_of_events; i++) { VECTOR(st)[i]=1; } IGRAPH_CHECK(igraph_vector_long_init(&papers, igraph_vcount(graph))); IGRAPH_FINALLY(igraph_vector_long_destroy, &papers); for (i=0; i<igraph_vector_size(authors); i++) { long int author=(long int) VECTOR(*authors)[i]; VECTOR(papers)[author] += 1; if (VECTOR(papers)[author] > maxpapers) { maxpapers=(igraph_integer_t) VECTOR(papers)[author]; } } igraph_vector_long_destroy(&papers); IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&vtimeidx, 0); IGRAPH_VECTOR_INIT_FINALLY(&etimeidx, 0); IGRAPH_CHECK(igraph_vector_order1(vtime, &vtimeidx, no_of_events)); IGRAPH_CHECK(igraph_vector_order1(etime, &etimeidx, no_of_events)); IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_PROGRESS("Revolver p-p", 0, NULL); for (i=0; i<niter; i++) { IGRAPH_ALLOW_INTERRUPTION(); if (i+1 != niter) { /* not the last iteration */ /* measure */ IGRAPH_CHECK(igraph_revolver_mes_p_p(graph, &inclist, kernel, 0 /*sd*/, 0 /*norm*/, 0/*cites*/, 0/*debug*/, 0 /*debugres*/, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers)); /* normalize */ igraph_matrix_scale(kernel, 1/igraph_matrix_sum(kernel)); /* update st */ IGRAPH_CHECK(igraph_revolver_st_p_p(graph, &inclist, &st, kernel, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers)); } else { /* measure */ IGRAPH_CHECK(igraph_revolver_mes_p_p(graph, &inclist, kernel, sd, norm, cites, debug, debugres, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers)); /* normalize */ igraph_matrix_scale(kernel, 1/igraph_matrix_sum(kernel)); /* update st */ IGRAPH_CHECK(igraph_revolver_st_p_p(graph, &inclist, &st, kernel, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers)); /* expected number of citations */ if (expected) { IGRAPH_CHECK(igraph_revolver_exp_p_p(graph, &inclist, expected, kernel, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers)); } /* error calculation */ if (logprob || lognull) { IGRAPH_CHECK(igraph_revolver_error_p_p(graph, &inclist, kernel, &st, vtime, &vtimeidx, etime, &etimeidx, no_of_events, authors, eventsizes, maxpapers, logprob, lognull)); } } IGRAPH_PROGRESS("Revolver p-p", 100.0*(i+1)/niter, NULL); } igraph_lazy_inclist_destroy(&inclist); igraph_vector_destroy(&etimeidx); igraph_vector_destroy(&vtimeidx); igraph_vector_destroy(&st); 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); }
int igraph_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, igraph_vector_t *csize, igraph_integer_t *no) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t next_nei=IGRAPH_VECTOR_NULL; long int i, n, num_seen; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int no_of_clusters=1; long int act_cluster_size; igraph_vector_t out=IGRAPH_VECTOR_NULL; const igraph_vector_int_t* tmp; igraph_adjlist_t adjlist; /* The result */ IGRAPH_VECTOR_INIT_FINALLY(&next_nei, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&out, 0); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); if (membership) { IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); } IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); igraph_vector_null(&out); if (csize) { igraph_vector_clear(csize); } IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); num_seen = 0; for (i=0; i<no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); tmp = igraph_adjlist_get(&adjlist, i); if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { continue; } IGRAPH_CHECK(igraph_dqueue_push(&q, i)); while (!igraph_dqueue_empty(&q)) { long int act_node=(long int) igraph_dqueue_back(&q); tmp = igraph_adjlist_get(&adjlist, act_node); if (VECTOR(next_nei)[act_node]==0) { /* this is the first time we've met this vertex */ VECTOR(next_nei)[act_node]++; } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { /* we've already met this vertex but it has more children */ long int neighbor=(long int) VECTOR(*tmp)[(long int) VECTOR(next_nei)[act_node]-1]; if (VECTOR(next_nei)[neighbor] == 0) { IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); } VECTOR(next_nei)[act_node]++; } else { /* we've met this vertex and it has no more children */ IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); igraph_dqueue_pop_back(&q); num_seen++; if (num_seen % 10000 == 0) { /* time to report progress and allow the user to interrupt */ IGRAPH_PROGRESS("Strongly connected components: ", num_seen * 50.0 / no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); } } } /* while q */ } /* for */ IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); /* OK, we've the 'out' values for the nodes, let's use them in decreasing order with the help of a heap */ igraph_vector_null(&next_nei); /* mark already added vertices */ num_seen = 0; while (!igraph_vector_empty(&out)) { long int grandfather=(long int) igraph_vector_pop_back(&out); if (VECTOR(next_nei)[grandfather] != 0) { continue; } VECTOR(next_nei)[grandfather]=1; act_cluster_size=1; if (membership) { VECTOR(*membership)[grandfather]=no_of_clusters-1; } IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); num_seen++; if (num_seen % 10000 == 0) { /* time to report progress and allow the user to interrupt */ IGRAPH_PROGRESS("Strongly connected components: ", 50.0 + num_seen * 50.0 / no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); } while (!igraph_dqueue_empty(&q)) { long int act_node=(long int) igraph_dqueue_pop_back(&q); tmp = igraph_adjlist_get(&adjlist, act_node); n = igraph_vector_int_size(tmp); for (i=0; i<n; i++) { long int neighbor=(long int) VECTOR(*tmp)[i]; if (VECTOR(next_nei)[neighbor] != 0) { continue; } IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); VECTOR(next_nei)[neighbor]=1; act_cluster_size++; if (membership) { VECTOR(*membership)[neighbor]=no_of_clusters-1; } num_seen++; if (num_seen % 10000 == 0) { /* time to report progress and allow the user to interrupt */ IGRAPH_PROGRESS("Strongly connected components: ", 50.0 + num_seen * 50.0 / no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); } } } no_of_clusters++; if (csize) { IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); } } IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); if (no) { *no=(igraph_integer_t) no_of_clusters-1; } /* Clean up, return */ igraph_adjlist_destroy(&adjlist); igraph_vector_destroy(&out); igraph_dqueue_destroy(&q); igraph_vector_destroy(&next_nei); IGRAPH_FINALLY_CLEAN(4); return 0; }
/** * \ingroup structural * \function igraph_betweenness_estimate * \brief Estimated betweenness centrality of some vertices. * * </para><para> * The betweenness centrality of a vertex is the number of geodesics * going through it. If there are more than one geodesic between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. When estimating betweenness centrality, igraph * takes into consideration only those paths that are shorter than or * equal to a prescribed length. Note that the estimated centrality * will always be less than the real one. * * \param graph The graph object. * \param res The result of the computation, a vector containing the * estimated betweenness scores for the specified vertices. * \param vids The vertices of which the betweenness centrality scores * will be estimated. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact betweenness will be calculated * (no upper limit on path lengths). * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * \c IGRAPH_EINVVID, invalid vertex id passed in * \p vids. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * Note that the time complexity is independent of the number of * vertices for which the score is calculated. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_edge_betweenness() for calculating the betweenness score * of the edges in a graph. */ int igraph_betweenness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, igraph_integer_t cutoff) { long int no_of_nodes=igraph_vcount(graph); igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int *distance; long int *nrgeo; double *tmpscore; igraph_stack_t stack=IGRAPH_STACK_NULL; long int source; long int j, k; igraph_integer_t modein, modeout; igraph_vit_t vit; igraph_vector_t *neis; igraph_adjlist_t adjlist_out, adjlist_in; igraph_adjlist_t *adjlist_out_p, *adjlist_in_p; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); directed=directed && igraph_is_directed(graph); if (directed) { modeout=IGRAPH_OUT; modein=IGRAPH_IN; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_in, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_in); adjlist_out_p=&adjlist_out; adjlist_in_p=&adjlist_in; } else { modeout=modein=IGRAPH_ALL; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist_out, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist_out); adjlist_out_p=adjlist_in_p=&adjlist_out; } distance=igraph_Calloc(no_of_nodes, long int); if (distance==0) { IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, distance); nrgeo=igraph_Calloc(no_of_nodes, long int); if (nrgeo==0) { IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nrgeo); tmpscore=igraph_Calloc(no_of_nodes, double); if (tmpscore==0) { IGRAPH_ERROR("betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmpscore); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); igraph_stack_init(&stack, no_of_nodes); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); igraph_vector_null(res); /* here we go */ for (source=0; source<no_of_nodes; source++) { IGRAPH_PROGRESS("Betweenness centrality: ", 100.0*source/no_of_nodes, 0); IGRAPH_ALLOW_INTERRUPTION(); memset(distance, 0, no_of_nodes*sizeof(long int)); memset(nrgeo, 0, no_of_nodes*sizeof(long int)); memset(tmpscore, 0, no_of_nodes*sizeof(double)); igraph_stack_clear(&stack); /* it should be empty anyway... */ IGRAPH_CHECK(igraph_dqueue_push(&q, source)); nrgeo[source]=1; distance[source]=0; while (!igraph_dqueue_empty(&q)) { long int actnode=igraph_dqueue_pop(&q); if (cutoff > 0 && distance[actnode] >= cutoff) continue; neis = igraph_adjlist_get(adjlist_out_p, actnode); for (j=0; j<igraph_vector_size(neis); j++) { long int neighbor=VECTOR(*neis)[j]; if (nrgeo[neighbor] != 0) { /* we've already seen this node, another shortest path? */ if (distance[neighbor]==distance[actnode]+1) { nrgeo[neighbor]+=nrgeo[actnode]; } } else { /* we haven't seen this node yet */ nrgeo[neighbor]+=nrgeo[actnode]; distance[neighbor]=distance[actnode]+1; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_stack_push(&stack, neighbor)); } } } /* while !igraph_dqueue_empty */ /* Ok, we've the distance of each node and also the number of shortest paths to them. Now we do an inverse search, starting with the farthest nodes. */ while (!igraph_stack_empty(&stack)) { long int actnode=igraph_stack_pop(&stack); if (distance[actnode]<=1) { continue; } /* skip source node */ /* set the temporary score of the friends */ neis = igraph_adjlist_get(adjlist_in_p, actnode); for (j=0; j<igraph_vector_size(neis); j++) { long int neighbor=VECTOR(*neis)[j]; if (distance[neighbor]==distance[actnode]-1 && nrgeo[neighbor] != 0) { tmpscore[neighbor] += (tmpscore[actnode]+1)*nrgeo[neighbor]/nrgeo[actnode]; } } } /* Ok, we've the scores for this source */ for (k=0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), k++) { long int node=IGRAPH_VIT_GET(vit); VECTOR(*res)[k] += tmpscore[node]; tmpscore[node] = 0.0; /* in case a node is in vids multiple times */ } } /* for source < no_of_nodes */ /* divide by 2 for undirected graph */ if (!directed) { for (j=0; j<igraph_vector_size(res); j++) { VECTOR(*res)[j] /= 2.0; } } /* clean */ igraph_Free(distance); igraph_Free(nrgeo); igraph_Free(tmpscore); igraph_dqueue_destroy(&q); igraph_stack_destroy(&stack); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(6); if (directed) { igraph_adjlist_destroy(&adjlist_out); igraph_adjlist_destroy(&adjlist_in); IGRAPH_FINALLY_CLEAN(2); } else { igraph_adjlist_destroy(&adjlist_out); IGRAPH_FINALLY_CLEAN(1); } return 0; }
/** * \ingroup structural * \function igraph_closeness_estimate * \brief Closeness centrality estimations for some vertices. * * </para><para> * The closeness centrality of a vertex measures how easily other * vertices can be reached from it (or the other way: how easily it * can be reached from the other vertices). It is defined as the * number of the number of vertices minus one divided by the sum of the * lengths of all geodesics from/to the given vertex. When estimating * closeness centrality, igraph considers paths having a length less than * or equal to a prescribed cutoff value. * * </para><para> * If the graph is not connected, and there is no such path between two * vertices, the number of vertices is used instead the length of the * geodesic. This is always longer than the longest possible geodesic. * * </para><para> * Since the estimation considers vertex pairs with a distance greater than * the given value as disconnected, the resulting estimation will always be * lower than the actual closeness centrality. * * \param graph The graph object. * \param res The result of the computation, a vector containing the * closeness centrality scores for the given vertices. * \param vids Vector giving the vertices for which the closeness * centrality scores will be computed. * \param mode The type of shortest paths to be used for the * calculation in directed graphs. Possible values: * \clist * \cli IGRAPH_OUT * the lengths of the outgoing paths are calculated. * \cli IGRAPH_IN * the lengths of the incoming paths are calculated. * \cli IGRAPH_ALL * the directed graph is considered as an * undirected one for the computation. * \endclist * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact closeness will be calculated * (no upper limit on path lengths). * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(n|E|), * n is the number * of vertices for which the calculation is done and * |E| is the number * of edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_betweenness(). */ int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_integer_t cutoff) { long int no_of_nodes=igraph_vcount(graph); igraph_vector_t already_counted, *neis; long int i, j; long int nodes_reached; igraph_adjlist_t allneis; igraph_dqueue_t q; long int nodes_to_calc; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc=IGRAPH_VIT_SIZE(vit); if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { IGRAPH_ERROR("calculating closeness", IGRAPH_EINVMODE); } IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); for (IGRAPH_VIT_RESET(vit), i=0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { IGRAPH_CHECK(igraph_dqueue_push(&q, IGRAPH_VIT_GET(vit))); IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); nodes_reached=1; VECTOR(already_counted)[(long int)IGRAPH_VIT_GET(vit)]=i+1; IGRAPH_PROGRESS("Closeness: ", 100.0*i/no_of_nodes, NULL); IGRAPH_ALLOW_INTERRUPTION(); while (!igraph_dqueue_empty(&q)) { long int act=igraph_dqueue_pop(&q); long int actdist=igraph_dqueue_pop(&q); VECTOR(*res)[i] += actdist; if (cutoff>0 && actdist>=cutoff) continue; neis=igraph_adjlist_get(&allneis, act); for (j=0; j<igraph_vector_size(neis); j++) { long int neighbor=VECTOR(*neis)[j]; if (VECTOR(already_counted)[neighbor] == i+1) { continue; } VECTOR(already_counted)[neighbor] = i+1; nodes_reached++; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_dqueue_push(&q, actdist+1)); } } VECTOR(*res)[i] += ((igraph_integer_t)no_of_nodes * (no_of_nodes-nodes_reached)); VECTOR(*res)[i] = (no_of_nodes-1) / VECTOR(*res)[i]; } IGRAPH_PROGRESS("Closeness: ", 100.0, NULL); /* Clean */ igraph_dqueue_destroy(&q); igraph_vector_destroy(&already_counted); igraph_vit_destroy(&vit); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(4); return 0; }
/** * \ingroup structural * \function igraph_edge_betweenness_estimate * \brief Estimated betweenness centrality of the edges. * * </para><para> * The betweenness centrality of an edge is the number of geodesics * going through it. If there are more than one geodesics between two * vertices, the value of these geodesics are weighted by one over the * number of geodesics. When estimating betweenness centrality, igraph * takes into consideration only those paths that are shorter than or * equal to a prescribed length. Note that the estimated centrality * will always be less than the real one. * \param graph The graph object. * \param result The result of the computation, vector containing the * betweenness scores for the edges. * \param directed Logical, if true directed paths will be considered * for directed graphs. It is ignored for undirected graphs. * \param cutoff The maximal length of paths that will be considered. * If zero or negative, the exact betweenness will be calculated * (no upper limit on path lengths). * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. * * Time complexity: O(|V||E|), * |V| and * |E| are the number of vertices and * edges in the graph. * * \sa Other centrality types: \ref igraph_degree(), \ref igraph_closeness(). * See \ref igraph_betweenness() for calculating the betweenness score * of the vertices in a graph. */ int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, igraph_integer_t cutoff) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; long int *distance; long int *nrgeo; double *tmpscore; igraph_stack_t stack=IGRAPH_STACK_NULL; long int source; long int j; igraph_adjedgelist_t elist_out, elist_in; igraph_adjedgelist_t *elist_out_p, *elist_in_p; igraph_vector_t *neip; long int neino; long int i; igraph_integer_t modein, modeout; directed=directed && igraph_is_directed(graph); if (directed) { modeout=IGRAPH_OUT; modein=IGRAPH_IN; IGRAPH_CHECK(igraph_adjedgelist_init(graph, &elist_out, IGRAPH_OUT)); IGRAPH_FINALLY(igraph_adjedgelist_destroy, &elist_out); IGRAPH_CHECK(igraph_adjedgelist_init(graph, &elist_in, IGRAPH_IN)); IGRAPH_FINALLY(igraph_adjedgelist_destroy, &elist_in); elist_out_p=&elist_out; elist_in_p=&elist_in; } else { modeout=modein=IGRAPH_ALL; IGRAPH_CHECK(igraph_adjedgelist_init(graph,&elist_out, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjedgelist_destroy, &elist_out); elist_out_p=elist_in_p=&elist_out; } distance=igraph_Calloc(no_of_nodes, long int); if (distance==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, distance); nrgeo=igraph_Calloc(no_of_nodes, long int); if (nrgeo==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nrgeo); tmpscore=igraph_Calloc(no_of_nodes, double); if (tmpscore==0) { IGRAPH_ERROR("edge betweenness failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmpscore); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); IGRAPH_FINALLY(igraph_stack_destroy, &stack); IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); igraph_vector_null(result); /* here we go */ for (source=0; source<no_of_nodes; source++) { IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0*source/no_of_nodes, 0); IGRAPH_ALLOW_INTERRUPTION(); memset(distance, 0, no_of_nodes*sizeof(long int)); memset(nrgeo, 0, no_of_nodes*sizeof(long int)); memset(tmpscore, 0, no_of_nodes*sizeof(double)); igraph_stack_clear(&stack); /* it should be empty anyway... */ IGRAPH_CHECK(igraph_dqueue_push(&q, source)); nrgeo[source]=1; distance[source]=0; while (!igraph_dqueue_empty(&q)) { long int actnode=igraph_dqueue_pop(&q); if (cutoff > 0 && distance[actnode] >= cutoff ) continue; neip=igraph_adjedgelist_get(elist_out_p, actnode); neino=igraph_vector_size(neip); for (i=0; i<neino; i++) { igraph_integer_t edge=VECTOR(*neip)[i], from, to; long int neighbor; igraph_edge(graph, edge, &from, &to); neighbor = actnode!=from ? from : to; if (nrgeo[neighbor] != 0) { /* we've already seen this node, another shortest path? */ if (distance[neighbor]==distance[actnode]+1) { nrgeo[neighbor]+=nrgeo[actnode]; } } else { /* we haven't seen this node yet */ nrgeo[neighbor]+=nrgeo[actnode]; distance[neighbor]=distance[actnode]+1; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_stack_push(&stack, neighbor)); } } } /* while !igraph_dqueue_empty */ /* Ok, we've the distance of each node and also the number of shortest paths to them. Now we do an inverse search, starting with the farthest nodes. */ while (!igraph_stack_empty(&stack)) { long int actnode=igraph_stack_pop(&stack); if (distance[actnode]<1) { continue; } /* skip source node */ /* set the temporary score of the friends */ neip=igraph_adjedgelist_get(elist_in_p, actnode); neino=igraph_vector_size(neip); for (i=0; i<neino; i++) { igraph_integer_t from, to; long int neighbor; long int edgeno=VECTOR(*neip)[i]; igraph_edge(graph, edgeno, &from, &to); neighbor= actnode != from ? from : to; if (distance[neighbor]==distance[actnode]-1 && nrgeo[neighbor] != 0) { tmpscore[neighbor] += (tmpscore[actnode]+1)*nrgeo[neighbor]/nrgeo[actnode]; VECTOR(*result)[edgeno] += (tmpscore[actnode]+1)*nrgeo[neighbor]/nrgeo[actnode]; } } } /* Ok, we've the scores for this source */ } /* for source <= no_of_nodes */ IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); /* clean and return */ igraph_Free(distance); igraph_Free(nrgeo); igraph_Free(tmpscore); igraph_dqueue_destroy(&q); igraph_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(5); if (directed) { igraph_adjedgelist_destroy(&elist_out); igraph_adjedgelist_destroy(&elist_in); IGRAPH_FINALLY_CLEAN(2); } else { igraph_adjedgelist_destroy(&elist_out); IGRAPH_FINALLY_CLEAN(1); } /* divide by 2 for undirected graph */ if (!directed || !igraph_is_directed(graph)) { for (j=0; j<igraph_vector_size(result); j++) { VECTOR(*result)[j] /= 2.0; } } return 0; }