コード例 #1
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static real *get_or_assign_node_force(real *force, int i, SingleLinkedList l, int dim){

  real *f = (real*) node_data_get_data(SingleLinkedList_get_data(l));

  if (!f){
    node_data_get_data(SingleLinkedList_get_data(l)) = &(force[i*dim]);
    f = (real*) node_data_get_data(SingleLinkedList_get_data(l));
  }
  return f;
}
コード例 #2
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static void QuadTree_get_nearest_internal(QuadTree qt, real *x, real *y, real *min, int *imin, int tentative, int *flag){
  /* get the narest point years to {x[0], ..., x[dim]} and store in y.*/
  SingleLinkedList l;
  real *coord, dist;
  int dim, i, iq = -1;
  real qmin;
  real *point = x;

  *flag = 0;
  if (!qt) return;
  dim = qt->dim;
  l = qt->l;
  if (l){
    while (l){
      coord = node_data_get_coord(SingleLinkedList_get_data(l));
      dist = point_distance(point, coord, dim);
      if(*min < 0 || dist < *min) {
	*min = dist;
	*imin = node_data_get_id(SingleLinkedList_get_data(l));
	for (i = 0; i < dim; i++) y[i] = coord[i];
      }
      l = SingleLinkedList_get_next(l);
    }
  }
  
  if (qt->qts){
    dist = point_distance(qt->center, point, dim); 
    if (*min >= 0 && (dist - sqrt((real) dim) * qt->width > *min)){
      return;
    } else {
      if (tentative){/* quick first approximation*/
	qmin = -1;
	for (i = 0; i < 1<<dim; i++){
	  if (qt->qts[i]){
	    dist = point_distance(qt->qts[i]->average, point, dim); 
	    if (dist < qmin || qmin < 0){
	      qmin = dist; iq = i;
	    }
	  }
	}
	assert(iq >= 0);
	QuadTree_get_nearest_internal(qt->qts[iq], x, y, min, imin, tentative, flag);
      } else {
	for (i = 0; i < 1<<dim; i++){
	  QuadTree_get_nearest_internal(qt->qts[i], x, y, min, imin, tentative, flag);
	}
      }
    }
  }

}
コード例 #3
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
void QuadTree_get_supernodes_internal(QuadTree qt, real bh, real *point, int nodeid, int *nsuper, int *nsupermax, real **center, real **supernode_wgts, real **distances, real *counts, int *flag){
  SingleLinkedList l;
  real *coord, dist;
  int dim, i;

  (*counts)++;

  if (!qt) return;
  dim = qt->dim;
  l = qt->l;
  if (l){
    while (l){
      check_or_realloc_arrays(dim, nsuper, nsupermax, center, supernode_wgts, distances);
      if (node_data_get_id(SingleLinkedList_get_data(l)) != nodeid){
	coord = node_data_get_coord(SingleLinkedList_get_data(l));
	for (i = 0; i < dim; i++){
	  (*center)[dim*(*nsuper)+i] = coord[i];
	}
	(*supernode_wgts)[*nsuper] = node_data_get_weight(SingleLinkedList_get_data(l));
	(*distances)[*nsuper] = point_distance(point, coord, dim);
	(*nsuper)++;
      }
      l = SingleLinkedList_get_next(l);
    }
  }

  if (qt->qts){
    dist = point_distance(qt->center, point, dim); 
    if (qt->width < bh*dist){
      check_or_realloc_arrays(dim, nsuper, nsupermax, center, supernode_wgts, distances);
      for (i = 0; i < dim; i++){
        (*center)[dim*(*nsuper)+i] = qt->average[i];
      }
      (*supernode_wgts)[*nsuper] = qt->total_weight;
      (*distances)[*nsuper] = point_distance(qt->average, point, dim); 
      (*nsuper)++;
    } else {
      for (i = 0; i < 1<<dim; i++){
	QuadTree_get_supernodes_internal(qt->qts[i], bh, point, nodeid, nsuper, nsupermax, center, 
					 supernode_wgts, distances, counts, flag);
      }
    }
  }

}
コード例 #4
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static void QuadTree_print_internal(FILE *fp, QuadTree q, int level){
  /* dump a quad tree in Mathematica format. */
  SingleLinkedList l, l0;
  real *coord;
  int i, dim;

  if (!q) return;

  draw_polygon(fp, q->dim, q->center, q->width);
  dim = q->dim;
  
  l0 = l = q->l;
  if (l){
    printf(",(*a*) {Red,");
    while (l){
      if (l != l0) printf(",");
      coord = node_data_get_coord(SingleLinkedList_get_data(l));
      fprintf(fp, "(*node %d*) Point[{",  node_data_get_id(SingleLinkedList_get_data(l)));
      for (i = 0; i < dim; i++){
	if (i != 0) printf(",");
	fprintf(fp, "%f",coord[i]);
      }
      fprintf(fp, "}]");
      l = SingleLinkedList_get_next(l);
    }
    fprintf(fp, "}");
  }

  if (q->qts){
    for (i = 0; i < 1<<dim; i++){
      fprintf(fp, ",(*b*){");
      QuadTree_print_internal(fp, q->qts[i], level + 1); 
      fprintf(fp, "}");
    }
  }


}
コード例 #5
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static void QuadTree_repulsive_force_accumulate(QuadTree qt, real *force, real *counts){
  /* push down forces on cells into the node level */
  real wgt, wgt2;
  real *f, *f2;
  SingleLinkedList l = qt->l;
  int i, k, dim;
  QuadTree qt2;

  dim = qt->dim;
  wgt = qt->total_weight;
  f = get_or_alloc_force_qt(qt, dim);
  assert(wgt > 0);
  counts[2]++;

  if (l){
    while (l){
      i = node_data_get_id(SingleLinkedList_get_data(l));
      f2 = get_or_assign_node_force(force, i, l, dim);
      wgt2 = node_data_get_weight(SingleLinkedList_get_data(l));
      wgt2 = wgt2/wgt;
      for (k = 0; k < dim; k++) f2[k] += wgt2*f[k];
      l = SingleLinkedList_get_next(l);
    }
    return;
  }

  for (i = 0; i < 1<<dim; i++){
    qt2 = qt->qts[i];
    if (!qt2) continue;
    assert(qt2->n > 0);
    f2 = get_or_alloc_force_qt(qt2, dim);
    wgt2 = qt2->total_weight;
    wgt2 = wgt2/wgt;
    for (k = 0; k < dim; k++) f2[k] += wgt2*f[k];
    QuadTree_repulsive_force_accumulate(qt2, force, counts);
  }

}
コード例 #6
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static QuadTree QuadTree_add_internal(QuadTree q, real *coord, real weight, int id, int level){
  int i, dim = q->dim, ii;
  node_data nd = NULL;

  int max_level = q->max_level;
  int idd;

  /* Make sure that coord is within bounding box */
  for (i = 0; i < q->dim; i++) {
    if (coord[i] < q->center[i] - q->width - 1.e5*MACHINEACC*q->width || coord[i] > q->center[i] + q->width + 1.e5*MACHINEACC*q->width) {
#ifdef DEBUG_PRINT
      fprintf(stderr,"coordinate %f is outside of the box:{%f, %f}, \n(q->center[i] - q->width) - coord[i] =%g, coord[i]-(q->center[i] + q->width) = %g\n",coord[i], (q->center[i] - q->width), (q->center[i] + q->width),
	      (q->center[i] - q->width) - coord[i],  coord[i]-(q->center[i] + q->width));
#endif
      //return NULL;
    }
  }

  if (q->n == 0){
    /* if this node is empty right now */
    q->n = 1;
    q->total_weight = weight;
    q->average = MALLOC(sizeof(real)*dim);
    for (i = 0; i < q->dim; i++) q->average[i] = coord[i];
    nd = node_data_new(q->dim, weight, coord, id);
    assert(!(q->l));
    q->l = SingleLinkedList_new(nd);
  } else if (level < max_level){
    /* otherwise open up into 2^dim quadtrees unless the level is too high */
    q->total_weight += weight;
    for (i = 0; i < q->dim; i++) q->average[i] = ((q->average[i])*q->n + coord[i])/(q->n + 1);
    if (!q->qts){
      q->qts = MALLOC(sizeof(QuadTree)*(1<<dim));
      for (i = 0; i < 1<<dim; i++) q->qts[i] = NULL;
    }/* done adding new quadtree, now add points to them */
    
    /* insert the old node (if exist) and the current node into the appropriate child quadtree */
    ii = QuadTree_get_quadrant(dim, q->center, coord);
    assert(ii < 1<<dim && ii >= 0);
    if (q->qts[ii] == NULL) q->qts[ii] = QuadTree_new_in_quadrant(q->dim, q->center, (q->width)/2, max_level, ii);
    
    q->qts[ii] = QuadTree_add_internal(q->qts[ii], coord, weight, id, level + 1);
    assert(q->qts[ii]);

    if (q->l){
      idd = node_data_get_id(SingleLinkedList_get_data(q->l));
      assert(q->n == 1);
      coord = node_data_get_coord(SingleLinkedList_get_data(q->l));
      weight = node_data_get_weight(SingleLinkedList_get_data(q->l));
      ii = QuadTree_get_quadrant(dim, q->center, coord);
      assert(ii < 1<<dim && ii >= 0);

      if (q->qts[ii] == NULL) q->qts[ii] = QuadTree_new_in_quadrant(q->dim, q->center, (q->width)/2, max_level, ii);

      q->qts[ii] = QuadTree_add_internal(q->qts[ii], coord, weight, idd, level + 1);
      assert(q->qts[ii]);
      
      /* delete the old node data on parent */
      SingleLinkedList_delete(q->l, node_data_delete);
      q->l = NULL;
    }
    
    (q->n)++;
  } else {
    assert(!(q->qts));
    /* level is too high, append data in the linked list */
    (q->n)++;
    q->total_weight += weight;
    for (i = 0; i < q->dim; i++) q->average[i] = ((q->average[i])*q->n + coord[i])/(q->n + 1);
    nd = node_data_new(q->dim, weight, coord, id);
    assert(q->l);
    q->l = SingleLinkedList_prepend(q->l, nd);
  }
  return q;
}
コード例 #7
0
ファイル: QuadTree.c プロジェクト: AhmedAMohamed/graphviz
static void QuadTree_repulsive_force_interact(QuadTree qt1, QuadTree qt2, real *x, real *force, real bh, real p, real KP, real *counts){
  /* calculate the all to all reopulsive force and accumulate on each node of the quadtree if an interaction is possible.
     force[i*dim+j], j=1,...,dim is teh force on node i 
   */
  SingleLinkedList l1, l2;
  real *x1, *x2, dist, wgt1, wgt2, f, *f1, *f2, w1, w2;
  int dim, i, j, i1, i2, k;
  QuadTree qt11, qt12; 

  if (!qt1 || !qt2) return;
  assert(qt1->n > 0 && qt2->n > 0);
  dim = qt1->dim;

  l1 = qt1->l;
  l2 = qt2->l;

  /* far enough, calculate repulsive force */
  dist = point_distance(qt1->average, qt2->average, dim); 
  if (qt1->width + qt2->width < bh*dist){
    counts[0]++;
    x1 = qt1->average;
    w1 = qt1->total_weight;
    f1 = get_or_alloc_force_qt(qt1, dim);
    x2 = qt2->average;
    w2 = qt2->total_weight;
    f2 = get_or_alloc_force_qt(qt2, dim);
    assert(dist > 0);
    for (k = 0; k < dim; k++){
      if (p == -1){
	f = w1*w2*KP*(x1[k] - x2[k])/(dist*dist);
      } else {
	f = w1*w2*KP*(x1[k] - x2[k])/pow(dist, 1.- p);
      }
      f1[k] += f;
      f2[k] -= f;
    }
    return;
  }


  /* both at leaves, calculate repulsive force */
  if (l1 && l2){
    while (l1){
      x1 = node_data_get_coord(SingleLinkedList_get_data(l1));
      wgt1 = node_data_get_weight(SingleLinkedList_get_data(l1));
      i1 = node_data_get_id(SingleLinkedList_get_data(l1));
      f1 = get_or_assign_node_force(force, i1, l1, dim);
      l2 = qt2->l;
      while (l2){
	x2 = node_data_get_coord(SingleLinkedList_get_data(l2));
	wgt2 = node_data_get_weight(SingleLinkedList_get_data(l2));
	i2 = node_data_get_id(SingleLinkedList_get_data(l2));
	f2 = get_or_assign_node_force(force, i2, l2, dim);
	if ((qt1 == qt2 && i2 < i1) || i1 == i2) {
	  l2 = SingleLinkedList_get_next(l2);
	  continue;
	}
	counts[1]++;
	dist = distance_cropped(x, dim, i1, i2);
	for (k = 0; k < dim; k++){
	  if (p == -1){
	    f = wgt1*wgt2*KP*(x1[k] - x2[k])/(dist*dist);
	  } else {
	    f = wgt1*wgt2*KP*(x1[k] - x2[k])/pow(dist, 1.- p);
	  }
	  f1[k] += f;
	  f2[k] -= f;
	}
	l2 = SingleLinkedList_get_next(l2);
      }
      l1 = SingleLinkedList_get_next(l1);
    }
    return;
  }


  /* identical, split one */
  if (qt1 == qt2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	for (j = i; j < 1<<dim; j++){
	  qt12 = qt1->qts[j];
	  QuadTree_repulsive_force_interact(qt11, qt12, x, force, bh, p, KP, counts);
	}
      }
  } else {
    /* split the one with bigger box, or one not at the last level */
    if (qt1->width > qt2->width && !l1){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt2, x, force, bh, p, KP, counts);
      }
    } else if (qt2->width > qt1->width && !l2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt2->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt1, x, force, bh, p, KP, counts);
      }
    } else if (!l1){/* pick one that is not at the last level */
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt2, x, force, bh, p, KP, counts);
      }
    } else if (!l2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt2->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt1, x, force, bh, p, KP, counts);
      }
    } else {
      assert(0); /* can be both at the leaf level since that should be catched at the beginning of this func. */
    }
  }
}
コード例 #8
0
ファイル: mq.c プロジェクト: tomgr/graphviz-cmake
Multilevel_MQ_Clustering Multilevel_MQ_Clustering_establish(Multilevel_MQ_Clustering grid, int maxcluster) {
    int *matching = grid->matching;
    SparseMatrix A = grid->A;
    int n = grid->n, level = grid->level, nc = 0, nclusters = n;
    real mq = 0, mq_in = 0, mq_out = 0, mq_new, mq_in_new, mq_out_new, mq_max = 0, mq_in_max = 0, mq_out_max = 0;
    int *ia = A->ia, *ja = A->ja;
    real *a, amax = 0;
    real *deg_intra = grid->deg_intra, *wgt = grid->wgt;
    real *deg_intra_new, *wgt_new = NULL;
    int i, j, k, jj, jc, jmax;
    real *deg_inter, gain = 0, *dout = grid->dout, *dout_new, deg_in_i, deg_in_j, wgt_i, wgt_j, a_ij, dout_i, dout_j, dout_max = 0, wgt_jmax = 0;
    int *mask;
    real maxgain = 0;
    real total_gain = 0;
    SingleLinkedList *neighbors = NULL, lst;


    neighbors = MALLOC(sizeof(SingleLinkedList)*n);
    for (i = 0; i < n; i++) neighbors[i] = NULL;

    mq = grid->mq;
    mq_in = grid->mq_in;
    mq_out = grid->mq_out;

    deg_intra_new = MALLOC(sizeof(real)*n);
    wgt_new = MALLOC(sizeof(real)*n);
    deg_inter = MALLOC(sizeof(real)*n);
    mask = MALLOC(sizeof(int)*n);
    dout_new = MALLOC(sizeof(real)*n);
    for (i = 0; i < n; i++) mask[i] = -1;

    assert(n == A->n);
    for (i = 0; i < n; i++) matching[i] = UNMATCHED;

    /* gain in merging node A into cluster B is
       mq_in_new = mq_in - |E(A,A)|/(V(A))^2 - |E(B,B)|/(V(B))^2 + (|E(A,A)|+|E(B,B)|+|E(A,B)|)/(|V(A)|+|V(B)|)^2
       .         = mq_in - deg_intra(A)/|A|^2 - deg_intra(B)/|B|^2 + (deg_intra(A)+deg_intra(B)+a(A,B))/(|A|+|B|)^2

       mq_out_new = mq_out - |E(A,B)|/(|V(A)|*V(B)|)-\sum_{C and A connected, C!=B} |E(A,C)|/(|V(A)|*|V(C)|)-\sum_{C and B connected,C!=B} |E(B,C)|/(|V(B)|*|V(C)|)
       .                  + \sum_{C connected to A or B, C!=A, C!=B} (|E(A,C)|+|E(B,C)|)/(|V(C)|*(|V(A)|+|V(B)|)
       .          = mq_out + a(A,B)/(|A|*|B|)-\sum_{C and A connected} a(A,C)/(|A|*|C|)-\sum_{C and B connected} a(B,C)/(|B|*|C|)
       .                  + \sum_{C connected to A or B, C!=A, C!=B} (a(A,C)+a(B,C))/(|C|*(|A|+|B|))
       Denote:
       dout(i) = \sum_{j -- i} a(i,j)/|j|
       then

       mq_out_new = mq_out - |E(A,B)|/(|V(A)|*V(B)|)-\sum_{C and A connected, C!=B} |E(A,C)|/(|V(A)|*|V(C)|)-\sum_{C and B connected,C!=B} |E(B,C)|/(|V(B)|*|V(C)|)
       .                  + \sum_{C connected to A or B, C!=A, C!=B} (|E(A,C)|+|E(B,C)|)/(|V(C)|*(|V(A)|+|V(B)|)
       .          = mq_out + a(A,B)/(|A|*|B|)-dout(A)/|A| - dout(B)/|B|
       .                  + (dout(A)+dout(B))/(|A|+|B|) - (a(A,B)/|A|+a(A,B)/|B|)/(|A|+|B|)
       .          = mq_out -dout(A)/|A| - dout(B)/|B| + (dout(A)+dout(B))/(|A|+|B|)
       after merging A and B into cluster AB,
       dout(AB) = dout(A) + dout(B);
       dout(C) := dout(C) - a(A,C)/|A| - a(B,C)/|B| + a(A,C)/(|A|+|B|) + a(B, C)/(|A|+|B|)

       mq_new = mq_in_new/(k-1) - mq_out_new/((k-1)*(k-2))
       gain = mq_new - mq
    */
    a = (real*) A->a;
    for (i = 0; i < n; i++) {
        if (matching[i] != UNMATCHED) continue;
        /* accumulate connections between i and clusters */
        for (j = ia[i]; j < ia[i+1]; j++) {
            jj = ja[j];
            if (jj == i) continue;
            if ((jc=matching[jj]) != UNMATCHED) {
                if (mask[jc] != i) {
                    mask[jc] = i;
                    deg_inter[jc] = a[j];
                } else {
                    deg_inter[jc] += a[j];
                }
            }
        }
        deg_in_i = deg_intra[i];
        wgt_i = wgt[i];
        dout_i = dout[i];

        maxgain = 0;
        jmax = -1;
        for (j = ia[i]; j < ia[i+1]; j++) {
            jj = ja[j];
            if (jj == i) continue;
            jc = matching[jj];
            if (jc == UNMATCHED) {
                a_ij = a[j];
                wgt_j = wgt[jj];
                deg_in_j = deg_intra[jj];
                dout_j = dout[jj];
            } else if (deg_inter[jc] < 0) {
                continue;
            } else {
                a_ij = deg_inter[jc];
                wgt_j = wgt_new[jc];
                deg_inter[jc] = -1; /* so that we do not redo the calulation when we hit another neighbor in cluster jc */
                deg_in_j = deg_intra_new[jc];
                dout_j = dout_new[jc];
            }

            mq_in_new = mq_in - deg_in_i/pow(wgt_i, 2) - deg_in_j/pow(wgt_j,2)
                        + (deg_in_i + deg_in_j + a_ij)/pow(wgt_i + wgt_j,2);

            mq_out_new = mq_out - dout_i/wgt_i - dout_j/wgt_j + (dout_i + dout_j)/(wgt_i + wgt_j);

            if (nclusters > 2) {
                mq_new =  2*(mq_in_new/(nclusters - 1) - mq_out_new/((nclusters - 1)*(nclusters - 2)));
            } else {
                mq_new =  2*mq_in_new/(nclusters - 1);
            }

#ifdef DEBUG
            {   int ncluster;
                double mq2, mq_in2, mq_out2, *dout2;
                int *matching2, nc2 = nc;
                matching2 = MALLOC(sizeof(int)*A->m);
                matching2 = MEMCPY(matching2, matching, sizeof(real)*A->m);
                if (jc != UNMATCHED) {
                    matching2[i] = jc;
                } else {
                    matching2[i] = nc2;
                    matching2[jj] = nc2;
                    nc2++;
                }
                for (k = 0; k < n; k++) if (matching2[k] == UNMATCHED) matching2[k] =nc2++;
                mq2 = get_mq(A, matching2, &ncluster, &mq_in2, &mq_out2, &dout2);
                fprintf(stderr," {dout_i, dout_j}={%f,%f}, {predicted, calculated}: mq = {%f, %f}, mq_in ={%f,%f}, mq_out = {%f,%f}\n",dout_i, dout_j, mq_new, mq2, mq_in_new, mq_in2, mq_out_new, mq_out2);

                mq_new = mq2;

            }
#endif

            gain = mq_new - mq;
            if (Verbose) fprintf(stderr,"gain in merging node %d with node %d = %f-%f = %f\n", i, jj, mq, mq_new, gain);
            if (j == ia[i] || gain > maxgain) {
                maxgain = gain;
                jmax = jj;
                amax = a_ij;
                dout_max = dout_j;
                wgt_jmax = wgt_j;
                mq_max = mq_new;
                mq_in_max = mq_in_new;
                mq_out_max = mq_out_new;
            }

        }

        /* now merge i and jmax */
        if (maxgain > 0 || (nc >= 1 && nc > maxcluster)) {
            total_gain += maxgain;
            jc = matching[jmax];
            if (jc == UNMATCHED) {
                fprintf(stderr, "maxgain=%f, merge %d, %d\n",maxgain, i, jmax);
                neighbors[nc] = SingleLinkedList_new_int(jmax);
                neighbors[nc] = SingleLinkedList_prepend_int(neighbors[nc], i);
                dout_new[nc] = dout_i + dout_max;
                matching[i] = matching[jmax] = nc;
                wgt_new[nc] = wgt[i] + wgt[jmax];
                deg_intra_new[nc] = deg_intra[i] + deg_intra[jmax] + amax;
                nc++;
            } else {
                fprintf(stderr,"maxgain=%f, merge with existing cluster %d, %d\n",maxgain, i, jc);
                neighbors[jc] = SingleLinkedList_prepend_int(neighbors[jc], i);
                dout_new[jc] = dout_i + dout_max;
                wgt_new[jc] += wgt[i];
                matching[i] = jc;
                deg_intra_new[jc] += deg_intra[i] + amax;
            }
            mq = mq_max;
            mq_in = mq_in_max;
            mq_out = mq_out_max;
            nclusters--;
        } else {
            fprintf(stderr,"gain: %f -- no gain, skip merging node %d\n", maxgain, i);
            assert(maxgain <= 0);
            neighbors[nc] = SingleLinkedList_new_int(i);
            matching[i] = nc;
            deg_intra_new[nc] = deg_intra[i];
            wgt_new[nc] = wgt[i];
            nc++;
        }


        /* update scaled outdegree of neighbors of i and its merged node/cluster jmax */
        jc = matching[i];
        lst = neighbors[jc];
        do {
            mask[*((int*) SingleLinkedList_get_data(lst))] = n+i;
            lst = SingleLinkedList_get_next(lst);
        } while (lst);

        lst = neighbors[jc];

        do {
            k = *((int*) SingleLinkedList_get_data(lst));
            for (j = ia[k]; j < ia[k+1]; j++) {
                jj = ja[j];
                if (mask[jj] == n+i) continue;/* link to within cluster */
                if ((jc = matching[jj]) == UNMATCHED) {
                    if (k == i) {
                        dout[jj] += -a[j]/wgt_i + a[j]/(wgt_i + wgt_jmax);
                    } else {
                        dout[jj] += -a[j]/wgt_jmax + a[j]/(wgt_i + wgt_jmax);
                    }
                } else {
                    if (k == i) {
                        dout_new[jc] += -a[j]/wgt_i + a[j]/(wgt_i + wgt_jmax);
                    } else {
                        dout_new[jc] += -a[j]/wgt_jmax + a[j]/(wgt_i + wgt_jmax);
                    }
                }
            }
            lst = SingleLinkedList_get_next(lst);
        } while (lst);

    }

    fprintf(stderr,"verbose=%d\n",Verbose);
    if (Verbose) fprintf(stderr,"mq = %f new mq = %f level = %d, n = %d, nc = %d, gain = %g, mq_in = %f, mq_out = %f\n", mq, mq + total_gain,
                             level, n, nc, total_gain, mq_in, mq_out);

#ifdef DEBUG
    {   int ncluster;

        mq = get_mq(A, matching, &ncluster, &mq_in, &mq_out, &dout);
        fprintf(stderr," mq = %f\n",mq);

    }
#endif

    if (nc >= 1 && (total_gain > 0 || nc < n)) {
        /* now set up restriction and prolongation operator */
        SparseMatrix P, R, R0, B, cA;
        real one = 1.;
        Multilevel_MQ_Clustering cgrid;

        R0 = SparseMatrix_new(nc, n, 1, MATRIX_TYPE_REAL, FORMAT_COORD);
        for (i = 0; i < n; i++) {
            jj = matching[i];
            SparseMatrix_coordinate_form_add_entries(R0, 1, &jj, &i, &one);
        }
        R = SparseMatrix_from_coordinate_format(R0);
        SparseMatrix_delete(R0);
        P = SparseMatrix_transpose(R);
        B = SparseMatrix_multiply(R, A);
        if (!B) goto RETURN;
        cA = SparseMatrix_multiply(B, P);
        if (!cA) goto RETURN;
        SparseMatrix_delete(B);
        grid->P = P;
        grid->R = R;
        level++;
        cgrid = Multilevel_MQ_Clustering_init(cA, level);
        deg_intra_new = REALLOC(deg_intra_new, nc*sizeof(real));
        wgt_new = REALLOC(wgt_new, nc*sizeof(real));
        cgrid->deg_intra = deg_intra_new;
        cgrid->mq = grid->mq + total_gain;
        cgrid->wgt = wgt_new;
        dout_new =  REALLOC(dout_new, nc*sizeof(real));
        cgrid->dout = dout_new;

        cgrid = Multilevel_MQ_Clustering_establish(cgrid, maxcluster);

        grid->next = cgrid;
        cgrid->prev = grid;
    } else {
        /* no more improvement, stop and final clustering found */
        for (i = 0; i < n; i++) matching[i] = i;

        FREE(deg_intra_new);
        FREE(wgt_new);
        FREE(dout_new);
    }

RETURN:
    for (i = 0; i < n; i++) SingleLinkedList_delete(neighbors[i], free);
    FREE(neighbors);

    FREE(deg_inter);
    FREE(mask);
    return grid;
}