Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
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);
  }
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
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;
}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
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;
}