Esempio n. 1
0
/*************************************************************************
* This function takes a graph and produces a bisection of it
**************************************************************************/
int MlevelKWayPartitioning(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, float *tpwgts, float ubfactor)
{
  int i, j, nvtxs, tvwgt, tpwgts2[2];
  GraphType *cgraph;
  int wgtflag=3, numflag=0, options[10], edgecut;

  cgraph = Coarsen2Way(ctrl, graph);

  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
  AllocateKWayPartitionMemory(ctrl, cgraph, nparts);

  options[0] = 1; 
  options[OPTION_CTYPE] = MATCH_SHEMKWAY;
  options[OPTION_ITYPE] = IPART_GGPKL;
  options[OPTION_RTYPE] = RTYPE_FM;
  options[OPTION_DBGLVL] = 0;

  METIS_WPartGraphRecursive(&cgraph->nvtxs, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, 
                            cgraph->adjwgt, &wgtflag, &numflag, &nparts, tpwgts, options, 
                            &edgecut, cgraph->where);

  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial %d-way partitioning cut: %d\n", nparts, edgecut));

  IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where));

  RefineKWay(ctrl, graph, cgraph, nparts, tpwgts, ubfactor);

  idxcopy(graph->nvtxs, graph->where, part);

  GKfree(&graph->gdata, &graph->rdata, LTERM);

  return graph->mincut;

}
Esempio n. 2
0
/*************************************************************************
* This function performs multilevel bisection
**************************************************************************/
void MlevelNodeBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
{
  GraphType *cgraph;

  ctrl->CoarsenTo = graph->nvtxs/8;
  if (ctrl->CoarsenTo > 100)
    ctrl->CoarsenTo = 100;
  else if (ctrl->CoarsenTo < 40)
    ctrl->CoarsenTo = 40;
  ctrl->maxvwgt = 1.5*((tpwgts[0]+tpwgts[1])/ctrl->CoarsenTo);

  cgraph = Coarsen2Way(ctrl, graph);

  switch (ctrl->IType) {
    case IPART_GGPKL:
      Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor);

      IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SepTmr));

      Compute2WayPartitionParams(ctrl, cgraph);
      ConstructSeparator(ctrl, cgraph, ubfactor);

      IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SepTmr));
      break;
    case IPART_GGPKLNODE:
      InitSeparator(ctrl, cgraph, ubfactor);
      break;
  }

  Refine2WayNode(ctrl, graph, cgraph, ubfactor);

}
Esempio n. 3
0
void testCoarsening(int *nvtxs, idxtype *xadj, idxtype *adjncy,
                    idxtype *vwgt, idxtype *adjwgt,
                    int *wgtflag, int ct)
{
    GraphType graph, *cgraph;
    CtrlType ctrl;

    my_SetUpGraph(&graph, *nvtxs, xadj, adjncy, vwgt, adjwgt,
                  *wgtflag, 1);
    /* The last argument indicates we are setting up the original
     * graph */

    ctrl.CoarsenTo=ct;
    ctrl.CType=MATCH_SHEMN;

    my_AllocateWorkSpace(&ctrl,&graph);

    cgraph = Coarsen2Way(&ctrl,&graph);

    do {
        dump_graph(cgraph);
        cgraph = cgraph->finer;
    } while ( cgraph != NULL );

}
Esempio n. 4
0
/*************************************************************************
* This function performs multilevel bisection
**************************************************************************/
void MlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, floattype ubfactor)
{
  GraphType *cgraph;

  cgraph = Coarsen2Way(ctrl, graph);

  Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor);

  Refine2Way(ctrl, graph, cgraph, tpwgts, ubfactor);

/*
  IsConnectedSubdomain(ctrl, graph, 0);
  IsConnectedSubdomain(ctrl, graph, 1);
*/
}
Esempio n. 5
0
/*************************************************************************
* This function performs multilevel bisection. It performs multiple 
* bisections and selects the best.
**************************************************************************/
void MlevelNodeBisectionMultiple(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
{
  int i, nvtxs, cnvtxs, mincut, tmp;
  GraphType *cgraph; 
  idxtype *bestwhere;

  if (ctrl->nseps == 1 || graph->nvtxs < (ctrl->oflags&OFLAG_COMPRESS ? 1000 : 2000)) {
    MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor);
    return;
  }

  nvtxs = graph->nvtxs;

  if (ctrl->oflags&OFLAG_COMPRESS) { /* Multiple separators at the original graph */
    bestwhere = idxmalloc(nvtxs, "MlevelNodeBisection2: bestwhere");
    mincut = nvtxs;

    for (i=ctrl->nseps; i>0; i--) {
      MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor);

      /* printf("%5d ", cgraph->mincut); */

      if (graph->mincut < mincut) {
        mincut = graph->mincut;
        idxcopy(nvtxs, graph->where, bestwhere);
      }

      GKfree(&graph->rdata, LTERM);
    
      if (mincut == 0)
        break;
    }
    /* printf("[%5d]\n", mincut); */

    Allocate2WayNodePartitionMemory(ctrl, graph);
    idxcopy(nvtxs, bestwhere, graph->where);
    free(bestwhere);

    Compute2WayNodePartitionParams(ctrl, graph);
  }
  else {  /* Coarsen it a bit */
    ctrl->CoarsenTo = nvtxs-1;

    cgraph = Coarsen2Way(ctrl, graph);

    cnvtxs = cgraph->nvtxs;

    bestwhere = idxmalloc(cnvtxs, "MlevelNodeBisection2: bestwhere");
    mincut = nvtxs;

    for (i=ctrl->nseps; i>0; i--) {
      ctrl->CType += 20; /* This is a hack. Look at coarsen.c */
      MlevelNodeBisection(ctrl, cgraph, tpwgts, ubfactor);

      /* printf("%5d ", cgraph->mincut); */

      if (cgraph->mincut < mincut) {
        mincut = cgraph->mincut;
        idxcopy(cnvtxs, cgraph->where, bestwhere);
      }

      GKfree(&cgraph->rdata, LTERM);
    
      if (mincut == 0)
        break;
    }
    /* printf("[%5d]\n", mincut); */

    Allocate2WayNodePartitionMemory(ctrl, cgraph);
    idxcopy(cnvtxs, bestwhere, cgraph->where);
    free(bestwhere);

    Compute2WayNodePartitionParams(ctrl, cgraph);

    Refine2WayNode(ctrl, graph, cgraph, ubfactor);
  }

}
Esempio n. 6
0
std::vector< std::set<idxtype> > srmclWithGraph(GraphType *graph,std::vector<std::set<idxtype> >& indices,Options opt )
{
	GraphType *cgraph,*t,*cgraphFinest, *cgraphCoarest;
	int nvtxs=graph->nvtxs;
	CtrlType ctrl;
	int levels=0;
	Matrix *M=NULL;

	int ct = ctrl.CoarsenTo = opt.coarsenTo, runMcl=0;
	ctrl.CType=opt.matchType;

	ctrl.optype=OP_KMETIS;
	ctrl.dbglvl=1;

	if ( ct < 5 )
		ctrl.maxvwgt = (int) ceil( (1.5*nvtxs)/ct );
	else if ( ct <= 100 )
		ctrl.maxvwgt = (int) ceil( (2.0*nvtxs)/ct );
	else if ( ct > 100 )
	{
		// we can allow some imbalance here.
		ctrl.maxvwgt = (int) ceil( 10.0 * nvtxs/ct );
	}

	my_AllocateWorkSpace(&ctrl,graph);

	cgraphCoarest = cgraph = Coarsen2Way(&ctrl,graph);
	

	FreeWorkSpace(&ctrl, graph);


	for(levels=0,t=cgraph ; t->finer!=NULL ; t=t->finer,levels++);

	int num_level = levels;
	printf("Coarsened %d levels\n", levels);
	printf("Number of vertices in coarsest graph:%d\n", cgraph->nvtxs);
	fflush(stdout);


	int times_rmcl= 1;	

	while ( cgraph != NULL )
	{
		Matrix *M0, *Mnew;
		int iters;

		M0=setupCanonicalMatrix(cgraph->nvtxs, cgraph->nedges, cgraph->xadj, cgraph->adjncy, cgraph->adjwgt, opt.ncutify);  //do weight normalization here
#ifdef TEST_OUTPUT
		//printf("level: %d, M0:\n",levels);
		//printMatrix(M0);
#endif


		if ( cgraph->coarser == NULL )
		{
		// if this is the coarsest graph, flow matrix is same as
		// the transition matrix. 
			M=M0;
	//		dumpMatrix(M);
		}

		if (cgraph->finer == NULL){
			iters=opt.num_last_iter;
			cgraphFinest = cgraph;
		}
		else{
			iters=opt.iter_per_level;
		}

		//printMatrix(M);
		Mnew=dprmcl(M,M0,cgraph, opt, iters, levels);        //YK: main process!
		M=Mnew;

		if ( cgraph->finer != NULL )  //YK: map nodes to the finer graph
		{
			int nnzRefinedGraph = 2*M->nnz;
			if ( ctrl.CType == MATCH_POWERLAW_FC )
			{
				int ii;
				nnzRefinedGraph = 0;
				for ( ii=0; ii<cgraph->finer->nvtxs; ii++ )
				{
					int tx=cgraph->finer->cmap[ii];
					nnzRefinedGraph +=
					M->xadj[tx+1]-M->xadj[tx];
				}
			}
			else if ( ctrl.CType == MATCH_HASH  && levels <= 3 )
			{
				int ii;
				nnzRefinedGraph=0;
				for ( ii=0; ii<cgraph->nvtxs; ii++ )
				{
					nnzRefinedGraph += cgraph->vwgt[ii] *
							(M->xadj[ii+1] - M->xadj[ii]);
				}
			}
//			dumpMatrix(M);
			Mnew=propagateFlow(M,cgraph,cgraph->finer,nnzRefinedGraph);
			//printf("times:%d, levels:%d Mnew\n",times_rmcl, levels);
			//printMatrix(Mnew);
			if ( M != NULL )
			{
				freeMatrix(M);
			}
			M=Mnew;
		}
		cgraph=cgraph->finer;  //change to the finer graph


		levels--;
		printf("Done level %d (%d times MLR-MCL)\n", levels+1,times_rmcl);
		fflush(stdout);
	}
	fflush(stdout);
	idxtype* firstIndices =  (idxtype*) malloc(sizeof(idxtype) * M->nvtxs);
	idxcopy(nvtxs, M->attractors, firstIndices);


	bool* isAttractor = (bool*) malloc(sizeof(bool) * M->nvtxs);  
	getAttractorsForAll(M, isAttractor);
	int** countAttractor = (int**) malloc(sizeof(int*) * (num_level+1));  //count the number of times the node being an attractor node
	for(int i=0; i <= num_level ; i++){
		countAttractor[i] = (int*) malloc (sizeof(int) * nvtxs);
		for(int j=0; j<M->nvtxs; j++){
			countAttractor[i][j] = 0;
		}
	}
#ifdef TEST_OUTPUT
	{
		for(int i=0; i<M->nvtxs; i++){
			int level = 1, nodeIdx = i;
			for(cgraph = cgraphFinest; cgraph->coarser!=NULL; cgraph = cgraph->coarser, level++){
				idxtype oldNodeIdx = nodeIdx;
				nodeIdx = cgraph->cmap[oldNodeIdx];
				printf("  cmap: level:%d  vid: %d to %d \n", level, oldNodeIdx+1, nodeIdx+1);
			}
		}
	}
#endif
	for(int i=0; i<M->nvtxs; i++){
		
		if(isAttractor[i]){
			countAttractor[0][i]++;
#ifdef TEST_OUTPUT
			printf("attractor vid:%d(%d times), %d-th times MLRMCL\n",i+1,countAttractor[0][i],times_rmcl);fflush(stdout);
#endif
			int level = 1, nodeIdx = i;
			for(cgraph = cgraphFinest; cgraph->coarser!=NULL; cgraph = cgraph->coarser, level++){
				idxtype oldNodeIdx = nodeIdx;
				nodeIdx = cgraph->cmap[nodeIdx];

				countAttractor[level][nodeIdx]++;
#ifdef TEST_OUTPUT
				//printf("countAttractor[%d][%d] = %d;\n", level, nodeIdx,countAttractor[level][nodeIdx]);
#endif
			}
			
		}
	}
	
	//construct hash table
	idxtype* hashtable = idxmalloc(nvtxs,"Hashtable");
	for(int i=0;i<nvtxs;i++)
		hashtable[i]=-1;  	// clear hashtable.
	int num_clusters = 0;  //include singleton
	for(int i=0;i<nvtxs;i++){
		if ( hashtable[firstIndices[i]] == -1){
			hashtable[firstIndices[i]] = num_clusters;
			num_clusters++;
		}
	}
	/*idxtype* invHashTable = idxmalloc(num_clusters,"inverse hashtable");  //map cluster id back to the attractor id
	for(int i=0;i<nvtxs;i++){
		if ( hashtable[firstIndices[i]] != -1){
			invHashTable[hashtable[firstIndices[i]]] = firstIndices[i];
		}
	}*/
	//construct clusters
	std::vector< std::set<idxtype> > clusters(0);   //clusters[i] contains nodes' numbers (in original graph) in cluster i
	for(int i=0; i<num_clusters ; i++){
		std::set<idxtype> newCluster;
		clusters.push_back(newCluster);
	}
	for(int i=0; i<nvtxs ; i++){
		if( firstIndices[i] != -1){
			idxtype cID = hashtable[firstIndices[i]];
			indices[i].insert(cID); 
			if(cID == -1)
				continue;
			clusters[ cID  ].insert( i );
		}
	}
	idxtype* lastIndices = firstIndices;
	bool* lastAttractor = isAttractor;
	idxtype* oldHashtable = hashtable;

	printf("number of clusters in 1st time MLR-MCL: %d\n", num_clusters);fflush(stdout);

	idxtype* thisIndices =  (idxtype*) malloc(sizeof(idxtype) * M->nvtxs);
	idxtype* newHashtable = idxmalloc(nvtxs,"newHashtable");
	bool* thisAttractor = (bool*) malloc(sizeof(bool) * M->nvtxs);  
	while(times_rmcl < opt.time_rmcl){  //do more than one times R-MCL but penalize attractor nodes (by assigning them higher inflation rate)

		times_rmcl++;
		cgraph = cgraphCoarest;
		for(levels=0,t=cgraph ; t->finer!=NULL ; t=t->finer,levels++);

		while ( cgraph != NULL )
		{
			Matrix *M0, *Mnew;
			int iters;

			M0=setupCanonicalMatrix(cgraph->nvtxs, cgraph->nedges, cgraph->xadj, cgraph->adjncy, cgraph->adjwgt, opt.ncutify);  //do weight normalization here

			if ( cgraph->coarser == NULL )
			{
				M=M0;
			}
			

			if (cgraph->finer == NULL)
				iters=opt.num_last_iter;
			else
				iters=opt.iter_per_level;

			//Mnew=dprmcl(M,M0,cgraph, opt, iters,levels);  //original
			Mnew=dprmcl_penalizeAttractors(M,M0,cgraph, opt, iters, levels, countAttractor);        //YK: main process!
			
			M=Mnew;

			if ( cgraph->finer != NULL )  //YK: map nodes to the finer graph
			{
				int nnzRefinedGraph = 2*M->nnz;
				if ( ctrl.CType == MATCH_POWERLAW_FC )
				{
					int ii;
					nnzRefinedGraph = 0;
					for ( ii=0; ii<cgraph->finer->nvtxs; ii++ )
					{
						int tx=cgraph->finer->cmap[ii];
						nnzRefinedGraph +=
						M->xadj[tx+1] - M->xadj[tx];
					}
				}
				else if ( ctrl.CType == MATCH_HASH  && levels <= 3 )
				{
					int ii;
					nnzRefinedGraph=0;
					for ( ii=0; ii<cgraph->nvtxs; ii++ )
					{
						nnzRefinedGraph += cgraph->vwgt[ii] *
								(M->xadj[ii+1] - M->xadj[ii]);
					}
				}
				Mnew=propagateFlow(M,cgraph,cgraph->finer,nnzRefinedGraph);
				//printf("times:%d, levels:%d Mnew\n",times_rmcl, levels);
				//printMatrix(Mnew);
				if ( M != NULL )
				{
					freeMatrix(M);
				}
				M=Mnew;
			}
			cgraph=cgraph->finer;  //change to the finer graph

			// These two didn't get freed earlier, when we freed
			// gdata.
			

			levels--;
			printf("Done level %d (%d-th times MLR-MCL)\n", levels+1,times_rmcl);fflush(stdout);

		}
		//update clusters
		
		idxcopy(nvtxs, M->attractors, thisIndices);

		
		getAttractorsForAll(M, thisAttractor);
		for(int i=0; i<M->nvtxs; i++){
			if(thisAttractor[i]){
				countAttractor[0][i]++;
	#ifdef TEST_OUTPUT
					printf("attractor vid:%d(%d times), %d-th times MLRMCL\n",i+1,countAttractor[0][i],times_rmcl);
	#endif
				int level = 1, nodeIdx = i;
				for(cgraph = cgraphFinest; cgraph->coarser!=NULL; cgraph = cgraph->coarser, level++){
					nodeIdx = cgraph->cmap[nodeIdx];
					countAttractor[level][nodeIdx]++;
				}
				//printf("attractor:%d, times:%d\n",i+1,times_rmcl);
			}
		}
	
		//construct hash table

		for(int i=0;i<nvtxs;i++)
			newHashtable[i]=-1;  	// clear newHashtable.

		int new_num_clusters = 0 ;
		for(int i=0;i<nvtxs;i++){
			if ( newHashtable[thisIndices[i]] == -1){
				newHashtable[thisIndices[i]] = num_clusters+new_num_clusters;  //cluster ID
				if(num_clusters+new_num_clusters == 22704 || num_clusters+new_num_clusters == 29925)
					printf("cluster #%d's attractor node %d\n",num_clusters+new_num_clusters,thisIndices[i]);
				new_num_clusters++;
			}
		}
		//construct clusters
		for(int i=0; i<new_num_clusters;  i++){
			std::set<idxtype>* newCluster = new std::set<idxtype>();
			clusters.push_back(*newCluster);
		}
		for(int i=0; i<nvtxs ; i++){
			if( thisIndices[i] != -1){
				idxtype cID = newHashtable[thisIndices[i]];
				indices[i].insert(cID); 
				if(cID == -1)
					continue;
				clusters[ cID ].insert( i );
			}
		}
		printf("number of clusters in %dth times MLR-MCL: %d\ttotal # clusters:%zu\n", times_rmcl, new_num_clusters,clusters.size());
		num_clusters += new_num_clusters;

		//detect last attractor is split to multiple clusters
		/*
		double* countToCluster = (double*) malloc(sizeof(double) * num_clusters) ; 
		for(idxtype vID=0; vID<nvtxs ; vID++){
			
			//printf("test:%d\n",vID+1);fflush(stdout);
			if(lastAttractor[vID] && !thisAttractor[vID]){
				for(int i=0; i<num_clusters; i++)
					countToCluster[i] = 0;
				int lastCluster = oldHashtable[lastIndices[vID]];
				//printf("lastCluster:cid%d (attractor:%d size:%zu), num clusters:%d\n",lastCluster,vID+1,clusters[lastCluster].size(),num_clusters);fflush(stdout);
				
				for(std::set<idxtype>::iterator nodeIterator = clusters[lastCluster].begin(); nodeIterator != clusters[lastCluster].end(); nodeIterator++){
					
					int currentCluster = newHashtable[thisIndices[*nodeIterator]];
					countToCluster[currentCluster] ++;
				}
				for(idxtype cID = 0; cID < num_clusters; cID++){
					if(countToCluster[cID] / clusters[lastCluster].size() >= opt.ratio_nodes_another_cluster && clusters[cID].find(vID)==clusters[cID].end()  ){
						//clusters[ cID ].insert(vID);
						//indices[ vID ].insert(cID);
#ifdef TEST_OUTPUT
						printf("^add extra cluster: vid:%d to cid:%d\n",vID+1, cID);fflush(stdout);
#endif
					}
				}
				
				
			}
		}	
		free(countToCluster);*/
		

		lastIndices = thisIndices;
		lastAttractor = thisAttractor;
		oldHashtable = newHashtable;
		
	}
	printf("total number of clusters after %d times MLR-MCL:%zu\n", opt.time_rmcl, clusters.size());fflush(stdout);

	free(hashtable);
	for(int i=0; i <= num_level ; i++)
		free(countAttractor[i]);
	free(countAttractor);
	free(isAttractor);
	free(firstIndices);
	free(thisIndices);
	free(newHashtable);
	free(thisAttractor);
	freeMatrix(M);
	return clusters;
}
void mlmclWithGraph(GraphType *graph,idxtype* indices,Options opt)
{
	GraphType *cgraph,*t;
	int nvtxs=graph->nvtxs;
	CtrlType ctrl;
	int levels=0;
	Matrix *M=NULL;

	int ct = ctrl.CoarsenTo = opt.coarsenTo, runMcl=0;
	ctrl.CType=opt.matchType;
/*	if ( nvtxs > 50000 )
		ctrl.CType = MATCH_POWERLAW_FC; */
	ctrl.optype=OP_KMETIS;
//	ctrl.maxvwgt = (int) round( ((ct>100)?nvtxs/100: nvtxs/ct));
//	ctrl.maxvwgt = (ctrl.maxvwgt > 5000 ) ? 5000 : ctrl.maxvwgt ;
	ctrl.dbglvl=1;

	if ( ct < 5 )
		ctrl.maxvwgt = (int) ceil( (1.5*nvtxs)/ct );
	else if ( ct <= 100 )
		ctrl.maxvwgt = (int) ceil( (2.0*nvtxs)/ct );
	else if ( ct > 100 )
	{
		// we can allow some imbalance here.
		ctrl.maxvwgt = (int) ceil( 10.0 * nvtxs/ct );
	}

	my_AllocateWorkSpace(&ctrl,graph);

	cgraph = Coarsen2Way(&ctrl,graph);	

	FreeWorkSpace(&ctrl, graph);

//	printf( "Coarsen time:%.2f\n",gettimer(ctrl.CoarsenTmr) );
	for(levels=0,t=cgraph;t->finer!=NULL;t=t->finer,levels++);

	printf("Coarsened %d levels\n", levels);
	printf("Number of vertices in coarsest graph:%d\n", cgraph->nvtxs);
//	printf("In lib/mlmcl.c: penalty_power:%.1f\n", opt.penalty_power);
	fflush(stdout);

	while ( cgraph != NULL )
	{
		Matrix *M0, *Mnew;
		int iters;
//		dump_graph(cgraph);
		M0=setupCanonicalMatrix(cgraph->nvtxs, cgraph->nedges,
			cgraph->xadj, cgraph->adjncy, cgraph->adjwgt, opt.ncutify);

		if ( cgraph->coarser != NULL )
		{
			// if this is not the original graph, then we don't
			// need the adjacency information in the graph
			// anymore. M0 is what we want, and we have it, so we
			// can free cgraph->gdata.
			GKfree( (void**)&(cgraph->gdata), LTERM);
		}

		if ( cgraph->coarser == NULL )
		{
		// if this is the coarsest graph, flow matrix is same as
		// the transition matrix. 
			M=M0;
	//		dumpMatrix(M);
		}

		if (cgraph->finer == NULL)
			iters=opt.num_last_iter;
		else
			iters=opt.iter_per_level;

		if ( cgraph->coarser == NULL )
		{;
			//printRow(M,35);
			//printRow(M,36);
		}

		Mnew=dprmcl(M,M0,cgraph, opt, iters, levels);
		// M would have been freed in mclForMultiLevel, so no
		// need to free it here.
	/*	if ( M0 != NULL )
		{
			freeMatrix(M0);
		} */

		if ( cgraph->coarser == NULL )
		{
			//printRow(Mnew,35);
			//printRow(Mnew,36);
		}

		if ( runMcl > 0 && cgraph->finer == NULL )
		{
			/* run original mcl so that the matrix moves closer to
			 * convergence */
			M=dprmcl(Mnew,Mnew,cgraph, opt, iters/2,levels);
			/* Mnew would not have been freed in mclForMultiLevel
			 * above. */
			freeMatrix(Mnew);
		}
		else
		{
			M=Mnew;
		}

		if ( cgraph->finer != NULL )
		{
			int nnzRefinedGraph = 2*M->nnz;
			if ( ctrl.CType == MATCH_POWERLAW_FC )
			{
				int ii;
				nnzRefinedGraph = 0;
				for ( ii=0; ii<cgraph->finer->nvtxs; ii++ )
				{
					int tx=cgraph->finer->cmap[ii];
					nnzRefinedGraph +=
					M->xadj[tx+1]-M->xadj[tx];
				}
			}
			else if ( ctrl.CType == MATCH_HASH  && levels <= 3 )
			{
				int ii;
				nnzRefinedGraph=0;
				for ( ii=0; ii<cgraph->nvtxs; ii++ )
				{
					nnzRefinedGraph += cgraph->vwgt[ii] *
							(M->xadj[ii+1] - M->xadj[ii]);
				}
			}
//			dumpMatrix(M);
			Mnew=propagateFlow(M,cgraph,cgraph->finer,nnzRefinedGraph);
//			dumpMatrix(Mnew);
			if ( M != NULL )
			{
				freeMatrix(M);
			}
			M=Mnew;
		}
		cgraph=cgraph->finer;

		// These two didn't get freed earlier, when we freed
		// gdata.
		if ( cgraph != NULL )
		{
			GKfree( (void**)&(cgraph->coarser->vwgt),
					(void**)&(cgraph->coarser->rmap1), LTERM);
		}

		levels--;
		printf("Done level %d\n", levels+1);
		fflush(stdout);
	}

	getAttractorsForAll(M);
	idxcopy(nvtxs, M->attractors, indices);

/*	int nClusters = 0;
	wgttype minWgt = 0;
	idxtype *ret = getNodesToComponentMap(M, &nClusters, minWgt);
	idxcopy(nvtxs, ret, indices);
	free(ret);
*/
	freeMatrix(M);
}