Пример #1
0
int igraph_i_local_scan_1_directed(const igraph_t *graph,
				   igraph_vector_t *res,
				   const igraph_vector_t *weights,
				   igraph_neimode_t mode) {

  int no_of_nodes=igraph_vcount(graph);
  igraph_inclist_t incs;
  int i, node;

  igraph_vector_int_t neis;

  IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs);

  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 *edges1=igraph_inclist_get(&incs, node);
    int edgeslen1=igraph_vector_int_size(edges1);

    IGRAPH_ALLOW_INTERRUPTION();

    /* Mark neighbors and self*/
    VECTOR(neis)[node] = node+1;
    for (i=0; i<edgeslen1; i++) {
      int e=VECTOR(*edges1)[i];
      int nei=IGRAPH_OTHER(graph, e, node);
      igraph_real_t w= weights ? VECTOR(*weights)[e] : 1;
      VECTOR(neis)[nei] = node+1;
      VECTOR(*res)[node] += w;
    }

    /* Crawl neighbors */
    for (i=0; i<edgeslen1; i++) {
      int e2=VECTOR(*edges1)[i];
      int nei=IGRAPH_OTHER(graph, e2, node);
      igraph_vector_int_t *edges2=igraph_inclist_get(&incs, nei);
      int j, edgeslen2=igraph_vector_int_size(edges2);
      for (j=0; j<edgeslen2; j++) {
	int e2=VECTOR(*edges2)[j];
	int nei2=IGRAPH_OTHER(graph, e2, nei);
	igraph_real_t w2= weights ? VECTOR(*weights)[e2] : 1;
	if (VECTOR(neis)[nei2] == node+1) {
	  VECTOR(*res)[node] += w2;
	}
      }
    }

  } /* node < no_of_nodes */

  igraph_vector_int_destroy(&neis);
  igraph_inclist_destroy(&incs);
  IGRAPH_FINALLY_CLEAN(2);

  return 0;
}
Пример #2
0
static int igraph_i_bridges_rec(const igraph_t *graph, const igraph_inclist_t *il, igraph_integer_t u, igraph_integer_t *time, igraph_vector_t *bridges, igraph_vector_bool_t *visited, igraph_vector_int_t *disc, igraph_vector_int_t *low, igraph_vector_int_t *parent) {
    igraph_vector_int_t *incedges;
    long nc; /* neighbour count */
    long i;

    VECTOR(*visited)[u] = 1;

    *time += 1;

    VECTOR(*disc)[u] = *time;
    VECTOR(*low)[u] = *time;

    incedges = igraph_inclist_get(il, u);
    nc = igraph_vector_int_size(incedges);
    for (i=0; i < nc; ++i) {
        long edge = (long) VECTOR(*incedges)[i];
        igraph_integer_t v = IGRAPH_TO(graph, edge) == u ? IGRAPH_FROM(graph, edge) : IGRAPH_TO(graph, edge);

        if (! VECTOR(*visited)[v]) {
            VECTOR(*parent)[v] = u;
            IGRAPH_CHECK(igraph_i_bridges_rec(graph, il, v, time, bridges, visited, disc, low, parent));

            VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*low)[v] ? VECTOR(*low)[u] : VECTOR(*low)[v];

            if (VECTOR(*low)[v] > VECTOR(*disc)[u])
                IGRAPH_CHECK(igraph_vector_push_back(bridges, edge));
        }
        else if (v != VECTOR(*parent)[u]) {
            VECTOR(*low)[u] = VECTOR(*low)[u] < VECTOR(*disc)[v] ? VECTOR(*low)[u] : VECTOR(*disc)[v];
        }
    }

    return IGRAPH_SUCCESS;
}
Пример #3
0
int igraph_local_scan_neighborhood_ecount(const igraph_t *graph,
			  igraph_vector_t *res,
			  const igraph_vector_t *weights,
			  const igraph_vector_ptr_t *neighborhoods) {

  int node, no_of_nodes=igraph_vcount(graph);
  igraph_inclist_t incs;
  igraph_vector_int_t marked;
  igraph_bool_t directed=igraph_is_directed(graph);

  if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) {
    IGRAPH_ERROR("Invalid weight vector length in local scan", IGRAPH_EINVAL);
  }
  if (igraph_vector_ptr_size(neighborhoods) != no_of_nodes) {
    IGRAPH_ERROR("Invalid neighborhood list length in local scan",
		 IGRAPH_EINVAL);
  }

  IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
  IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_OUT));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs);

  IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
  igraph_vector_null(res);

  for (node=0; node < no_of_nodes; node++) {
    igraph_vector_int_t *nei=VECTOR(*neighborhoods)[node];
    int i, neilen=igraph_vector_int_size(nei);
    VECTOR(marked)[node] = node + 1;
    for (i=0; i<neilen; i++) {
      int vertex=VECTOR(*nei)[i];
      if (vertex < 0 || vertex >= no_of_nodes) {
	IGRAPH_ERROR("Invalid vertex id in neighborhood list in local scan",
		     IGRAPH_EINVAL);
      }
      VECTOR(marked)[vertex] = node + 1;
    }

    for (i=0; i<neilen; i++) {
      int vertex=VECTOR(*nei)[i];
      igraph_vector_int_t *edges=igraph_inclist_get(&incs, vertex);
      int j, edgeslen=igraph_vector_int_size(edges);
      for (j=0; j<edgeslen; j++) {
	int edge=VECTOR(*edges)[j];
	int nei2=IGRAPH_OTHER(graph, edge, vertex);
	if (VECTOR(marked)[nei2] == node+1) {
	  igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1;
	  VECTOR(*res)[node] += w;
	}
      }
    }
    if (!directed) { VECTOR(*res)[node] /= 2.0; }
  }

  igraph_inclist_destroy(&incs);
  igraph_vector_int_destroy(&marked);
  IGRAPH_FINALLY_CLEAN(2);

  return 0;
}
Пример #4
0
int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them,
				    int k, igraph_vector_t *res,
				    const igraph_vector_t *weights_them,
				    igraph_neimode_t mode) {

  int no_of_nodes=igraph_vcount(us);
  int node;
  igraph_dqueue_int_t Q;
  igraph_vector_int_t marked;
  igraph_stack_int_t ST;
  igraph_inclist_t incs_us, incs_them;

  if (igraph_vcount(them) != no_of_nodes) {
    IGRAPH_ERROR("Number of vertices must match in scan-k", IGRAPH_EINVAL);
  }
  if (igraph_is_directed(us) != igraph_is_directed(them)) {
    IGRAPH_ERROR("Directedness must match in scan-k", IGRAPH_EINVAL);
  }
  if (k < 0) {
    IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL);
  }
  if (weights_them &&
      igraph_vector_size(weights_them) != igraph_ecount(them)) {
    IGRAPH_ERROR("Invalid weight vector length in k-scan (them)",
		 IGRAPH_EINVAL);
  }

  if (k==0) {
    return igraph_local_scan_0_them(us, them, res, weights_them, mode);
  }
  if (k==1) {
    return igraph_local_scan_1_ecount_them(us, them, res, weights_them, mode);
  }

  /* We mark the nodes in US in a BFS. Then we check the outgoing edges
     of all marked nodes in THEM. */

  IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100));
  IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q);
  IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
  IGRAPH_CHECK(igraph_inclist_init(us, &incs_us, mode));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs_us);
  IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them);
  IGRAPH_CHECK(igraph_stack_int_init(&ST, 100));
  IGRAPH_FINALLY(igraph_stack_int_destroy, &ST);

  IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
  igraph_vector_null(res);

  for (node=0; node < no_of_nodes; node++) {

    /* BFS to mark the nodes in US */
    IGRAPH_CHECK(igraph_dqueue_int_push(&Q, node));
    IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0));
    IGRAPH_CHECK(igraph_stack_int_push(&ST, node));
    VECTOR(marked)[node] = node+1;
    while (!igraph_dqueue_int_empty(&Q)) {
      int act=igraph_dqueue_int_pop(&Q);
      int dist=igraph_dqueue_int_pop(&Q) + 1;
      igraph_vector_int_t *edges=igraph_inclist_get(&incs_us, act);
      int i, edgeslen=igraph_vector_int_size(edges);
      for (i=0; i<edgeslen; i++) {
	int edge=VECTOR(*edges)[i];
	int nei=IGRAPH_OTHER(us, edge, act);
	if (dist <= k && VECTOR(marked)[nei] != node+1) {
	  igraph_dqueue_int_push(&Q, nei);
	  igraph_dqueue_int_push(&Q, dist);
	  VECTOR(marked)[nei] = node+1;
	  igraph_stack_int_push(&ST, nei);
	}
      }
    }

    /* Now check the edges of all nodes in THEM */
    while (!igraph_stack_int_empty(&ST)) {
      int act=igraph_stack_int_pop(&ST);
      igraph_vector_int_t *edges=igraph_inclist_get(&incs_them, act);
      int i, edgeslen=igraph_vector_int_size(edges);
      for (i=0; i<edgeslen; i++) {
	int edge=VECTOR(*edges)[i];
	int nei=IGRAPH_OTHER(them, edge, act);
	if (VECTOR(marked)[nei] == node+1) {
	  igraph_real_t w=weights_them ? VECTOR(*weights_them)[edge] : 1;
	  VECTOR(*res)[node] += w;
	}
      }
    }

    if (mode == IGRAPH_ALL || ! igraph_is_directed(us)) {
      VECTOR(*res)[node] /= 2;
    }

  } /* node < no_of_nodes */

  igraph_stack_int_destroy(&ST);
  igraph_inclist_destroy(&incs_them);
  igraph_inclist_destroy(&incs_us);
  igraph_vector_int_destroy(&marked);
  igraph_dqueue_int_destroy(&Q);
  IGRAPH_FINALLY_CLEAN(5);

  return 0;
}
Пример #5
0
int igraph_local_scan_k_ecount(const igraph_t *graph, int k,
			       igraph_vector_t *res,
			       const igraph_vector_t *weights,
			       igraph_neimode_t mode) {

  int no_of_nodes=igraph_vcount(graph);
  int node;
  igraph_dqueue_int_t Q;
  igraph_vector_int_t marked;
  igraph_inclist_t incs;

  if (k < 0) {
    IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL);
  }
  if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) {
    IGRAPH_ERROR("Invalid weight vector length in k-scan", IGRAPH_EINVAL);
  }

  if (k==0) { return igraph_local_scan_0(graph, res, weights, mode); }
  if (k==1) { return igraph_local_scan_1_ecount(graph, res, weights, mode); }

  /* We do a BFS form each node, and simply count the number
     of edges on the way */

  IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100));
  IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q);
  IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
  IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs);

  IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
  igraph_vector_null(res);

  for (node=0 ; node < no_of_nodes ; node++) {
    igraph_dqueue_int_push(&Q, node);
    igraph_dqueue_int_push(&Q, 0);
    VECTOR(marked)[node] = node+1;
    while (!igraph_dqueue_int_empty(&Q)) {
      int act=igraph_dqueue_int_pop(&Q);
      int dist=igraph_dqueue_int_pop(&Q) + 1;
      igraph_vector_int_t *edges=igraph_inclist_get(&incs, act);
      int i, edgeslen=igraph_vector_int_size(edges);
      for (i=0; i<edgeslen; i++) {
	int edge=VECTOR(*edges)[i];
	int nei=IGRAPH_OTHER(graph, edge, act);
	if (dist <= k || VECTOR(marked)[nei] == node+1) {
	  igraph_real_t w=weights ? VECTOR(*weights)[edge] : 1;
	  VECTOR(*res)[node] += w;
	}
	if (dist <= k && VECTOR(marked)[nei] != node+1) {
	  igraph_dqueue_int_push(&Q, nei);
	  igraph_dqueue_int_push(&Q, dist);
	  VECTOR(marked)[nei] = node+1;
	}
      }
    }

    if (mode == IGRAPH_ALL || ! igraph_is_directed(graph)) {
      VECTOR(*res)[node] /= 2.0;
    }

  } /* node < no_of_nodes */

  igraph_inclist_destroy(&incs);
  igraph_vector_int_destroy(&marked);
  igraph_dqueue_int_destroy(&Q);
  IGRAPH_FINALLY_CLEAN(3);

  return 0;
}
Пример #6
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;
}
Пример #7
0
int igraph_i_local_scan_1_sumweights(const igraph_t *graph,
				     igraph_vector_t *res,
				     const igraph_vector_t *weights) {

  long int no_of_nodes=igraph_vcount(graph);
  long int node, i, j, nn;
  igraph_inclist_t allinc;
  igraph_vector_int_t *neis1, *neis2;
  long int neilen1, neilen2;
  long int *neis;
  long int maxdegree;

  igraph_vector_int_t order;
  igraph_vector_int_t rank;
  igraph_vector_t degree, *edge1=&degree; /* reuse degree as edge1 */

  if (igraph_vector_size(weights) != igraph_ecount(graph)) {
    IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
  }

  igraph_vector_int_init(&order, no_of_nodes);
  IGRAPH_FINALLY(igraph_vector_int_destroy, &order);
  IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);

  IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
			     IGRAPH_LOOPS));
  maxdegree=(long int) igraph_vector_max(&degree)+1;
  igraph_vector_order1_int(&degree, &order, maxdegree);
  igraph_vector_int_init(&rank, no_of_nodes);
  IGRAPH_FINALLY(igraph_vector_int_destroy, &rank);
  for (i=0; i<no_of_nodes; i++) {
    VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes-i-1;
  }

  IGRAPH_CHECK(igraph_inclist_init(graph, &allinc, IGRAPH_ALL));
  IGRAPH_FINALLY(igraph_inclist_destroy, &allinc);
  IGRAPH_CHECK(igraph_i_trans4_il_simplify(graph, &allinc, &rank));

  neis=igraph_Calloc(no_of_nodes, long int);
  if (neis==0) {
    IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM);
  }
  IGRAPH_FINALLY(igraph_free, neis);

  IGRAPH_CHECK(igraph_strength(graph, res, igraph_vss_all(), IGRAPH_ALL,
			       IGRAPH_LOOPS, weights));

  for (nn=no_of_nodes-1; nn>=0; nn--) {
    node=VECTOR(order)[nn];

    IGRAPH_ALLOW_INTERRUPTION();

    neis1=igraph_inclist_get(&allinc, node);
    neilen1=igraph_vector_int_size(neis1);

    /* Mark the neighbors of the node */
    for (i=0; i<neilen1; i++) {
      int edge = VECTOR(*neis1)[i];
      int nei = IGRAPH_OTHER(graph, edge, node);
      VECTOR(*edge1)[nei] = VECTOR(*weights)[edge];
      neis[nei] = node+1;
    }

    for (i=0; i<neilen1; i++) {
      long int edge=VECTOR(*neis1)[i];
      long int nei=IGRAPH_OTHER(graph, edge, node);
      igraph_real_t w=VECTOR(*weights)[edge];
      neis2=igraph_inclist_get(&allinc, nei);
      neilen2=igraph_vector_int_size(neis2);
      for (j=0; j<neilen2; j++) {
	long int edge2=VECTOR(*neis2)[j];
	long int nei2=IGRAPH_OTHER(graph, edge2, nei);
	igraph_real_t w2=VECTOR(*weights)[edge2];
	if (neis[nei2] == node+1) {
	  VECTOR(*res)[node] += w2;
	  VECTOR(*res)[nei2] += w;
	  VECTOR(*res)[nei] += VECTOR(*edge1)[nei2];
	}
      }
    }
  }

  igraph_free(neis);
  igraph_inclist_destroy(&allinc);
  igraph_vector_int_destroy(&rank);
  igraph_vector_destroy(&degree);
  igraph_vector_int_destroy(&order);
  IGRAPH_FINALLY_CLEAN(5);

  return 0;
}
Пример #8
0
int igraph_i_local_scan_1_directed_all(const igraph_t *graph,
				       igraph_vector_t *res,
				       const igraph_vector_t *weights) {

  int no_of_nodes=igraph_vcount(graph);
  igraph_inclist_t incs;
  int i, node;

  igraph_vector_int_t neis;

  IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL));
  IGRAPH_FINALLY(igraph_inclist_destroy, &incs);

  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 *edges1=igraph_inclist_get(&incs, node);
    int edgeslen1=igraph_vector_int_size(edges1);

    IGRAPH_ALLOW_INTERRUPTION();

    /* Mark neighbors. We also count the edges that are incident to ego.
       Note that this time we do not mark ego, because we don't want to
       double count its incident edges later, when we are going over the
       incident edges of ego's neighbors. */
    for (i=0; i<edgeslen1; i++) {
      int e=VECTOR(*edges1)[i];
      int nei=IGRAPH_OTHER(graph, e, node);
      igraph_real_t w= weights ? VECTOR(*weights)[e] : 1;
      VECTOR(neis)[nei] = node+1;
      VECTOR(*res)[node] += w;
    }

    /* Crawl neighbors. We make sure that each neighbor of 'node' is
       only crawed once. We count all qualifying edges of ego, and
       then unmark ego to avoid double counting. */
    for (i=0; i<edgeslen1; i++) {
      int e2=VECTOR(*edges1)[i];
      int nei=IGRAPH_OTHER(graph, e2, node);
      igraph_vector_int_t *edges2;
      int j, edgeslen2;
      if (VECTOR(neis)[nei] != node+1) { continue; }
      edges2=igraph_inclist_get(&incs, nei);
      edgeslen2=igraph_vector_int_size(edges2);
      for (j=0; j<edgeslen2; j++) {
	int e2=VECTOR(*edges2)[j];
	int nei2=IGRAPH_OTHER(graph, e2, nei);
	igraph_real_t w2= weights ? VECTOR(*weights)[e2] : 1;
	if (VECTOR(neis)[nei2] == node+1) {
	  VECTOR(*res)[node] += w2;
	}
      }
      VECTOR(neis)[nei] = 0;
    }

  } /* node < no_of_nodes */

  igraph_vector_int_destroy(&neis);
  igraph_inclist_destroy(&incs);
  IGRAPH_FINALLY_CLEAN(2);

  return 0;
}
Пример #9
0
int igraph_biconnected_components(const igraph_t *graph,
				  igraph_integer_t *no,
				  igraph_vector_ptr_t *tree_edges,
				  igraph_vector_ptr_t *component_edges,
				  igraph_vector_ptr_t *components,
				  igraph_vector_t *articulation_points) {

  long int no_of_nodes=igraph_vcount(graph);
  igraph_vector_long_t nextptr;
  igraph_vector_long_t num, low;
  igraph_vector_bool_t found;
  igraph_vector_int_t *adjedges;
  igraph_stack_t path;
  igraph_vector_t edgestack;
  igraph_inclist_t inclist;
  long int i, counter, rootdfs=0;  
  igraph_vector_long_t vertex_added;
  long int comps=0;
  igraph_vector_ptr_t *mycomponents=components, vcomponents;

  IGRAPH_CHECK(igraph_vector_long_init(&nextptr, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_long_destroy, &nextptr);
  IGRAPH_CHECK(igraph_vector_long_init(&num, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_long_destroy, &num);
  IGRAPH_CHECK(igraph_vector_long_init(&low, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_long_destroy, &low);
  IGRAPH_CHECK(igraph_vector_bool_init(&found, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_bool_destroy, &found);

  IGRAPH_CHECK(igraph_stack_init(&path, 100));
  IGRAPH_FINALLY(igraph_stack_destroy, &path);
  IGRAPH_VECTOR_INIT_FINALLY(&edgestack, 0);
  IGRAPH_CHECK(igraph_vector_reserve(&edgestack, 100));

  IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL));
  IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);

  IGRAPH_CHECK(igraph_vector_long_init(&vertex_added, no_of_nodes));
  IGRAPH_FINALLY(igraph_vector_long_destroy, &vertex_added);

  if (no) {
    *no=0;
  }
  if (tree_edges) {
    igraph_vector_ptr_clear(tree_edges);
  }
  if (components) {
    igraph_vector_ptr_clear(components);
  }
  if (component_edges) {
    igraph_vector_ptr_clear(component_edges);
  }
  if (articulation_points) {
    igraph_vector_clear(articulation_points);
  }
  if (component_edges && !components) {
    mycomponents=&vcomponents;
    IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0));
    IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents);
  }

  for (i=0; i<no_of_nodes; i++) {
    
    if (VECTOR(low)[i] != 0) { continue; } /* already visited */

    IGRAPH_ALLOW_INTERRUPTION();

    IGRAPH_CHECK(igraph_stack_push(&path, i));
    counter=1; 
    rootdfs=0;
    VECTOR(low)[i]=VECTOR(num)[i]=counter++;
    while (!igraph_stack_empty(&path)) {
      long int n;
      long int act=(long int) igraph_stack_top(&path);
      long int actnext=VECTOR(nextptr)[act];
      
      adjedges=igraph_inclist_get(&inclist, act);
      n=igraph_vector_int_size(adjedges);
      if (actnext < n) {
	/* Step down (maybe) */
	long int edge=(long int) VECTOR(*adjedges)[actnext];
	long int nei=IGRAPH_OTHER(graph, edge, act);
	if (VECTOR(low)[nei] == 0) {
	  if (act==i) { rootdfs++; }
	  IGRAPH_CHECK(igraph_vector_push_back(&edgestack, edge));
	  IGRAPH_CHECK(igraph_stack_push(&path, nei));
	  VECTOR(low)[nei] = VECTOR(num)[nei]=counter++;
	} else {
	  /* Update low value if needed */
	  if (VECTOR(num)[nei] < VECTOR(low)[act]) {
	    VECTOR(low)[act]=VECTOR(num)[nei];
	  }
	}
	VECTOR(nextptr)[act] += 1;
      } else {
	/* Step up */
	igraph_stack_pop(&path);
	if (!igraph_stack_empty(&path)) {
	  long int prev=(long int) igraph_stack_top(&path);
	  /* Update LOW value if needed */
	  if (VECTOR(low)[act] < VECTOR(low)[prev]) {
	    VECTOR(low)[prev] = VECTOR(low)[act];
	  }
	  /* Check for articulation point */
	  if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
	    if (articulation_points && !VECTOR(found)[prev] 
		&& prev != i /* the root */) {
	      IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev));
	      VECTOR(found)[prev] = 1;
	    }
	    if (no) { *no += 1; }

	    /*------------------------------------*/
	    /* Record the biconnected component just found */
	    if (tree_edges || mycomponents) {
	      igraph_vector_t *v = 0, *v2 = 0;
	      comps++;
	      if (tree_edges) { 
		v=igraph_Calloc(1, igraph_vector_t);
		if (!v) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); }
		IGRAPH_CHECK(igraph_vector_init(v, 0));
		IGRAPH_FINALLY(igraph_vector_destroy, v);
	      }
	      if (mycomponents) {
		v2=igraph_Calloc(1, igraph_vector_t);
		if (!v2) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); }
		IGRAPH_CHECK(igraph_vector_init(v2, 0));
		IGRAPH_FINALLY(igraph_vector_destroy, v2);
	      }
	      
	      while (!igraph_vector_empty(&edgestack)) {
		long int e=(long int) igraph_vector_pop_back(&edgestack);
		long int from=IGRAPH_FROM(graph,e);
		long int to=IGRAPH_TO(graph,e);
		if (tree_edges) { 
		  IGRAPH_CHECK(igraph_vector_push_back(v, e));
		}
		if (mycomponents) {
		  if (VECTOR(vertex_added)[from] != comps) { 
		    VECTOR(vertex_added)[from] = comps;
		    IGRAPH_CHECK(igraph_vector_push_back(v2, from));
		  }
		  if (VECTOR(vertex_added)[to] != comps) {
		    VECTOR(vertex_added)[to] = comps;
		    IGRAPH_CHECK(igraph_vector_push_back(v2, to));
		  }
		}
		if (from==prev || to==prev) {
		  break;
		}
	      }
	      
	      if (mycomponents) {
		IGRAPH_CHECK(igraph_vector_ptr_push_back(mycomponents, v2));
		IGRAPH_FINALLY_CLEAN(1);
	      }
	      if (tree_edges) { 
		IGRAPH_CHECK(igraph_vector_ptr_push_back(tree_edges, v));
		IGRAPH_FINALLY_CLEAN(1);
	      }
	      if (component_edges) {
		igraph_vector_t *nodes=VECTOR(*mycomponents)[comps-1];
		igraph_vector_t *vv=igraph_Calloc(1, igraph_vector_t);
		long int ii, no_vert=igraph_vector_size(nodes);
		if (!vv) { IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); }
		IGRAPH_CHECK(igraph_vector_init(vv, 0));
		IGRAPH_FINALLY(igraph_vector_destroy, vv);
		for (ii=0; ii<no_vert; ii++) {
		  long int vert=(long int) VECTOR(*nodes)[ii];
		  igraph_vector_int_t *edges=igraph_inclist_get(&inclist, 
								vert);
		  long int j, nn=igraph_vector_int_size(edges);
		  for (j=0; j<nn; j++) {
		    long int e=(long int) VECTOR(*edges)[j];
		    long int nei=IGRAPH_OTHER(graph, e, vert);
		    if (VECTOR(vertex_added)[nei] == comps && nei<vert) {
		      IGRAPH_CHECK(igraph_vector_push_back(vv, e));
		    }
		  }
		}
		IGRAPH_CHECK(igraph_vector_ptr_push_back(component_edges, vv));
		IGRAPH_FINALLY_CLEAN(1);
	      }
	    } /* record component if requested */
	    /*------------------------------------*/

	  }
	} /* !igraph_stack_empty(&path) */
      }
      
    } /* !igraph_stack_empty(&path) */
    
    if (articulation_points && rootdfs >= 2) {
      IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i));
    }

  } /* i < no_of_nodes */

  if (mycomponents != components) {
    igraph_i_free_vectorlist(mycomponents);
    IGRAPH_FINALLY_CLEAN(1);
  }

  igraph_vector_long_destroy(&vertex_added);
  igraph_inclist_destroy(&inclist);
  igraph_vector_destroy(&edgestack);
  igraph_stack_destroy(&path);
  igraph_vector_bool_destroy(&found);
  igraph_vector_long_destroy(&low);
  igraph_vector_long_destroy(&num);
  igraph_vector_long_destroy(&nextptr);
  IGRAPH_FINALLY_CLEAN(8);

  return 0;
}
Пример #10
0
int igraph_difference(igraph_t *res, 
		      const igraph_t *orig, const igraph_t *sub) {

  /* Quite nasty, but we will use that an edge adjacency list
     contains the vertices according to the order of the 
     vertex ids at the "other" end of the edge. */

  long int no_of_nodes_orig=igraph_vcount(orig);
  long int no_of_nodes_sub =igraph_vcount(sub);
  long int no_of_nodes=no_of_nodes_orig;
  long int smaller_nodes;
  igraph_bool_t directed=igraph_is_directed(orig);
  igraph_vector_t edges;
  igraph_vector_t edge_ids;
  igraph_vector_t *nei1, *nei2;
  igraph_inclist_t inc_orig, inc_sub;
  long int i;
  igraph_integer_t v1, v2;

  if (directed != igraph_is_directed(sub)) {
    IGRAPH_ERROR("Cannot subtract directed and undirected graphs",
		 IGRAPH_EINVAL);
  }
  
  IGRAPH_VECTOR_INIT_FINALLY(&edge_ids, 0);
  IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
  IGRAPH_CHECK(igraph_inclist_init(orig, &inc_orig, IGRAPH_OUT));
  IGRAPH_FINALLY(igraph_inclist_destroy, &inc_orig);
  IGRAPH_CHECK(igraph_inclist_init(sub, &inc_sub, IGRAPH_OUT));
  IGRAPH_FINALLY(igraph_inclist_destroy, &inc_sub);
  
  smaller_nodes=no_of_nodes_orig > no_of_nodes_sub ?
    no_of_nodes_sub : no_of_nodes_orig;
  
  for (i=0; i<smaller_nodes; i++) {
    long int n1, n2, e1, e2;
    IGRAPH_ALLOW_INTERRUPTION();
    nei1=igraph_inclist_get(&inc_orig, i);
    nei2=igraph_inclist_get(&inc_sub, i);
    n1=igraph_vector_size(nei1)-1;
    n2=igraph_vector_size(nei2)-1;
    while (n1>=0 && n2>=0) {
      e1=(long int) VECTOR(*nei1)[n1];
      e2=(long int) VECTOR(*nei2)[n2];
      v1=IGRAPH_OTHER(orig, e1, i);
      v2=IGRAPH_OTHER(sub, e2, i);
      
      if (!directed && v1<i) { 
	n1--;
      } else if (!directed && v2<i) {
	n2--;
      } else if (v1>v2) {
	IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
	n1--;	
      } else if (v2>v1) {
	n2--;
      } else {
	n1--;
	n2--;
      }
    }
    
    /* Copy remaining edges */
    while (n1>=0) {
      e1=(long int) VECTOR(*nei1)[n1];
      v1=IGRAPH_OTHER(orig, e1, i);
      if (directed || v1 >= i) { 
	IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
      }
      n1--;
    }
  }

  /* copy remaining edges, use the previous value of 'i' */
  for (; i<no_of_nodes_orig; i++) {
    long int n1, e1;
    nei1=igraph_inclist_get(&inc_orig, i);
    n1=igraph_vector_size(nei1)-1;
    while (n1>=0) {
      e1=(long int) VECTOR(*nei1)[n1];
      v1=IGRAPH_OTHER(orig, e1, i);
      if (directed || v1 >= i) { 
	IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, i));
	IGRAPH_CHECK(igraph_vector_push_back(&edges, v1));
      }
      n1--;
    }
  }

  igraph_inclist_destroy(&inc_sub);
  igraph_inclist_destroy(&inc_orig);
  IGRAPH_FINALLY_CLEAN(2);
  IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes,
			     directed));
  igraph_vector_destroy(&edges);  
  IGRAPH_FINALLY_CLEAN(1);

  /* Attributes */
  if (orig->attr) {
    IGRAPH_I_ATTRIBUTE_DESTROY(res);
    IGRAPH_I_ATTRIBUTE_COPY(res, orig, /*graph=*/1, /*vertex=*/1, /*edge=*/0);
    IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids));
  }
  
  igraph_vector_destroy(&edge_ids);
  IGRAPH_FINALLY_CLEAN(1);
  
  return 0;
}
Пример #11
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;
}