Exemple #1
0
static void Multilevel_coarsen(SparseMatrix A, SparseMatrix *cA, real *node_wgt, real **cnode_wgt,
			       SparseMatrix *P, SparseMatrix *R, Multilevel_control ctrl, int *coarsen_scheme_used){
  int *matching = NULL, nmatch, nc, nzc, n, i;
  int *irn = NULL, *jcn = NULL, *ia = NULL, *ja = NULL;
  real *val = NULL;
  SparseMatrix B = NULL;
  int *vset = NULL, nvset, ncov, j;
  int *cluster, *clusterp, ncluster;

  assert(A->m == A->n);
  *cA = NULL;
  *P = NULL;
  *R = NULL;
  n = A->m;

  *coarsen_scheme_used = ctrl->coarsen_scheme;

  switch (ctrl->coarsen_scheme){
  case COARSEN_HYBRID:
#ifdef DEBUG_PRINT
    if (Verbose)
      fprintf(stderr, "hybrid scheme, try COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_LEAVES_FIRST first\n");
#endif
    *coarsen_scheme_used = ctrl->coarsen_scheme =  COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_LEAVES_FIRST;
    Multilevel_coarsen(A, cA, node_wgt, cnode_wgt, P, R, ctrl, coarsen_scheme_used);

    if (!(*cA)) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "switching to COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_SUPERNODES_FIRST\n");
#endif
      *coarsen_scheme_used = ctrl->coarsen_scheme =  COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_SUPERNODES_FIRST;
      Multilevel_coarsen(A, cA, node_wgt, cnode_wgt, P, R, ctrl, coarsen_scheme_used);
    }

    if (!(*cA)) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "switching to COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_CLUSTER_PERNODE_LEAVES_FIRST\n");
#endif
      *coarsen_scheme_used = ctrl->coarsen_scheme =  COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_CLUSTER_PERNODE_LEAVES_FIRST;
      Multilevel_coarsen(A, cA, node_wgt, cnode_wgt, P, R, ctrl, coarsen_scheme_used);
    }

    if (!(*cA)) {
#ifdef DEBUG_PRINT
     if (Verbose)
        fprintf(stderr, "switching to COARSEN_INDEPENDENT_VERTEX_SET\n");
#endif
      *coarsen_scheme_used = ctrl->coarsen_scheme = COARSEN_INDEPENDENT_VERTEX_SET;
      Multilevel_coarsen(A, cA, node_wgt, cnode_wgt, P, R, ctrl, coarsen_scheme_used);
    }


    if (!(*cA)) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "switching to COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE\n");
#endif
      *coarsen_scheme_used = ctrl->coarsen_scheme = COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE;
      Multilevel_coarsen(A, cA, node_wgt, cnode_wgt, P, R, ctrl, coarsen_scheme_used);
    }
    ctrl->coarsen_scheme = COARSEN_HYBRID;
    break;
  case  COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_SUPERNODES_FIRST:
  case  COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_CLUSTER_PERNODE_LEAVES_FIRST:
  case COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_LEAVES_FIRST:
    if (ctrl->coarsen_scheme == COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_LEAVES_FIRST) {
      maximal_independent_edge_set_heavest_edge_pernode_leaves_first(A, ctrl->randomize, &cluster, &clusterp, &ncluster);
    } else if (ctrl->coarsen_scheme == COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_SUPERNODES_FIRST) {
      maximal_independent_edge_set_heavest_edge_pernode_supernodes_first(A, ctrl->randomize, &cluster, &clusterp, &ncluster);
    } else {
      maximal_independent_edge_set_heavest_cluster_pernode_leaves_first(A, 4, ctrl->randomize, &cluster, &clusterp, &ncluster);
    }
    assert(ncluster <= n);
    nc = ncluster;
    if (nc > ctrl->min_coarsen_factor*n || nc < ctrl->minsize) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "nc = %d, nf = %d, minsz = %d, coarsen_factor = %f coarsening stops\n",nc, n, ctrl->minsize, ctrl->min_coarsen_factor);
#endif
      goto RETURN;
    }
    irn = N_GNEW(n,int);
    jcn = N_GNEW(n,int);
    val = N_GNEW(n,real);
    nzc = 0; 
    for (i = 0; i < ncluster; i++){
      for (j = clusterp[i]; j < clusterp[i+1]; j++){
	assert(clusterp[i+1] > clusterp[i]);
	irn[nzc] = cluster[j];
	jcn[nzc] = i;
	val[nzc++] = 1.;
     }
    }
    assert(nzc == n);
    *P = SparseMatrix_from_coordinate_arrays(nzc, n, nc, irn, jcn, (void *) val, MATRIX_TYPE_REAL);
    *R = SparseMatrix_transpose(*P);
    B = SparseMatrix_multiply(*R, A);
    if (!B) goto RETURN;
    *cA = SparseMatrix_multiply(B, *P); 
    if (!*cA) goto RETURN;
    SparseMatrix_multiply_vector(*R, node_wgt, cnode_wgt, FALSE);
    *R = SparseMatrix_divide_row_by_degree(*R);
    SparseMatrix_set_symmetric(*cA);
    SparseMatrix_set_pattern_symmetric(*cA);
    *cA = SparseMatrix_remove_diagonal(*cA);
    break;
  case COARSEN_INDEPENDENT_EDGE_SET:
    maximal_independent_edge_set(A, ctrl->randomize, &matching, &nmatch);
  case COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE:
    if (ctrl->coarsen_scheme == COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE) 
      maximal_independent_edge_set_heavest_edge_pernode(A, ctrl->randomize, &matching, &nmatch);
  case COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_DEGREE_SCALED:
    if (ctrl->coarsen_scheme == COARSEN_INDEPENDENT_EDGE_SET_HEAVEST_EDGE_PERNODE_DEGREE_SCALED) 
      maximal_independent_edge_set_heavest_edge_pernode_scaled(A, ctrl->randomize, &matching, &nmatch);
    nc = nmatch;
    if (nc > ctrl->min_coarsen_factor*n || nc < ctrl->minsize) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "nc = %d, nf = %d, minsz = %d, coarsen_factor = %f coarsening stops\n",nc, n, ctrl->minsize, ctrl->min_coarsen_factor);
#endif
      goto RETURN;
    }
    irn = N_GNEW(n,int);
    jcn = N_GNEW(n,int);
    val = N_GNEW(n,real);
    nzc = 0; nc = 0;
    for (i = 0; i < n; i++){
      if (matching[i] >= 0){
	if (matching[i] == i){
	  irn[nzc] = i;
	  jcn[nzc] = nc;
	  val[nzc++] = 1.;
	} else {
	  irn[nzc] = i;
	  jcn[nzc] = nc;
	  val[nzc++] = 1;
	  irn[nzc] = matching[i];
	  jcn[nzc] = nc;
	  val[nzc++] = 1;
	  matching[matching[i]] = -1;
	}
	nc++;
	matching[i] = -1;
      }
    }
    assert(nc == nmatch);
    assert(nzc == n);
    *P = SparseMatrix_from_coordinate_arrays(nzc, n, nc, irn, jcn, (void *) val, MATRIX_TYPE_REAL);
    *R = SparseMatrix_transpose(*P);
    B = SparseMatrix_multiply(*R, A);
    if (!B) goto RETURN;
    *cA = SparseMatrix_multiply(B, *P); 
    if (!*cA) goto RETURN;
    SparseMatrix_multiply_vector(*R, node_wgt, cnode_wgt, FALSE);
    *R = SparseMatrix_divide_row_by_degree(*R);
    SparseMatrix_set_symmetric(*cA);
    SparseMatrix_set_pattern_symmetric(*cA);
    *cA = SparseMatrix_remove_diagonal(*cA);
    break;
  case COARSEN_INDEPENDENT_VERTEX_SET:
  case COARSEN_INDEPENDENT_VERTEX_SET_RS:
    if (ctrl->coarsen_scheme == COARSEN_INDEPENDENT_VERTEX_SET){
      maximal_independent_vertex_set(A, ctrl->randomize, &vset, &nvset, &nzc);
    } else {
      maximal_independent_vertex_set_RS(A, ctrl->randomize, &vset, &nvset, &nzc);
    }
    ia = A->ia;
    ja = A->ja;
    nc = nvset;
    if (nc > ctrl->min_coarsen_factor*n || nc < ctrl->minsize) {
#ifdef DEBUG_PRINT
      if (Verbose)
        fprintf(stderr, "nc = %d, nf = %d, minsz = %d, coarsen_factor = %f coarsening stops\n",nc, n, ctrl->minsize, ctrl->min_coarsen_factor);
#endif
      goto RETURN;
    }
    irn = N_GNEW(nzc,int);
    jcn = N_GNEW(nzc,int);
    val = N_GNEW(nzc,real);
    nzc = 0; 
    for (i = 0; i < n; i++){
      if (vset[i] == MAX_IND_VTX_SET_F){
	ncov = 0;
	for (j = ia[i]; j < ia[i+1]; j++){
	  if (vset[ja[j]] >= MAX_IND_VTX_SET_C){
	    ncov++;
	  }
	}
	assert(ncov > 0);
	for (j = ia[i]; j < ia[i+1]; j++){
	  if (vset[ja[j]] >= MAX_IND_VTX_SET_C){
	    irn[nzc] = i;
	    jcn[nzc] = vset[ja[j]];
	    val[nzc++] = 1./(double) ncov;
	  }
	}
      } else {
	assert(vset[i] >= MAX_IND_VTX_SET_C);
	irn[nzc] = i;
	jcn[nzc] = vset[i];
	val[nzc++] = 1.;
      }
    }

    *P = SparseMatrix_from_coordinate_arrays(nzc, n, nc, irn, jcn, (void *) val, MATRIX_TYPE_REAL);
    *R = SparseMatrix_transpose(*P);
    B = SparseMatrix_multiply(*R, A);
    if (!B) goto RETURN;
    *cA = SparseMatrix_multiply(B, *P); 
    if (!*cA) goto RETURN;
    SparseMatrix_multiply_vector(*R, node_wgt, cnode_wgt, FALSE);
    SparseMatrix_set_symmetric(*cA);
    SparseMatrix_set_pattern_symmetric(*cA);
    *cA = SparseMatrix_remove_diagonal(*cA);
    break;
  default:
    goto RETURN;
  }
 RETURN:
  if (matching) FREE(matching);
  if (vset) FREE(vset);
  if (irn) FREE(irn);
  if (jcn) FREE(jcn);
  if (val) FREE(val);
  if (B) SparseMatrix_delete(B);
}
Exemple #2
0
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;
}