/**
* \function igraph_membership_reindex
* \brief Reindexes membership vector with sparse community id's to be
* dense and to start from zero
*
* This function reindexes membership vector with sparse community id's
* to be dense and to start from zero.
*
*
* \param membership Numeric vector which gives the type of each
* vertex, ie. the component to which it belongs.
* \param new2old Pointer to an vector, which will contain old id for
* each new one, if not NULL. The vector will be resized as needed.
*
* Time complexity: should be O(nlogn) for n elements.
*/
int igraph_membership_reindex(igraph_vector_t *membership,
igraph_vector_t *new2old) {
long int size=igraph_vector_size(membership);
long int ids;
long int i, p;
igraph_vector_t old_sorted, new_sorted;
// create sorted membership vector
IGRAPH_CHECK(igraph_vector_copy(&old_sorted, membership));
IGRAPH_FINALLY(igraph_vector_destroy, &old_sorted);
igraph_vector_sort(&old_sorted);
// reindex from 0 to new membership vector
IGRAPH_VECTOR_INIT_FINALLY(&new_sorted, size);
long int last_id = 0;
long int new_id = -1;
long int old_id;
for (i = 0; i < size; i++) {
old_id = (long int)VECTOR(old_sorted)[i];
if ((old_id != last_id) || (new_id == -1)) {
last_id = old_id;
new_id++;
}
VECTOR(new_sorted)[i] = new_id;
}
ids = new_id + 1;
if (new2old) {
IGRAPH_CHECK(igraph_vector_resize(new2old, ids));
igraph_vector_null(new2old);
}
// finally reindex membership vector
for (i = 0; i < size; i++) {
igraph_vector_binsearch(&old_sorted, VECTOR(*membership)[i], &p);
new_id = VECTOR(new_sorted)[p];
VECTOR(*membership)[i] = new_id;
if (new2old) {
VECTOR(*new2old)[new_id] = VECTOR(old_sorted)[p];
}
}
igraph_vector_destroy(&old_sorted);
igraph_vector_destroy(&new_sorted);
IGRAPH_FINALLY_CLEAN(2);
return 0;
}
/**
* \function igraph_shrink
* \brief Shrink the graph according to vertex membership
*
* This function shrinks the graph according to vertex membership. The
* vertices with the same id will be collapsed into one vertex in the resulting
* graph. The number of edges will remain unchanged, while rewiring them
* between new vertices with the same id.
*
*
* \param graph The graph which will be shrinked.
* \param membership Numeric vector which gives the id of each
* vertex, ie. the component to which it belongs. It will
* be modified to describe shrinked graph vertex id's.
*
* Time complexity: O(|V|log|V|+|E|), the O(nlogn) for reindexing membership vector
* plus the number of edges.
*/
int igraph_shrink(igraph_t *graph, igraph_vector_t *membership) {
igraph_vector_t new2old, edges;
long int no_of_edges=igraph_ecount(graph);
long int i, e;
igraph_integer_t from, to;
if (igraph_vector_size(membership) < igraph_vcount(graph)) {
IGRAPH_ERROR("cannot shrink graph, membership vector too short",
IGRAPH_EINVAL);
}
IGRAPH_VECTOR_INIT_FINALLY(&new2old, 0);
IGRAPH_CHECK(igraph_membership_reindex(membership, &new2old));
IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
// create shrinked graph edge list
for (e = 0; e < no_of_edges; e++) {
igraph_edge(graph, e, &from, &to);
VECTOR(edges)[e * 2] = VECTOR(*membership)[(long int) from];
VECTOR(edges)[e * 2 + 1] = VECTOR(*membership)[(long int) to];
}
long int n = igraph_vector_size(&new2old);
igraph_bool_t directed = igraph_is_directed(graph);
igraph_destroy(graph);
igraph_create(graph, &edges, n, directed);
IGRAPH_CHECK(igraph_vector_resize(membership, n));
for (i = 0; i < n; i++) {
VECTOR(*membership)[i] = VECTOR(new2old)[i];
}
igraph_vector_destroy(&new2old);
igraph_vector_destroy(&edges);
IGRAPH_FINALLY_CLEAN(2);
return 0;
}