int Zoltan_Set_Key_Param( ZZ *zz, /* Zoltan structure */ const char *name, /* name of variable */ const char *val, /* value of variable */ int idx /* index of vector param, -1 if scalar */ ) { char *yo = "Zoltan_Set_Key_Param"; char msg[256]; int status; /* return code */ PARAM_UTYPE result; /* value returned from Check_Param */ int index; /* index returned from Check_Param */ int tmp; int export, import; status = Zoltan_Check_Param(name, val, Key_params, &result, &index); if (status == 0) { switch (index) { case 0: /* Imbalance_Tol */ if (result.def) result.fval = ZOLTAN_LB_IMBALANCE_TOL_DEF; if (result.fval < 1.0) { sprintf(msg, "Invalid Imbalance_Tol value (%g) " "being set to %g.", result.fval, ZOLTAN_LB_IMBALANCE_TOL_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.fval = ZOLTAN_LB_IMBALANCE_TOL_DEF; } if (idx > zz->Obj_Weight_Dim){ sprintf(msg, "Imbalance_Tol index %d > Obj_Weight_Dim = %d\n", idx, zz->Obj_Weight_Dim); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); } else if (idx < -1){ sprintf(msg, "Invalid Imbalance_Tol index %d\n", idx); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); } else if (idx == -1){ /* Set all entries to the same value. */ for (idx=0; idx<zz->LB.Imb_Tol_Len; idx++) zz->LB.Imbalance_Tol[idx] = result.fval; } else zz->LB.Imbalance_Tol[idx] = result.fval; status = 3; /* Don't add to Params field of ZZ */ break; case 1: /* Help_Migrate */ if (result.def) result.ival = ZOLTAN_AUTO_MIGRATE_DEF; zz->Migrate.Auto_Migrate = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 2: /* Object weight dim. */ if (result.def) result.ival = ZOLTAN_OBJ_WEIGHT_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Obj_Weight_Dim value (%d) " "being set to %d.", result.ival, ZOLTAN_OBJ_WEIGHT_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_OBJ_WEIGHT_DEF; } zz->Obj_Weight_Dim = result.ival; if (zz->Obj_Weight_Dim > zz->LB.Imb_Tol_Len){ /* Resize and reallocate Imb_Tol. */ zz->LB.Imb_Tol_Len += 10; zz->LB.Imbalance_Tol = (float *) ZOLTAN_REALLOC(zz->LB.Imbalance_Tol, zz->LB.Imb_Tol_Len * sizeof(float)); } status = 3; /* Don't add to Params field of ZZ */ break; case 3: /* Edge weight dim. */ case 13: if (result.def) result.ival = ZOLTAN_EDGE_WEIGHT_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Edge_Weight_Dim value (%d) " "being set to %d.", result.ival, ZOLTAN_EDGE_WEIGHT_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_EDGE_WEIGHT_DEF; } zz->Edge_Weight_Dim = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 4: /* Debug level */ if (result.def) result.ival = ZOLTAN_DEBUG_LEVEL_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Debug_Level value (%d) " "being set to %d.", result.ival, ZOLTAN_DEBUG_LEVEL_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_DEBUG_LEVEL_DEF; } zz->Debug_Level = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 5: /* Debug processor */ if (result.def) result.ival = ZOLTAN_DEBUG_PROC_DEF; if (result.ival < 0 || result.ival > zz->Num_Proc) { sprintf(msg, "Invalid Debug_Processor value (%d) " "being set to %d.", result.ival, ZOLTAN_DEBUG_PROC_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_DEBUG_PROC_DEF; } zz->Debug_Proc = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 6: /* Deterministic flag */ if (result.def) result.ival = ZOLTAN_DETERMINISTIC_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Deterministic value (%d) " "being set to %d.", result.ival, ZOLTAN_DETERMINISTIC_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_DETERMINISTIC_DEF; } zz->Deterministic = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 7: /* Timer */ status = Zoltan_Set_Timer_Param(name, val, &tmp); zz->Timer = tmp; Zoltan_Timer_ChangeFlag(zz->ZTime, zz->Timer); if (status==0) status = 3; /* Don't add to Params field of ZZ */ break; case 8: /* Num_GID_Entries */ if (result.def) result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF; if (result.ival < 1) { sprintf(msg, "Invalid Num_GID_Entries value (%d); " "being set to %d.", result.ival, ZOLTAN_NUM_ID_ENTRIES_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF; } zz->Num_GID = result.ival; status = 3; break; case 9: /* Num_LID_Entries */ if (result.def) result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Num_LID_Entries value (%d); " "being set to %d.", result.ival, ZOLTAN_NUM_ID_ENTRIES_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF; } zz->Num_LID = result.ival; status = 3; break; case 10: /* LB.Return_Lists */ export = (strstr(result.sval, "EXPORT") != NULL); import = (strstr(result.sval, "IMPORT") != NULL); if ((export && import) || (strcmp(result.sval, "ALL") == 0)) { tmp = ZOLTAN_LB_ALL_LISTS; /* export AND import lists */ status = 3; } else if (import){ tmp = ZOLTAN_LB_IMPORT_LISTS; /* import lists */ status = 3; } else if (export){ tmp = ZOLTAN_LB_EXPORT_LISTS; /* export lists */ status = 3; } else if (strstr(result.sval, "PART")!=NULL) { /* list of every object's part assignment */ tmp = ZOLTAN_LB_COMPLETE_EXPORT_LISTS; status = 3; } else if (strcmp(result.sval, "NONE")==0) { tmp = ZOLTAN_LB_NO_LISTS; /* no lists */ status = 3; } else if (strcmp(result.sval, "CANDIDATE_LISTS")==0) { tmp = ZOLTAN_LB_CANDIDATE_LISTS; /* candidates needed in matching */ status = 3; } else{ tmp = ZOLTAN_LB_RETURN_LISTS_DEF; sprintf(msg, "Unknown return_lists option %s.", result.sval); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); status = 2; /* Illegal parameter */ } zz->LB.Return_Lists = tmp; break; case 11: /* LB_Method */ if (result.def) strcpy(result.sval, "RCB"); status = Zoltan_LB_Set_LB_Method(zz,result.sval); if (status == ZOLTAN_OK) status = 3; break; case 12: /* Tflops Special flag */ if (result.def) result.ival = ZOLTAN_TFLOPS_SPECIAL_DEF; if (result.ival < 0) { sprintf(msg, "Invalid Tflops Special value (%d) " "being set to %d.", result.ival, ZOLTAN_TFLOPS_SPECIAL_DEF); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = ZOLTAN_TFLOPS_SPECIAL_DEF; } zz->Tflops_Special = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 14: /* Num_Global_Parts */ case 15: if (result.def) result.ival = zz->Num_Proc; if (result.ival < 1) { sprintf(msg, "Invalid Num_Global_Parts value (%d); " "being set to %d.", result.ival,zz->Num_Proc); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = zz->Num_Proc; } zz->LB.Num_Global_Parts_Param = result.ival; status = 3; break; case 16: /* Num_Local_Parts */ case 17: if (result.def) result.ival = -1; if (result.ival < -1) { sprintf(msg, "Invalid Num_Local_Parts value (%d); " "being set to %d.", result.ival,-1); ZOLTAN_PRINT_WARN(zz->Proc, yo, msg); result.ival = -1; } zz->LB.Num_Local_Parts_Param = result.ival; status = 3; break; case 18: /* Migrate_Only_Proc_Changes */ if (result.def) result.ival = ZOLTAN_MIGRATE_ONLY_PROC_CHANGES_DEF; zz->Migrate.Only_Proc_Changes = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 19: /* LB.Remap */ if (result.def) result.ival = 0; zz->LB.Remap_Flag = result.ival; status = 3; /* Don't add to Params field of ZZ */ break; case 20: /* Seed */ if (result.def) result.ival = Zoltan_Seed(); zz->Seed = result.ival; Zoltan_Srand(result.ival, NULL); status = 3; break; case 21: /* LB_APPROACH */ if (result.def) strcpy(result.sval, ZOLTAN_LB_APPROACH_DEF); strcpy(zz->LB.Approach, result.sval); status = 3; break; } /* end switch (index) */
int Zoltan_Random( ZZ *zz, /* The Zoltan structure. */ float *part_sizes, /* Input: Array of size zz->LB.Num_Global_Parts * zz->Obj_Weight_Dim containing the percentage of work to be assigned to each partition. */ int *num_import, /* Return -1. Random uses only export lists. */ ZOLTAN_ID_PTR *import_global_ids, /* Not used. */ ZOLTAN_ID_PTR *import_local_ids, /* Not used. */ int **import_procs, /* Not used. */ int **import_to_part, /* Not used. */ int *num_export, /* Output: Number of objects to export. */ ZOLTAN_ID_PTR *export_global_ids, /* Output: GIDs to export. */ ZOLTAN_ID_PTR *export_local_ids, /* Output: LIDs to export. */ int **export_procs, /* Output: Processsors to export to. */ int **export_to_part /* Output: Partitions to export to. */ ) { int ierr = ZOLTAN_OK; int i, count, num_obj; int max_export; double rand_frac = 1.0; /* Default is to move all objects. */ ZOLTAN_ID_PTR global_ids = NULL; ZOLTAN_ID_PTR local_ids = NULL; int *parts = NULL; float *dummy = NULL; static char *yo = "Zoltan_Random"; static int first_time = 1; ZOLTAN_TRACE_ENTER(zz, yo); /* Synchronize the random number generator. * This synchronization is needed only for sanity in our nightly testing. * If some other operation (eg., Zoltan_LB_Eval) changes the status of * the random number generator, the answers here will change. They won't * be wrong, but they will be different from our accepted answers. */ if (first_time) { Zoltan_Srand(zz->Seed, NULL); Zoltan_Rand(NULL); first_time=0; } /* No import lists computed. */ *num_import = -1; /* Get parameter values. */ Zoltan_Bind_Param(Random_params, "RANDOM_MOVE_FRACTION", (void *) &rand_frac); Zoltan_Assign_Param_Vals(zz->Params, Random_params, zz->Debug_Level, zz->Proc, zz->Debug_Proc); /* Get list of local objects. */ ierr = Zoltan_Get_Obj_List(zz, &num_obj, &global_ids, &local_ids, 0, &dummy, &parts); /* Bound number of objects to export. */ max_export = 1.5*rand_frac*num_obj; /* Allocate export lists. */ *export_global_ids = *export_local_ids = NULL; *export_procs = *export_to_part = NULL; if (max_export > 0) { if (!Zoltan_Special_Malloc(zz, (void **)export_global_ids, max_export, ZOLTAN_SPECIAL_MALLOC_GID) || !Zoltan_Special_Malloc(zz, (void **)export_local_ids, max_export, ZOLTAN_SPECIAL_MALLOC_LID) || !Zoltan_Special_Malloc(zz, (void **)export_procs, max_export, ZOLTAN_SPECIAL_MALLOC_INT) || !Zoltan_Special_Malloc(zz, (void **)export_to_part, max_export, ZOLTAN_SPECIAL_MALLOC_INT)) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Memory error."); ierr = ZOLTAN_MEMERR; goto End; } } /* Randomly assign ids to procs. */ count=0; for (i=0; i<num_obj; i++){ /* Randomly select some objects to move (export) */ if ((count<max_export) && (Zoltan_Rand(NULL)<rand_frac*ZOLTAN_RAND_MAX)){ /* export_global_ids[count] = global_ids[i]; */ ZOLTAN_SET_GID(zz, &((*export_global_ids)[count*zz->Num_GID]), &global_ids[i*zz->Num_GID]); if (local_ids) /* export_local_ids[count] = local_ids[i]; */ ZOLTAN_SET_LID(zz, &((*export_local_ids)[count*zz->Num_LID]), &local_ids[i*zz->Num_LID]); /* Randomly pick new partition number. */ (*export_to_part)[count] = Zoltan_Rand_InRange(NULL, zz->LB.Num_Global_Parts); /* Processor number is derived from partition number. */ (*export_procs)[count] = Zoltan_LB_Part_To_Proc(zz, (*export_to_part)[count], &global_ids[i*zz->Num_GID]); /* printf("Debug: Export gid %u to part %d and proc %d.\n", (*export_global_ids)[count], (*export_to_part)[count], (*export_procs)[count]); */ ++count; } } (*num_export) = count; End: /* Free local memory, but not export lists. */ ZOLTAN_FREE(&global_ids); ZOLTAN_FREE(&local_ids); ZOLTAN_FREE(&parts); ZOLTAN_TRACE_EXIT(zz, yo); return ierr; }
int Zoltan_PHG_CoarsePartition( ZZ *zz, HGraph *phg, /* Input: coarse hypergraph -- distributed! */ int numPart, /* Input: number of partitions to generate. */ float *part_sizes, /* Input: array of size numPart listing target sizes (% of work) for the partitions */ Partition part, /* Input: array of initial partition assignments. Output: array of computed partition assignments. */ PHGPartParams *hgp /* Input: parameters to use. */ ) { /* * Zoltan_PHG_CoarsePartition computes a partitioning of a hypergraph. * Typically, this routine is called at the bottom level in a * multilevel scheme (V-cycle). * It gathers the distributed hypergraph to each processor and computes * a decomposition of the serial hypergraph. * It computes a different partition on each processor * using different random numbers (and possibly also * different algorithms) and selects the best. */ char *yo = "Zoltan_PHG_CoarsePartition"; int ierr = ZOLTAN_OK; int i, si, j; static PHGComm scomm; /* Serial communicator info */ static int first_time = 1; HGraph *shg = NULL; /* Serial hypergraph gathered from phg */ int *spart = NULL; /* Partition vectors for shg. */ int *new_part = NULL; /* Ptr to new partition vector. */ float *bestvals = NULL; /* Best cut values found so far */ int worst, new_cand; float bal, cut, worst_cut; int fine_timing = (hgp->use_timers > 2); struct phg_timer_indices *timer = Zoltan_PHG_LB_Data_timers(zz); int local_coarse_part = hgp->LocalCoarsePartition; /* Number of iterations to try coarse partitioning on each proc. */ /* 10 when p=1, and 1 when p is large. */ const int num_coarse_iter = 1 + 9/zz->Num_Proc; ZOLTAN_TRACE_ENTER(zz, yo); if (fine_timing) { if (timer->cpgather < 0) timer->cpgather = Zoltan_Timer_Init(zz->ZTime, 1, "CP Gather"); if (timer->cprefine < 0) timer->cprefine = Zoltan_Timer_Init(zz->ZTime, 0, "CP Refine"); if (timer->cpart < 0) timer->cpart = Zoltan_Timer_Init(zz->ZTime, 0, "CP Part"); ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator); } /* Force LocalCoarsePartition if large global graph */ #define LARGE_GRAPH_VTX 64000 #define LARGE_GRAPH_PINS 256000 if (phg->dist_x[phg->comm->nProc_x] > LARGE_GRAPH_VTX){ /* TODO: || (global_nPins > LARGE_GRAPH_PINS) */ local_coarse_part = 1; } /* take care of all special cases first */ if (!strcasecmp(hgp->coarsepartition_str, "no") || !strcasecmp(hgp->coarsepartition_str, "none")) { /* Do no coarse partitioning. */ /* Do a sanity test and mapping to parts [0,...,numPart-1] */ int first = 1; PHGComm *hgc=phg->comm; Zoltan_Srand_Sync (Zoltan_Rand(NULL), &(hgc->RNGState_col), hgc->col_comm); if (hgp->UsePrefPart) { for (i = 0; i < phg->nVtx; i++) { /* Impose fixed vertex/preferred part constraints. */ if (phg->pref_part[i] < 0) { /* Free vertex in fixedvertex partitioning or repart */ /* randomly assigned to a part */ part[i] = Zoltan_Rand_InRange(&(hgc->RNGState_col), numPart); } else { if (phg->bisec_split < 0) /* direct k-way, use part numbers directly */ part[i] = phg->pref_part[i]; else /* recursive bisection, map to 0-1 part numbers */ part[i] = (phg->pref_part[i] < phg->bisec_split ? 0 : 1); } } } else { for (i = 0; i < phg->nVtx; i++) { if (part[i] >= numPart || part[i]<0) { if (first) { ZOLTAN_PRINT_WARN(zz->Proc, yo, "Initial part number > numParts."); first = 0; ierr = ZOLTAN_WARN; } part[i] = ((part[i]<0) ? -part[i] : part[i]) % numPart; } } } } else if (numPart == 1) { /* everything goes in the one partition */ for (i = 0; i < phg->nVtx; i++) part[i] = 0; } else if (!hgp->UsePrefPart && numPart >= phg->dist_x[phg->comm->nProc_x]) { /* more partitions than vertices, trivial answer */ for (i = 0; i < phg->nVtx; i++) part[i] = phg->dist_x[phg->comm->myProc_x]+i; } else if (local_coarse_part) { /* Apply local partitioner to each column */ ierr = local_coarse_partitioner(zz, phg, numPart, part_sizes, part, hgp, hgp->CoarsePartition); } else { /* Normal case: * Gather distributed HG to each processor; * compute different partitioning on each processor; * select the "best" result. */ ZOLTAN_PHG_COARSEPARTITION_FN *CoarsePartition; /* Select different coarse partitioners for processors here. */ CoarsePartition = hgp->CoarsePartition; if (CoarsePartition == NULL) { /* auto */ /* Select a coarse partitioner from the array of coarse partitioners */ CoarsePartition = CoarsePartitionFns[phg->comm->myProc % NUM_COARSEPARTITION_FNS]; } if (phg->comm->nProc == 1) { /* Serial and parallel hgraph are the same. */ shg = phg; } else { /* Set up a serial communication struct for gathered HG */ if (first_time) { scomm.nProc_x = scomm.nProc_y = 1; scomm.myProc_x = scomm.myProc_y = 0; scomm.Communicator = MPI_COMM_SELF; scomm.row_comm = MPI_COMM_SELF; scomm.col_comm = MPI_COMM_SELF; scomm.myProc = 0; scomm.nProc = 1; first_time = 0; } scomm.RNGState = Zoltan_Rand(NULL); scomm.RNGState_row = Zoltan_Rand(NULL); scomm.RNGState_col = Zoltan_Rand(NULL); scomm.zz = zz; /* * Gather parallel hypergraph phg to each processor, creating * serial hypergraph shg. */ if (fine_timing) { ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator); ZOLTAN_TIMER_START(zz->ZTime, timer->cpgather, phg->comm->Communicator); } ierr = Zoltan_PHG_Gather_To_All_Procs(zz, phg, hgp, &scomm, &shg); if (ierr < 0) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Error returned from gather."); goto End; } if (fine_timing) { ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpgather, phg->comm->Communicator); ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator); } } /* * Allocate partition array spart for the serial hypergraph shg * and partition shg. */ spart = (int *) ZOLTAN_CALLOC(shg->nVtx * (NUM_PART_KEEP+1), sizeof(int)); bestvals = (float *) ZOLTAN_MALLOC((NUM_PART_KEEP+1)*sizeof(int)); if ((!spart) || (!bestvals)) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Out of memory."); ierr = ZOLTAN_MEMERR; goto End; } /* Compute several coarse partitionings. */ /* Keep the NUM_PART_KEEP best ones around. */ /* Currently, only the best one is used. */ /* Set RNG so different procs compute different parts. */ Zoltan_Srand(Zoltan_Rand(NULL) + zz->Proc, NULL); new_cand = 0; new_part = spart; for (i=0; i< num_coarse_iter; i++){ int savefmlooplimit=hgp->fm_loop_limit; /* Overwrite worst partition with new candidate. */ ierr = CoarsePartition(zz, shg, numPart, part_sizes, new_part, hgp); if (ierr < 0) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Error returned from CoarsePartition."); goto End; } /* time refinement step in coarse partitioner */ if (fine_timing) { ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator); ZOLTAN_TIMER_START(zz->ZTime, timer->cprefine, phg->comm->Communicator); } /* UVCUVC: Refine new candidate: only one pass is enough. */ hgp->fm_loop_limit = 1; Zoltan_PHG_Refinement(zz, shg, numPart, part_sizes, new_part, hgp); hgp->fm_loop_limit = savefmlooplimit; /* stop refinement timer */ if (fine_timing) { ZOLTAN_TIMER_STOP(zz->ZTime, timer->cprefine, phg->comm->Communicator); ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator); } /* Decide if candidate is in the top tier or not. */ /* Our objective is a combination of cuts and balance */ bal = Zoltan_PHG_Compute_Balance(zz, shg, part_sizes, 0, numPart, new_part); cut = Zoltan_PHG_Compute_ConCut(shg->comm, shg, new_part, numPart, &ierr); /* Use ratio-cut as our objective. There are many other options! */ bestvals[new_cand] = cut/(MAX(2.-bal, 0.0001)); /* avoid divide-by-0 */ if (ierr < 0) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Error returned from Zoltan_PHG_Compute_ConCut."); goto End; } if (i<NUM_PART_KEEP) new_cand = i+1; else { /* find worst partition vector, to overwrite it */ /* future optimization: keep bestvals sorted */ worst = 0; worst_cut = bestvals[0]; for (j=1; j<NUM_PART_KEEP+1; j++){ if (worst_cut < bestvals[j]){ worst_cut = bestvals[j]; worst = j; } } new_cand = worst; } new_part = spart+new_cand*(shg->nVtx); } /* Copy last partition vector such that all the best ones are contiguous starting at spart. */ for (i=0; i<shg->nVtx; i++){ new_part[i] = spart[NUM_PART_KEEP*(shg->nVtx)+i]; } /* Also update bestvals */ bestvals[new_cand] = bestvals[NUM_PART_KEEP]; /* Evaluate and select the best. */ /* For now, only pick the best one, in the future we pick the k best. */ ierr = pick_best(zz, hgp, phg->comm, shg, numPart, MIN(NUM_PART_KEEP, num_coarse_iter), spart, bestvals); if (ierr < 0) { ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Error returned from pick_best."); goto End; } if (phg->comm->nProc > 1) { /* Map gathered partition back to 2D distribution */ for (i = 0; i < phg->nVtx; i++) { /* KDDKDD Assume vertices in serial HG are ordered by GNO of phg */ si = VTX_LNO_TO_GNO(phg, i); part[i] = spart[si]; } Zoltan_HG_HGraph_Free(shg); ZOLTAN_FREE(&shg); } else { /* single processor */ for (i = 0; i < phg->nVtx; i++) part[i] = spart[i]; } ZOLTAN_FREE(&spart); ZOLTAN_FREE(&bestvals); } End: if (fine_timing) ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator); ZOLTAN_TRACE_EXIT(zz, yo); return ierr; }