int igraph_i_kleinberg2(igraph_real_t *to, const igraph_real_t *from, long int n, void *extra) { igraph_adjlist_t *in = ((igraph_i_kleinberg_data_t*)extra)->in; igraph_adjlist_t *out = ((igraph_i_kleinberg_data_t*)extra)->out; igraph_vector_t *tmp = ((igraph_i_kleinberg_data_t*)extra)->tmp; igraph_vector_t *neis; long int i, j, nlen; for (i=0; i<n; i++) { neis=igraph_adjlist_get(in, i); nlen=igraph_vector_size(neis); VECTOR(*tmp)[i]=0.0; for (j=0; j<nlen; j++) { long int nei=VECTOR(*neis)[j]; VECTOR(*tmp)[i] += from[nei]; } } for (i=0; i<n; i++) { neis=igraph_adjlist_get(out, i); nlen=igraph_vector_size(neis); to[i]=0.0; for (j=0; j<nlen; j++) { long int nei=VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } } return 0; }
int igraph_i_maximal_cliques_select_pivot(const igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, int *pivot, igraph_vector_int_t *nextv, int oldPS, int oldXE) { igraph_vector_int_t *pivotvectneis; int i, pivotvectlen, j, usize=-1; int soldPS=oldPS+1, soldXE=oldXE+1, sPS=PS+1, sPE=PE+1; /* Choose a pivotvect, and bring up P vertices at the same time */ for (i=PS; i<=XE; i++) { int av=VECTOR(*PX)[i]; igraph_vector_int_t *avneis=igraph_adjlist_get(adjlist, av); int *avp=VECTOR(*avneis); int avlen=igraph_vector_int_size(avneis); int *ave=avp+avlen; int *avnei=avp, *pp=avp; for (; avnei < ave; avnei++) { int avneipos=VECTOR(*pos)[(int)(*avnei)]; if (avneipos < soldPS || avneipos > soldXE) { break; } if (avneipos >= sPS && avneipos <= sPE) { if (pp != avnei) { int tmp=*avnei; *avnei = *pp; *pp = tmp; } pp++; } } if ((j=pp-avp) > usize) { *pivot = av; usize=j; } } igraph_vector_int_push_back(nextv, -1); pivotvectneis=igraph_adjlist_get(adjlist, *pivot); pivotvectlen=igraph_vector_int_size(pivotvectneis); for (j=PS; j <= PE; j++) { int vcand=VECTOR(*PX)[j]; igraph_bool_t nei=0; int k=0; for (k=0; k < pivotvectlen; k++) { int unv=VECTOR(*pivotvectneis)[k]; int unvpos=VECTOR(*pos)[unv]; if (unvpos < sPS || unvpos > sPE) { break; } if (unv == vcand) { nei=1; break; } } if (!nei) { igraph_vector_int_push_back(nextv, vcand); } } return 0; }
int igraph_i_pagerank(igraph_real_t *to, const igraph_real_t *from, long int n, void *extra) { igraph_i_pagerank_data_t *data=extra; igraph_adjlist_t *adjlist=data->adjlist; igraph_vector_t *outdegree=data->outdegree; igraph_vector_t *tmp=data->tmp; igraph_vector_t *neis; long int i, j, nlen; igraph_real_t sumfrom=0.0; for (i=0; i<n; i++) { sumfrom += from[i]; VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i]; } for (i=0; i<n; i++) { neis=igraph_adjlist_get(adjlist, i); nlen=igraph_vector_size(neis); to[i]=0.0; for (j=0; j<nlen; j++) { long int nei=VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } to[i] *= data->damping; to[i] += (1-data->damping)/n * sumfrom; } return 0; }
int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, int mynextv, igraph_vector_int_t *R, int *newPS, int *newXE) { igraph_vector_int_t *vneis=igraph_adjlist_get(adjlist, mynextv); int j, vneislen=igraph_vector_int_size(vneis); int sPS=PS+1, sPE=PE+1, sXS=XS+1, sXE=XE+1; *newPS=PE+1; *newXE=XS-1; for (j=0; j<vneislen; j++) { int vnei=VECTOR(*vneis)[j]; int vneipos=VECTOR(*pos)[vnei]; if (vneipos >= sPS && vneipos <= sPE) { (*newPS)--; SWAP(vneipos-1, *newPS); } else if (vneipos >= sXS && vneipos <= sXE) { (*newXE)++; SWAP(vneipos-1, *newXE); } } igraph_vector_int_push_back(R, mynextv); return 0; }
int igraph_i_maximal_cliques_reorder_adjlists( const igraph_vector_int_t *PX, int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, igraph_adjlist_t *adjlist) { int j; int sPS=PS+1, sPE=PE+1; for (j=PS; j<=XE; j++) { int av=VECTOR(*PX)[j]; igraph_vector_int_t *avneis=igraph_adjlist_get(adjlist, av); int *avp=VECTOR(*avneis); int avlen=igraph_vector_int_size(avneis); int *ave=avp+avlen; int *avnei=avp, *pp=avp; for (; avnei < ave; avnei++) { int avneipos=VECTOR(*pos)[(int)(*avnei)]; if (avneipos >= sPS && avneipos <= sPE) { if (pp != avnei) { int tmp=*avnei; *avnei = *pp; *pp = tmp; } pp++; } } } return 0; }
int igraph_i_separators_store(igraph_vector_ptr_t *separators, const igraph_adjlist_t *adjlist, igraph_vector_t *components, igraph_vector_t *leaveout, unsigned long int *mark, igraph_vector_t *sorter) { /* We need to stote N(C), the neighborhood of C, but only if it is * not already stored among the separators. */ long int cptr=0, next, complen=igraph_vector_size(components); while (cptr < complen) { long int saved=cptr; igraph_vector_clear(sorter); /* Calculate N(C) for the next C */ while ( (next=(long int) VECTOR(*components)[cptr++]) != -1) { VECTOR(*leaveout)[next] = *mark; } cptr=saved; while ( (next=(long int) VECTOR(*components)[cptr++]) != -1) { igraph_vector_int_t *neis=igraph_adjlist_get(adjlist, next); long int j, nn=igraph_vector_int_size(neis); for (j=0; j<nn; j++) { long int nei=(long int) VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei] != *mark) { igraph_vector_push_back(sorter, nei); VECTOR(*leaveout)[nei] = *mark; } } } igraph_vector_sort(sorter); UPDATEMARK(); /* Add it to the list of separators, if it is new */ if (igraph_i_separators_newsep(separators, sorter)) { igraph_vector_t *newc=igraph_Calloc(1, igraph_vector_t); if (!newc) { IGRAPH_ERROR("Cannot calculate minimal separators", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newc); igraph_vector_copy(newc, sorter); IGRAPH_FINALLY(igraph_vector_destroy, newc); IGRAPH_CHECK(igraph_vector_ptr_push_back(separators, newc)); IGRAPH_FINALLY_CLEAN(2); } } /* while cptr < complen */ return 0; }
int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t *clique_number, igraph_bool_t keep_only_largest, igraph_bool_t complementer) { igraph_i_max_ind_vsets_data_t clqdata; long int no_of_nodes = igraph_vcount(graph), i; if (igraph_is_directed(graph)) IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); clqdata.matrix_size=no_of_nodes; clqdata.keep_only_largest=keep_only_largest; if (complementer) IGRAPH_CHECK(igraph_adjlist_init_complementer(graph, &clqdata.adj_list, IGRAPH_ALL, 0)); else IGRAPH_CHECK(igraph_adjlist_init(graph, &clqdata.adj_list, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &clqdata.adj_list); clqdata.IS = igraph_Calloc(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) IGRAPH_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_free, clqdata.IS); IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); for (i=0; i<no_of_nodes; i++) VECTOR(clqdata.deg)[i] = igraph_vector_size(igraph_adjlist_get(&clqdata.adj_list, i)); clqdata.buckets = igraph_Calloc(no_of_nodes+1, igraph_set_t); if (clqdata.buckets == 0) IGRAPH_ERROR("igraph_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); for (i=0; i<no_of_nodes; i++) IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); if (res) igraph_vector_ptr_clear(res); /* Do the show */ clqdata.largest_set_size=0; IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, &clqdata, 0)); /* Cleanup */ for (i=0; i<no_of_nodes; i++) igraph_set_destroy(&clqdata.buckets[i]); igraph_adjlist_destroy(&clqdata.adj_list); igraph_vector_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); if (clique_number) *clique_number = clqdata.largest_set_size; return 0; }
int igraph_i_clusters_leaveout(const igraph_adjlist_t *adjlist, igraph_vector_t *components, igraph_vector_t *leaveout, unsigned long int *mark, igraph_dqueue_t *Q) { /* Another trick: we use the same 'leaveout' vector to mark the * vertices that were already found in the BFS */ long int i, no_of_nodes=igraph_adjlist_size(adjlist); igraph_dqueue_clear(Q); igraph_vector_clear(components); for (i=0; i<no_of_nodes; i++) { if (VECTOR(*leaveout)[i] == *mark) continue; VECTOR(*leaveout)[i]= *mark; igraph_dqueue_push(Q, i); igraph_vector_push_back(components, i); while (!igraph_dqueue_empty(Q)) { long int act_node=(long int) igraph_dqueue_pop(Q); igraph_vector_int_t *neis=igraph_adjlist_get(adjlist, act_node); long int j, n=igraph_vector_int_size(neis); for (j=0; j<n; j++) { long int nei=(long int) VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei]== *mark) continue; IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); VECTOR(*leaveout)[nei]= *mark; igraph_vector_push_back(components, nei); } } igraph_vector_push_back(components, -1); } UPDATEMARK(); return 0; }
int igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_adjlist_t *adjlist = (igraph_adjlist_t *) extra; igraph_vector_int_t *neis; int i, j, nlen; for (i=0; i<n; i++) { neis=igraph_adjlist_get(adjlist, i); nlen=igraph_vector_int_size(neis); to[i] = 0.0; for (j=0; j<nlen; j++) { int nei = VECTOR(*neis)[j]; to[i] += from[nei]; } } return 0; }
int igraph_i_eigenvector_centrality(igraph_real_t *to, const igraph_real_t *from, long int n, void *extra) { igraph_adjlist_t *adjlist=extra; igraph_vector_t *neis; long int i, j, nlen; for (i=0; i<n; i++) { neis=igraph_adjlist_get(adjlist, i); nlen=igraph_vector_size(neis); to[i]=0.0; for (j=0; j<nlen; j++) { long int nei=VECTOR(*neis)[j]; to[i] += from[nei]; } } 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; }
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; }
int igraph_bipartite_projection_size(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_integer_t *vcount1, igraph_integer_t *ecount1, igraph_integer_t *vcount2, igraph_integer_t *ecount2) { long int no_of_nodes=igraph_vcount(graph); long int vc1=0, ec1=0, vc2=0, ec2=0; igraph_adjlist_t adjlist; igraph_vector_long_t added; long int i; IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &added); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); for (i=0; i<no_of_nodes; i++) { igraph_vector_int_t *neis1; long int neilen1, j; long int *ecptr; if (VECTOR(*types)[i]) { vc2++; ecptr=&ec2; } else { vc1++; ecptr=&ec1; } neis1=igraph_adjlist_get(&adjlist, i); neilen1=igraph_vector_int_size(neis1); for (j=0; j<neilen1; j++) { long int k, neilen2, nei=(long int) VECTOR(*neis1)[j]; igraph_vector_int_t *neis2=igraph_adjlist_get(&adjlist, nei); if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { IGRAPH_ERROR("Non-bipartite edge found in bipartite projection", IGRAPH_EINVAL); } neilen2=igraph_vector_int_size(neis2); for (k=0; k<neilen2; k++) { long int nei2=(long int) VECTOR(*neis2)[k]; if (nei2 <= i) { continue; } if (VECTOR(added)[nei2] == i+1) { continue; } VECTOR(added)[nei2] = i+1; (*ecptr)++; } } } *vcount1=(igraph_integer_t) vc1; *ecount1=(igraph_integer_t) ec1; *vcount2=(igraph_integer_t) vc2; *ecount2=(igraph_integer_t) ec2; igraph_adjlist_destroy(&adjlist); igraph_vector_long_destroy(&added); IGRAPH_FINALLY_CLEAN(2); return 0; }
int igraph_i_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj, int which, igraph_vector_t *multiplicity) { long int no_of_nodes=igraph_vcount(graph); long int i, j, k; igraph_integer_t remaining_nodes=0; igraph_vector_t vertex_perm, vertex_index; igraph_vector_t edges; igraph_adjlist_t adjlist; igraph_vector_int_t *neis1, *neis2; long int neilen1, neilen2; igraph_vector_long_t added; igraph_vector_t mult; if (which < 0) { return 0; } IGRAPH_VECTOR_INIT_FINALLY(&vertex_perm, 0); IGRAPH_CHECK(igraph_vector_reserve(&vertex_perm, no_of_nodes)); IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&vertex_index, no_of_nodes); IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &added); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); if (multiplicity) { IGRAPH_VECTOR_INIT_FINALLY(&mult, no_of_nodes); igraph_vector_clear(multiplicity); } for (i=0; i<no_of_nodes; i++) { if (VECTOR(*types)[i] == which) { VECTOR(vertex_index)[i] = ++remaining_nodes; igraph_vector_push_back(&vertex_perm, i); } } for (i=0; i<no_of_nodes; i++) { if (VECTOR(*types)[i] == which) { long int new_i=(long int) VECTOR(vertex_index)[i]-1; long int iedges=0; neis1=igraph_adjlist_get(&adjlist, i); neilen1=igraph_vector_int_size(neis1); for (j=0; j<neilen1; j++) { long int nei=(long int) VECTOR(*neis1)[j]; if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { IGRAPH_ERROR("Non-bipartite edge found in bipartite projection", IGRAPH_EINVAL); } neis2=igraph_adjlist_get(&adjlist, nei); neilen2=igraph_vector_int_size(neis2); for (k=0; k<neilen2; k++) { long int nei2=(long int) VECTOR(*neis2)[k], new_nei2; if (nei2 <= i) { continue; } if (VECTOR(added)[nei2] == i+1) { if (multiplicity) { VECTOR(mult)[nei2]+=1; } continue; } VECTOR(added)[nei2] = i+1; if (multiplicity) { VECTOR(mult)[nei2]=1; } iedges++; IGRAPH_CHECK(igraph_vector_push_back(&edges, new_i)); if (multiplicity) { /* If we need the multiplicity as well, then we put in the old vertex ids here and rewrite it later */ IGRAPH_CHECK(igraph_vector_push_back(&edges, nei2)); } else { new_nei2=(long int) VECTOR(vertex_index)[nei2]-1; IGRAPH_CHECK(igraph_vector_push_back(&edges, new_nei2)); } } } if (multiplicity) { /* OK, we need to go through all the edges added for vertex new_i and check their multiplicity */ long int now=igraph_vector_size(&edges); long int from=now-iedges*2; for (j=from; j<now; j+=2) { long int nei2=(long int) VECTOR(edges)[j+1]; long int new_nei2=(long int) VECTOR(vertex_index)[nei2]-1; long int m=(long int) VECTOR(mult)[nei2]; VECTOR(edges)[j+1]=new_nei2; IGRAPH_CHECK(igraph_vector_push_back(multiplicity, m)); } } } /* if VECTOR(*type)[i] == which */ } if (multiplicity) { igraph_vector_destroy(&mult); IGRAPH_FINALLY_CLEAN(1); } igraph_adjlist_destroy(&adjlist); igraph_vector_long_destroy(&added); igraph_vector_destroy(&vertex_index); IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(proj, &edges, remaining_nodes, /*directed=*/ 0)); igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, proj); IGRAPH_I_ATTRIBUTE_DESTROY(proj); IGRAPH_I_ATTRIBUTE_COPY(proj, graph, 1, 0, 0); IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, proj, &vertex_perm)); igraph_vector_destroy(&vertex_perm); IGRAPH_FINALLY_CLEAN(2); 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; }
int igraph_i_maximal_independent_vertex_sets_backtrack(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_i_max_ind_vsets_data_t *clqdata, igraph_integer_t level) { long int v1, v2, v3, c, j, k; igraph_vector_t *neis1, *neis2; igraph_bool_t f; igraph_integer_t j1; long int it_state; IGRAPH_ALLOW_INTERRUPTION(); if (level >= clqdata->matrix_size-1) { igraph_integer_t size=0; if (res) { igraph_vector_t *vec; vec = igraph_Calloc(1, igraph_vector_t); if (vec == 0) IGRAPH_ERROR("igraph_i_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); IGRAPH_VECTOR_INIT_FINALLY(vec, 0); for (v1=0; v1<clqdata->matrix_size; v1++) if (clqdata->IS[v1] == 0) { IGRAPH_CHECK(igraph_vector_push_back(vec, v1)); } size=igraph_vector_size(vec); if (!clqdata->keep_only_largest) IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); else { if (size > clqdata->largest_set_size) { /* We are keeping only the largest sets, and we've found one that's * larger than all previous sets, so we have to clear the list */ j=igraph_vector_ptr_size(res); for (v1=0; v1<j; v1++) { igraph_vector_destroy(VECTOR(*res)[v1]); free(VECTOR(*res)[v1]); } igraph_vector_ptr_clear(res); IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); } else if (size == clqdata->largest_set_size) { IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); } else { igraph_vector_destroy(vec); free(vec); } } IGRAPH_FINALLY_CLEAN(1); } else { for (v1=0, size=0; v1<clqdata->matrix_size; v1++) if (clqdata->IS[v1] == 0) size++; } if (size>clqdata->largest_set_size) clqdata->largest_set_size=size; } else { v1 = level+1; /* Count the number of vertices with an index less than v1 that have * an IS value of zero */ neis1 = igraph_adjlist_get(&clqdata->adj_list, v1); c = 0; j = 0; while (j<VECTOR(clqdata->deg)[v1] && (v2=VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) c++; j++; } if (c == 0) { /* If there are no such nodes... */ j = 0; while (j<VECTOR(clqdata->deg)[v1] && (v2=VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]++; j++; } IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata,v1)); j = 0; while (j<VECTOR(clqdata->deg)[v1] && (v2=VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]--; j++; } } else { /* If there are such nodes, store the count in the IS value of v1 */ clqdata->IS[v1] = c; IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata,v1)); clqdata->IS[v1] = 0; f=1; j=0; while (j<VECTOR(clqdata->deg)[v1] && (v2=VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], j)); neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); k = 0; while (k<VECTOR(clqdata->deg)[v2] && (v3=VECTOR(*neis2)[k])<=level) { clqdata->IS[v3]--; if (clqdata->IS[v3] == 0) f=0; k++; } } clqdata->IS[v2]++; j++; } if (f) IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph,res,clqdata,v1)); j=0; while (j<VECTOR(clqdata->deg)[v1] && (v2=VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]--; j++; } it_state=0; while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j1)) { j=(long)j1; v2=VECTOR(*neis1)[j]; neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); k = 0; while (k<VECTOR(clqdata->deg)[v2] && (v3=VECTOR(*neis2)[k])<=level) { clqdata->IS[v3]++; k++; } } igraph_set_clear(&clqdata->buckets[v1]); } } return 0; }
int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { long i, vertex, maxdeg; long vc = igraph_vcount(graph); igraph_2wheap_t cn; /* indexed heap storing number of already coloured neighbours */ igraph_vector_int_t neigh_colors; igraph_adjlist_t adjlist; IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); igraph_vector_int_fill(colors, 0); /* Nothing to do for 0 or 1 vertices. * Remember that colours are integers starting from 0, * and the 'colors' vector is already 0-initialized above. */ if (vc <= 1) return IGRAPH_SUCCESS; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); /* find maximum degree and a corresponding vertex */ { igraph_vector_t degree; IGRAPH_CHECK(igraph_vector_init(°ree, 0)); IGRAPH_FINALLY(igraph_vector_destroy, °ree); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, 0)); vertex = igraph_vector_which_max(°ree); maxdeg = VECTOR(degree)[vertex]; igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } IGRAPH_CHECK(igraph_vector_int_init(&neigh_colors, 0)); IGRAPH_CHECK(igraph_vector_int_reserve(&neigh_colors, maxdeg)); IGRAPH_FINALLY(igraph_vector_int_destroy, &neigh_colors); IGRAPH_CHECK(igraph_2wheap_init(&cn, vc)); IGRAPH_FINALLY(igraph_2wheap_destroy, &cn); for (i=0; i < vc; ++i) if (i != vertex) igraph_2wheap_push_with_index(&cn, i, 0); /* should not fail since memory was already reserved */ while (1) { igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, vertex); long neigh_count = igraph_vector_int_size(neighbors); /* colour current vertex */ { igraph_integer_t col; IGRAPH_CHECK(igraph_vector_int_resize(&neigh_colors, neigh_count)); for (i=0; i < neigh_count; ++i) VECTOR(neigh_colors)[i] = VECTOR(*colors)[ VECTOR(*neighbors)[i] ]; igraph_vector_int_sort(&neigh_colors); i=0; col = 0; do { while (i < neigh_count && VECTOR(neigh_colors)[i] == col) i++; col++; } while (i < neigh_count && VECTOR(neigh_colors)[i] == col); VECTOR(*colors)[vertex] = col; } /* increment number of coloured neighbours for each neighbour of vertex */ for (i=0; i < neigh_count; ++i) { long idx = VECTOR(*neighbors)[i]; if (igraph_2wheap_has_elem(&cn, idx)) igraph_2wheap_modify(&cn, idx, igraph_2wheap_get(&cn, idx) + 1); } /* stop if no more vertices left to colour */ if (igraph_2wheap_empty(&cn)) break; igraph_2wheap_delete_max_index(&cn, &vertex); IGRAPH_ALLOW_INTERRUPTION(); } /* subtract 1 from each colour value, so that colours start at 0 */ igraph_vector_int_add_constant(colors, -1); /* free data structures */ igraph_vector_int_destroy(&neigh_colors); igraph_adjlist_destroy(&adjlist); igraph_2wheap_destroy(&cn); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; }
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_all_minimal_st_separators(const igraph_t *graph, igraph_vector_ptr_t *separators) { /* * Some notes about the tricks used here. For finding the components * of the graph after removing some vertices, we do the * following. First we mark the vertices with the actual mark stamp * (mark), then run breadth-first search on the graph, but not * considering the marked vertices. Then we increase the mark. If * there is integer overflow here, then we zero out the mark and set * it to one. (We might as well just always zero it out.) * * For each separator the vertices are stored in vertex id order. * This facilitates the comparison of the separators when we find a * potential new candidate. * * To keep track of which separator we already used as a basis, we * keep a boolean vector (already_tried). The try_next pointer show * the next separator to try as a basis. */ long int no_of_nodes=igraph_vcount(graph); igraph_vector_t leaveout; igraph_vector_bool_t already_tried; long int try_next=0; unsigned long int mark=1; long int v; igraph_adjlist_t adjlist; igraph_vector_t components; igraph_dqueue_t Q; igraph_vector_t sorter; igraph_vector_ptr_clear(separators); IGRAPH_FINALLY(igraph_i_separators_free, separators); IGRAPH_CHECK(igraph_vector_init(&leaveout, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_destroy, &leaveout); IGRAPH_CHECK(igraph_vector_bool_init(&already_tried, 0)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &already_tried); IGRAPH_CHECK(igraph_vector_init(&components, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &components); IGRAPH_CHECK(igraph_vector_reserve(&components, no_of_nodes*2)); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); IGRAPH_CHECK(igraph_vector_init(&sorter, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &sorter); IGRAPH_CHECK(igraph_vector_reserve(&sorter, no_of_nodes)); /* --------------------------------------------------------------- * INITIALIZATION, we check whether the neighborhoods of the * vertices separate the graph. The ones that do will form the * initial basis. */ for (v=0; v<no_of_nodes; v++) { /* Mark v and its neighbors */ igraph_vector_int_t *neis=igraph_adjlist_get(&adjlist, v); long int i, n=igraph_vector_int_size(neis); VECTOR(leaveout)[v]=mark; for (i=0; i<n; i++) { long int nei=(long int) VECTOR(*neis)[i]; VECTOR(leaveout)[nei]=mark; } /* Find the components */ IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, &leaveout, &mark, &Q)); /* Store the corresponding separators, N(C) for each component C */ IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, &components, &leaveout, &mark, &sorter)); } /* --------------------------------------------------------------- * GENERATION, we need to use all already found separators as * basis and see if they generate more separators */ while (try_next < igraph_vector_ptr_size(separators)) { igraph_vector_t *basis=VECTOR(*separators)[try_next]; long int b, basislen=igraph_vector_size(basis); for (b=0; b<basislen; b++) { /* Remove N(x) U basis */ long int x=(long int) VECTOR(*basis)[b]; igraph_vector_int_t *neis=igraph_adjlist_get(&adjlist, x); long int i, n=igraph_vector_int_size(neis); for (i=0; i<basislen; i++) { long int sn=(long int) VECTOR(*basis)[i]; VECTOR(leaveout)[sn]=mark; } for (i=0; i<n; i++) { long int nei=(long int) VECTOR(*neis)[i]; VECTOR(leaveout)[nei]=mark; } /* Find the components */ IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, &leaveout, &mark, &Q)); /* Store the corresponding separators, N(C) for each component C */ IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, &components, &leaveout, &mark, &sorter)); } try_next++; } /* --------------------------------------------------------------- */ igraph_vector_destroy(&sorter); igraph_dqueue_destroy(&Q); igraph_adjlist_destroy(&adjlist); igraph_vector_destroy(&components); igraph_vector_bool_destroy(&already_tried); igraph_vector_destroy(&leaveout); IGRAPH_FINALLY_CLEAN(7); /* +1 for separators */ return 0; }
int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, igraph_vector_t *res, const igraph_vector_t *weights_them, igraph_neimode_t mode) { int no_of_nodes=igraph_vcount(us); igraph_adjlist_t adj_us; igraph_inclist_t incs_them; igraph_vector_int_t neis; int node; if (igraph_vcount(them) != no_of_nodes) { IGRAPH_ERROR("Number of vertices must match in scan-1", IGRAPH_EINVAL); } if (igraph_is_directed(us) != igraph_is_directed(them)) { IGRAPH_ERROR("Directedness must match in scan-1", IGRAPH_EINVAL); } if (weights_them && igraph_vector_size(weights_them) != igraph_ecount(them)) { IGRAPH_ERROR("Invalid weight vector length in scan-1 (them)", IGRAPH_EINVAL); } igraph_adjlist_init(us, &adj_us, mode); IGRAPH_FINALLY(igraph_adjlist_destroy, &adj_us); igraph_adjlist_simplify(&adj_us); igraph_inclist_init(them, &incs_them, mode); IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them); igraph_vector_int_init(&neis, no_of_nodes); IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); igraph_vector_resize(res, no_of_nodes); igraph_vector_null(res); for (node=0; node < no_of_nodes; node++) { igraph_vector_int_t *neis_us=igraph_adjlist_get(&adj_us, node); igraph_vector_int_t *edges1_them=igraph_inclist_get(&incs_them, node); int len1_us=igraph_vector_int_size(neis_us); int len1_them=igraph_vector_int_size(edges1_them); int i; IGRAPH_ALLOW_INTERRUPTION(); /* Mark neighbors and self in us */ VECTOR(neis)[node] = node+1; for (i = 0; i < len1_us; i++) { int nei=VECTOR(*neis_us)[i]; VECTOR(neis)[nei] = node+1; } /* Crawl neighbors in them, first ego */ for (i = 0; i < len1_them; i++) { int e=VECTOR(*edges1_them)[i]; int nei=IGRAPH_OTHER(them, e, node); if (VECTOR(neis)[nei] == node+1) { igraph_real_t w=weights_them ? VECTOR(*weights_them)[e] : 1; VECTOR(*res)[node] += w; } } /* Then the rest */ for (i = 0; i < len1_us; i++) { int nei=VECTOR(*neis_us)[i]; igraph_vector_int_t *edges2_them=igraph_inclist_get(&incs_them, nei); int j, len2_them=igraph_vector_int_size(edges2_them); for (j = 0; j < len2_them; j++) { int e2=VECTOR(*edges2_them)[j]; int nei2=IGRAPH_OTHER(them, e2, nei); if (VECTOR(neis)[nei2] == node+1) { igraph_real_t w=weights_them ? VECTOR(*weights_them)[e2] : 1; VECTOR(*res)[node] += w; } } } /* For undirected, it was double counted */ if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) { VECTOR(*res)[node] /= 2.0; } } /* node < no_of_nodes */ igraph_vector_int_destroy(&neis); igraph_inclist_destroy(&incs_them); igraph_adjlist_destroy(&adj_us); IGRAPH_FINALLY_CLEAN(3); return 0; }
/** * Finding maximum bipartite matchings on bipartite graphs using the * Hungarian algorithm (a.k.a. Kuhn-Munkres algorithm). * * The algorithm uses a maximum cardinality matching on a subset of * tight edges as a starting point. This is achieved by * \c igraph_i_maximum_bipartite_matching_unweighted on the restricted * graph. * * The algorithm works reliably only if the weights are integers. The * \c eps parameter should specity a very small number; if the slack on * an edge falls below \c eps, it will be considered tight. If all your * weights are integers, you can safely set \c eps to zero. */ int igraph_i_maximum_bipartite_matching_weighted(const igraph_t* graph, const igraph_vector_bool_t* types, igraph_integer_t* matching_size, igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps) { long int i, j, k, n, no_of_nodes, no_of_edges; igraph_integer_t u, v, w, msize; igraph_t newgraph; igraph_vector_long_t match; /* will store the matching */ igraph_vector_t slack; /* will store the slack on each edge */ igraph_vector_t parent; /* parent vertices during a BFS */ igraph_vector_t vec1, vec2; /* general temporary vectors */ igraph_vector_t labels; /* will store the labels */ igraph_dqueue_long_t q; /* a FIFO for BST */ igraph_bool_t smaller_set; /* denotes which part of the bipartite graph is smaller */ long int smaller_set_size; /* size of the smaller set */ igraph_real_t dual; /* solution of the dual problem */ igraph_adjlist_t tight_phantom_edges; /* adjacency list to manage tight phantom edges */ igraph_integer_t alternating_path_endpoint; igraph_vector_t* neis; igraph_vector_int_t *neis2; igraph_inclist_t inclist; /* incidence list of the original graph */ /* The Hungarian algorithm is originally for complete bipartite graphs. * For non-complete bipartite graphs, a phantom edge of weight zero must be * added between every pair of non-connected vertices. We don't do this * explicitly of course. See the comments below about how phantom edges * are taken into account. */ no_of_nodes = igraph_vcount(graph); no_of_edges = igraph_ecount(graph); if (eps < 0) { IGRAPH_WARNING("negative epsilon given, clamping to zero"); eps = 0; } /* (1) Initialize data structures */ IGRAPH_CHECK(igraph_vector_long_init(&match, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_long_destroy, &match); IGRAPH_CHECK(igraph_vector_init(&slack, no_of_edges)); IGRAPH_FINALLY(igraph_vector_destroy, &slack); IGRAPH_VECTOR_INIT_FINALLY(&vec1, 0); IGRAPH_VECTOR_INIT_FINALLY(&vec2, 0); IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); IGRAPH_CHECK(igraph_dqueue_long_init(&q, 0)); IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); IGRAPH_VECTOR_INIT_FINALLY(&parent, no_of_nodes); IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &tight_phantom_edges); IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); /* (2) Find which set is the smaller one */ j = 0; for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] == 0) j++; } smaller_set = (j > no_of_nodes / 2); smaller_set_size = smaller_set ? (no_of_nodes - j) : j; /* (3) Calculate the initial labeling and the set of tight edges. Use the * smaller set only. Here we can assume that there are no phantom edges * among the tight ones. */ dual = 0; for (i = 0; i < no_of_nodes; i++) { igraph_real_t max_weight = 0; if (VECTOR(*types)[i] != smaller_set) { VECTOR(labels)[i] = 0; continue; } neis = igraph_inclist_get(&inclist, i); n = igraph_vector_size(neis); for (j = 0, k = 0; j < n; j++) { if (VECTOR(*weights)[(long int)VECTOR(*neis)[j]] > max_weight) { k = (long int) VECTOR(*neis)[j]; max_weight = VECTOR(*weights)[k]; } } VECTOR(labels)[i] = max_weight; dual += max_weight; } igraph_vector_clear(&vec1); IGRAPH_CHECK(igraph_get_edgelist(graph, &vec2, 0)); #define IS_TIGHT(i) (VECTOR(slack)[i] <= eps) for (i = 0, j = 0; i < no_of_edges; i++, j+=2) { u = (igraph_integer_t) VECTOR(vec2)[j]; v = (igraph_integer_t) VECTOR(vec2)[j+1]; VECTOR(slack)[i] = VECTOR(labels)[u] + VECTOR(labels)[v] - VECTOR(*weights)[i]; if (IS_TIGHT(i)) { IGRAPH_CHECK(igraph_vector_push_back(&vec1, u)); IGRAPH_CHECK(igraph_vector_push_back(&vec1, v)); } } igraph_vector_clear(&vec2); /* (4) Construct a temporary graph on which the initial maximum matching * will be calculated (only on the subset of tight edges) */ IGRAPH_CHECK(igraph_create(&newgraph, &vec1, (igraph_integer_t) no_of_nodes, 0)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_maximum_bipartite_matching(&newgraph, types, &msize, 0, &match, 0, 0)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); /* (5) Main loop until the matching becomes maximal */ while (msize < smaller_set_size) { igraph_real_t min_slack, min_slack_2; igraph_integer_t min_slack_u, min_slack_v; /* (7) Fill the push queue with the unmatched nodes from the smaller set. */ igraph_vector_clear(&vec1); igraph_vector_clear(&vec2); igraph_vector_fill(&parent, -1); for (i = 0; i < no_of_nodes; i++) { if (UNMATCHED(i) && VECTOR(*types)[i] == smaller_set) { IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); VECTOR(parent)[i] = i; IGRAPH_CHECK(igraph_vector_push_back(&vec1, i)); } } #ifdef MATCHING_DEBUG debug("Matching:"); igraph_vector_long_print(&match); debug("Unmatched vertices are marked by non-negative numbers:\n"); igraph_vector_print(&parent); debug("Labeling:"); igraph_vector_print(&labels); debug("Slacks:"); igraph_vector_print(&slack); #endif /* (8) Run the BFS */ alternating_path_endpoint = -1; while (!igraph_dqueue_long_empty(&q)) { v = (int) igraph_dqueue_long_pop(&q); debug("Considering vertex %ld\n", (long int)v); /* v is always in the smaller set. Find the neighbors of v, which * are all in the larger set. Find the pairs of these nodes in * the smaller set and push them to the queue. Mark the traversed * nodes as seen. * * Here we have to be careful as there are two types of incident * edges on v: real edges and phantom ones. Real edges are * given by igraph_inclist_get. Phantom edges are not given so we * (ab)use an adjacency list data structure that lists the * vertices connected to v by phantom edges only. */ neis = igraph_inclist_get(&inclist, v); n = igraph_vector_size(neis); for (i = 0; i < n; i++) { j = (long int) VECTOR(*neis)[i]; /* We only care about tight edges */ if (!IS_TIGHT(j)) continue; /* Have we seen the other endpoint already? */ u = IGRAPH_OTHER(graph, j, v); if (VECTOR(parent)[u] >= 0) continue; debug(" Reached vertex %ld via edge %ld\n", (long)u, (long)j); VECTOR(parent)[u] = v; IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); w = (int) VECTOR(match)[u]; if (w == -1) { /* u is unmatched and it is in the larger set. Therefore, we * could improve the matching by following the parents back * from u to the root. */ alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); } /* Now do the same with the phantom edges */ neis2 = igraph_adjlist_get(&tight_phantom_edges, v); n = igraph_vector_int_size(neis2); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(*neis2)[i]; /* Have we seen u already? */ if (VECTOR(parent)[u] >= 0) continue; /* Check if the edge is really tight; it might have happened that the * edge became non-tight in the meanwhile. We do not remove these from * tight_phantom_edges at the moment, so we check them once again here. */ if (fabs(VECTOR(labels)[(long int)v] + VECTOR(labels)[(long int)u]) > eps) continue; debug(" Reached vertex %ld via tight phantom edge\n", (long)u); VECTOR(parent)[u] = v; IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); w = (int) VECTOR(match)[u]; if (w == -1) { /* u is unmatched and it is in the larger set. Therefore, we * could improve the matching by following the parents back * from u to the root. */ alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); } } /* Okay; did we have an alternating path? */ if (alternating_path_endpoint != -1) { #ifdef MATCHING_DEBUG debug("BFS parent tree:"); igraph_vector_print(&parent); #endif /* Increase the size of the matching with the alternating path. */ v = alternating_path_endpoint; u = (igraph_integer_t) VECTOR(parent)[v]; debug("Extending matching with alternating path ending in %ld.\n", (long int)v); while (u != v) { w = (int) VECTOR(match)[v]; if (w != -1) VECTOR(match)[w] = -1; VECTOR(match)[v] = u; VECTOR(match)[v] = u; w = (int) VECTOR(match)[u]; if (w != -1) VECTOR(match)[w] = -1; VECTOR(match)[u] = v; v = (igraph_integer_t) VECTOR(parent)[u]; u = (igraph_integer_t) VECTOR(parent)[v]; } msize++; #ifdef MATCHING_DEBUG debug("New matching after update:"); igraph_vector_long_print(&match); debug("Matching size is now: %ld\n", (long)msize); #endif continue; } #ifdef MATCHING_DEBUG debug("Vertices reachable from unmatched ones via tight edges:\n"); igraph_vector_print(&vec1); igraph_vector_print(&vec2); #endif /* At this point, vec1 contains the nodes in the smaller set (A) * reachable from unmatched nodes in A via tight edges only, while vec2 * contains the nodes in the larger set (B) reachable from unmatched * nodes in A via tight edges only. Also, parent[i] >= 0 if node i * is reachable */ /* Check the edges between reachable nodes in A and unreachable * nodes in B, and find the minimum slack on them. * * Since the weights are positive, we do no harm if we first * assume that there are no "real" edges between the two sets * mentioned above and determine an upper bound for min_slack * based on this. */ min_slack = IGRAPH_INFINITY; min_slack_u = min_slack_v = 0; n = igraph_vector_size(&vec1); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] == smaller_set) continue; if (VECTOR(labels)[i] < min_slack) { min_slack = VECTOR(labels)[i]; min_slack_v = (igraph_integer_t) i; } } min_slack_2 = IGRAPH_INFINITY; for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; /* u is surely from the smaller set, but we are interested in it * only if it is reachable from an unmatched vertex */ if (VECTOR(parent)[u] < 0) continue; if (VECTOR(labels)[u] < min_slack_2) { min_slack_2 = VECTOR(labels)[u]; min_slack_u = u; } } min_slack += min_slack_2; debug("Starting approximation for min_slack = %.4f (based on vertex pair %ld--%ld)\n", min_slack, (long int)min_slack_u, (long int)min_slack_v); n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; /* u is a reachable node in A; get its incident edges. * * There are two types of incident edges: 1) real edges, * 2) phantom edges. Phantom edges were treated earlier * when we determined the initial value for min_slack. */ debug("Trying to expand along vertex %ld\n", (long int)u); neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { /* v is the vertex sitting at the other end of an edge incident * on u; check whether it was reached */ v = IGRAPH_OTHER(graph, VECTOR(*neis)[j], u); debug(" Edge %ld -- %ld (ID=%ld)\n", (long int)u, (long int)v, (long int)VECTOR(*neis)[j]); if (VECTOR(parent)[v] >= 0) { /* v was reached, so we are not interested in it */ debug(" %ld was reached, so we are not interested in it\n", (long int)v); continue; } /* v is the ID of the edge from now on */ v = (igraph_integer_t) VECTOR(*neis)[j]; if (VECTOR(slack)[v] < min_slack) { min_slack = VECTOR(slack)[v]; min_slack_u = u; min_slack_v = IGRAPH_OTHER(graph, v, u); } debug(" Slack of this edge: %.4f, min slack is now: %.4f\n", VECTOR(slack)[v], min_slack); } } debug("Minimum slack: %.4f on edge %d--%d\n", min_slack, (int)min_slack_u, (int)min_slack_v); if (min_slack > 0) { /* Decrease the label of reachable nodes in A by min_slack. * Also update the dual solution */ n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec1)[i]; VECTOR(labels)[u] -= min_slack; neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { debug(" Decreasing slack of edge %ld (%ld--%ld) by %.4f\n", (long)VECTOR(*neis)[j], (long)u, (long)IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); VECTOR(slack)[(long int)VECTOR(*neis)[j]] -= min_slack; } dual -= min_slack; } /* Increase the label of reachable nodes in B by min_slack. * Also update the dual solution */ n = igraph_vector_size(&vec2); for (i = 0; i < n; i++) { u = (igraph_integer_t) VECTOR(vec2)[i]; VECTOR(labels)[u] += min_slack; neis = igraph_inclist_get(&inclist, u); k = igraph_vector_size(neis); for (j = 0; j < k; j++) { debug(" Increasing slack of edge %ld (%ld--%ld) by %.4f\n", (long)VECTOR(*neis)[j], (long)u, (long)IGRAPH_OTHER(graph, (long)VECTOR(*neis)[j], u), min_slack); VECTOR(slack)[(long int)VECTOR(*neis)[j]] += min_slack; } dual += min_slack; } } /* Update the set of tight phantom edges. * Note that we must do it even if min_slack is zero; the reason is that * it can happen that min_slack is zero in the first step if there are * isolated nodes in the input graph. * * TODO: this is O(n^2) here. Can we do it faster? */ for (u = 0; u < no_of_nodes; u++) { if (VECTOR(*types)[u] != smaller_set) continue; for (v = 0; v < no_of_nodes; v++) { if (VECTOR(*types)[v] == smaller_set) continue; if (VECTOR(labels)[(long int)u] + VECTOR(labels)[(long int)v] <= eps) { /* Tight phantom edge found. Note that we don't have to check whether * u and v are connected; if they were, then the slack of this edge * would be negative. */ neis2 = igraph_adjlist_get(&tight_phantom_edges, u); if (!igraph_vector_int_binsearch(neis2, v, &i)) { debug("New tight phantom edge: %ld -- %ld\n", (long)u, (long)v); IGRAPH_CHECK(igraph_vector_int_insert(neis2, i, v)); } } } } #ifdef MATCHING_DEBUG debug("New labels:"); igraph_vector_print(&labels); debug("Slacks after updating with min_slack:"); igraph_vector_print(&slack); #endif } /* Cleanup: remove phantom edges from the matching */ for (i = 0; i < no_of_nodes; i++) { if (VECTOR(*types)[i] != smaller_set) continue; if (VECTOR(match)[i] != -1) { j = VECTOR(match)[i]; neis2 = igraph_adjlist_get(&tight_phantom_edges, i); if (igraph_vector_int_binsearch(neis2, j, 0)) { VECTOR(match)[i] = VECTOR(match)[j] = -1; msize--; } } } /* Fill the output parameters */ if (matching != 0) { IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); } if (matching_size != 0) { *matching_size = msize; } if (matching_weight != 0) { *matching_weight = 0; for (i = 0; i < no_of_edges; i++) { if (IS_TIGHT(i)) { IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) i, &u, &v)); if (VECTOR(match)[u] == v) *matching_weight += VECTOR(*weights)[i]; } } } /* Release everything */ #undef IS_TIGHT igraph_inclist_destroy(&inclist); igraph_adjlist_destroy(&tight_phantom_edges); igraph_vector_destroy(&parent); igraph_dqueue_long_destroy(&q); igraph_vector_destroy(&labels); igraph_vector_destroy(&vec1); igraph_vector_destroy(&vec2); igraph_vector_destroy(&slack); igraph_vector_long_destroy(&match); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; }