void AlgPTScotch<Adapter>::partition( const RCP<PartitioningSolution<Adapter> > &solution ) { HELLO; size_t numGlobalParts = solution->getTargetGlobalNumberOfParts(); SCOTCH_Num partnbr=0; TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(partnbr, numGlobalParts, env); #ifdef HAVE_ZOLTAN2_MPI int ierr = 0; int me = problemComm->getRank(); const SCOTCH_Num baseval = 0; // Base value for array indexing. // GraphModel returns GNOs from base 0. SCOTCH_Strat stratstr; // Strategy string // TODO: Set from parameters SCOTCH_stratInit(&stratstr); // Allocate and initialize PTScotch Graph data structure. SCOTCH_Dgraph *gr = SCOTCH_dgraphAlloc(); // Scotch distributed graph ierr = SCOTCH_dgraphInit(gr, mpicomm); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphInit", !ierr, BASIC_ASSERTION, problemComm); // Get vertex info ArrayView<const gno_t> vtxID; ArrayView<StridedData<lno_t, scalar_t> > xyz; ArrayView<StridedData<lno_t, scalar_t> > vwgts; size_t nVtx = model->getVertexList(vtxID, xyz, vwgts); SCOTCH_Num vertlocnbr=0; TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(vertlocnbr, nVtx, env); SCOTCH_Num vertlocmax = vertlocnbr; // Assumes no holes in global nums. // Get edge info ArrayView<const gno_t> edgeIds; ArrayView<const int> procIds; ArrayView<const lno_t> offsets; ArrayView<StridedData<lno_t, scalar_t> > ewgts; size_t nEdge = model->getEdgeList(edgeIds, procIds, offsets, ewgts); SCOTCH_Num edgelocnbr=0; TPL_Traits<SCOTCH_Num, size_t>::ASSIGN_TPL_T(edgelocnbr, nEdge, env); const SCOTCH_Num edgelocsize = edgelocnbr; // Assumes adj array is compact. SCOTCH_Num *vertloctab; // starting adj/vtx TPL_Traits<SCOTCH_Num, lno_t>::ASSIGN_TPL_T_ARRAY(&vertloctab, offsets, env); SCOTCH_Num *edgeloctab; // adjacencies TPL_Traits<SCOTCH_Num, gno_t>::ASSIGN_TPL_T_ARRAY(&edgeloctab, edgeIds, env); // We don't use these arrays, but we need them as arguments to Scotch. SCOTCH_Num *vendloctab = NULL; // Assume consecutive storage for adj SCOTCH_Num *vlblloctab = NULL; // Vertex label array SCOTCH_Num *edgegsttab = NULL; // Array for ghost vertices // Get weight info. SCOTCH_Num *velotab = NULL; // Vertex weights SCOTCH_Num *edlotab = NULL; // Edge weights int nVwgts = model->getNumWeightsPerVertex(); int nEwgts = model->getNumWeightsPerEdge(); if (nVwgts > 1 && me == 0) { std::cerr << "Warning: NumWeightsPerVertex is " << nVwgts << " but Scotch allows only one weight. " << " Zoltan2 will use only the first weight per vertex." << std::endl; } if (nEwgts > 1 && me == 0) { std::cerr << "Warning: NumWeightsPerEdge is " << nEwgts << " but Scotch allows only one weight. " << " Zoltan2 will use only the first weight per edge." << std::endl; } if (nVwgts) { velotab = new SCOTCH_Num[nVtx+1]; // +1 since Scotch wants all procs // to have non-NULL arrays scale_weights(nVtx, vwgts[0], velotab); } if (nEwgts) { edlotab = new SCOTCH_Num[nEdge+1]; // +1 since Scotch wants all procs // to have non-NULL arrays scale_weights(nEdge, ewgts[0], edlotab); } // Build PTScotch distributed data structure ierr = SCOTCH_dgraphBuild(gr, baseval, vertlocnbr, vertlocmax, vertloctab, vendloctab, velotab, vlblloctab, edgelocnbr, edgelocsize, edgeloctab, edgegsttab, edlotab); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphBuild", !ierr, BASIC_ASSERTION, problemComm); // Create array for Scotch to return results in. ArrayRCP<part_t> partList(new part_t[nVtx], 0, nVtx,true); SCOTCH_Num *partloctab = NULL; if (nVtx && (sizeof(SCOTCH_Num) == sizeof(part_t))) { // Can write directly into the solution's memory partloctab = (SCOTCH_Num *) partList.getRawPtr(); } else { // Can't use solution memory directly; will have to copy later. // Note: Scotch does not like NULL arrays, so add 1 to always have non-null. // ParMETIS has this same "feature." See Zoltan bug 4299. partloctab = new SCOTCH_Num[nVtx+1]; } // Get target part sizes float *partsizes = new float[numGlobalParts]; if (!solution->criteriaHasUniformPartSizes(0)) for (size_t i=0; i<numGlobalParts; i++) partsizes[i] = solution->getCriteriaPartSize(0, i); else for (size_t i=0; i<numGlobalParts; i++) partsizes[i] = 1.0 / float(numGlobalParts); // Allocate and initialize PTScotch target architecture data structure SCOTCH_Arch archdat; SCOTCH_archInit(&archdat); SCOTCH_Num velosum = 0; SCOTCH_dgraphSize (gr, &velosum, NULL, NULL, NULL); SCOTCH_Num *goalsizes = new SCOTCH_Num[partnbr]; // TODO: The goalsizes are set as in Zoltan; not sure it is correct there // or here. // It appears velosum is global NUMBER of vertices, not global total // vertex weight. I think we should use the latter. // Fix this when we add vertex weights. for (SCOTCH_Num i = 0; i < partnbr; i++) goalsizes[i] = SCOTCH_Num(ceil(velosum * partsizes[i])); delete [] partsizes; SCOTCH_archCmpltw(&archdat, partnbr, goalsizes); // Call partitioning; result returned in partloctab. ierr = SCOTCH_dgraphMap(gr, &archdat, &stratstr, partloctab); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphMap", !ierr, BASIC_ASSERTION, problemComm); SCOTCH_archExit(&archdat); delete [] goalsizes; // TODO - metrics #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY int me = env->comm_->getRank(); #endif #ifdef HAVE_SCOTCH_ZOLTAN2_GETMEMORYMAX if (me == 0){ size_t scotchBytes = SCOTCH_getMemoryMax(); std::cout << "Rank " << me << ": Maximum bytes used by Scotch: "; std::cout << scotchBytes << std::endl; } #endif // Clean up PTScotch SCOTCH_dgraphExit(gr); free(gr); SCOTCH_stratExit(&stratstr); // Load answer into the solution. if ((sizeof(SCOTCH_Num) != sizeof(part_t)) || (nVtx == 0)) { for (size_t i = 0; i < nVtx; i++) partList[i] = partloctab[i]; delete [] partloctab; } solution->setParts(partList); env->memory("Zoltan2-Scotch: After creating solution"); // Clean up copies made due to differing data sizes. TPL_Traits<SCOTCH_Num, lno_t>::DELETE_TPL_T_ARRAY(&vertloctab); TPL_Traits<SCOTCH_Num, gno_t>::DELETE_TPL_T_ARRAY(&edgeloctab); if (nVwgts) delete [] velotab; if (nEwgts) delete [] edlotab; #else // DO NOT HAVE_MPI // TODO: Handle serial case with calls to Scotch. // TODO: For now, assign everything to rank 0 and assume only one part. // TODO: Can probably use the code above for loading solution, // TODO: instead of duplicating it here. // TODO // TODO: Actual logic should call Scotch when number of processes == 1. ArrayView<const gno_t> vtxID; ArrayView<StridedData<lno_t, scalar_t> > xyz; ArrayView<StridedData<lno_t, scalar_t> > vwgts; size_t nVtx = model->getVertexList(vtxID, xyz, vwgts); ArrayRCP<part_t> partList(new part_t[nVtx], 0, nVtx, true); for (size_t i = 0; i < nVtx; i++) partList[i] = 0; solution->setParts(partList); #endif // DO NOT HAVE_MPI }
void AlgPTScotch( const RCP<const Environment> &env, // parameters & app comm const RCP<const Comm<int> > &problemComm, // problem comm #ifdef HAVE_ZOLTAN2_MPI MPI_Comm mpicomm, #endif const RCP<GraphModel<typename Adapter::base_adapter_t> > &model, // the graph RCP<PartitioningSolution<Adapter> > &solution ) { HELLO; typedef typename Adapter::lno_t lno_t; typedef typename Adapter::gno_t gno_t; typedef typename Adapter::scalar_t scalar_t; int ierr = 0; size_t numGlobalParts = solution->getTargetGlobalNumberOfParts(); SCOTCH_Num partnbr; SCOTCH_Num_Traits<size_t>::ASSIGN_TO_SCOTCH_NUM(partnbr, numGlobalParts, env); #ifdef HAVE_ZOLTAN2_MPI const SCOTCH_Num baseval = 0; // Base value for array indexing. // GraphModel returns GNOs from base 0. SCOTCH_Strat stratstr; // Strategy string // TODO: Set from parameters SCOTCH_stratInit(&stratstr); // Allocate & initialize PTScotch data structure. SCOTCH_Dgraph *gr = SCOTCH_dgraphAlloc(); // Scotch distributed graph ierr = SCOTCH_dgraphInit(gr, mpicomm); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphInit", !ierr, BASIC_ASSERTION, problemComm); // Get vertex info ArrayView<const gno_t> vtxID; ArrayView<StridedData<lno_t, scalar_t> > xyz; ArrayView<StridedData<lno_t, scalar_t> > vtxWt; size_t nVtx = model->getVertexList(vtxID, xyz, vtxWt); SCOTCH_Num vertlocnbr; SCOTCH_Num_Traits<size_t>::ASSIGN_TO_SCOTCH_NUM(vertlocnbr, nVtx, env); SCOTCH_Num vertlocmax = vertlocnbr; // Assumes no holes in global nums. // Get edge info ArrayView<const gno_t> edgeIds; ArrayView<const int> procIds; ArrayView<const lno_t> offsets; ArrayView<StridedData<lno_t, scalar_t> > ewgts; size_t nEdges = model->getEdgeList(edgeIds, procIds, offsets, ewgts); SCOTCH_Num edgelocnbr; SCOTCH_Num_Traits<size_t>::ASSIGN_TO_SCOTCH_NUM(edgelocnbr, nEdges, env); const SCOTCH_Num edgelocsize = edgelocnbr; // Assumes adj array is compact. SCOTCH_Num *vertloctab; // starting adj/vtx SCOTCH_Num_Traits<lno_t>::ASSIGN_SCOTCH_NUM_ARRAY(&vertloctab, offsets, env); SCOTCH_Num *edgeloctab; // adjacencies SCOTCH_Num_Traits<gno_t>::ASSIGN_SCOTCH_NUM_ARRAY(&edgeloctab, edgeIds, env); // We don't use these arrays, but we need them as arguments to Scotch. SCOTCH_Num *vendloctab = NULL; // Assume consecutive storage for adj SCOTCH_Num *vlblloctab = NULL; // Vertex label array SCOTCH_Num *edgegsttab = NULL; // Array for ghost vertices // Get weight info. // TODO: Actually get the weights; for now, not using weights. SCOTCH_Num *veloloctab = NULL; // Vertex weights SCOTCH_Num *edloloctab = NULL; // Edge weights //TODO int vwtdim = model->getVertexWeightDim(); //TODO int ewtdim = model->getEdgeWeightDim(); //TODO if (vwtdim) veloloctab = new SCOTCH_Num[nVtx]; //TODO if (ewtdim) edloloctab = new SCOTCH_Num[nEdges]; //TODO scale weights to SCOTCH_Nums. // Build PTScotch distributed data structure ierr = SCOTCH_dgraphBuild(gr, baseval, vertlocnbr, vertlocmax, vertloctab, vendloctab, veloloctab, vlblloctab, edgelocnbr, edgelocsize, edgeloctab, edgegsttab, edloloctab); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphBuild", !ierr, BASIC_ASSERTION, problemComm); // Create array for Scotch to return results in. ArrayRCP<partId_t> partList(new partId_t [nVtx], 0, nVtx,true); SCOTCH_Num *partloctab = NULL; if (nVtx && (sizeof(SCOTCH_Num) == sizeof(partId_t))) { // Can write directly into the solution's memory partloctab = (SCOTCH_Num *) partList.getRawPtr(); } else { // Can't use solution memory directly; will have to copy later. // Note: Scotch does not like NULL arrays, so add 1 to always have non-null. // ParMETIS has this same "feature." See Zoltan bug 4299. partloctab = new SCOTCH_Num[nVtx+1]; } // Call partitioning; result returned in partloctab. // TODO: Use SCOTCH_dgraphMap so can include a machine model in partitioning ierr = SCOTCH_dgraphPart(gr, partnbr, &stratstr, partloctab); env->globalInputAssertion(__FILE__, __LINE__, "SCOTCH_dgraphPart", !ierr, BASIC_ASSERTION, problemComm); // TODO - metrics #ifdef SHOW_ZOLTAN2_SCOTCH_MEMORY int me = env->comm_->getRank(); #endif #ifdef HAVE_SCOTCH_ZOLTAN2_GETMEMORYMAX if (me == 0){ size_t scotchBytes = SCOTCH_getMemoryMax(); std::cout << "Rank " << me << ": Maximum bytes used by Scotch: "; std::cout << scotchBytes << std::endl; } #endif // Clean up PTScotch SCOTCH_dgraphExit(gr); delete gr; SCOTCH_stratExit(&stratstr); // Load answer into the solution. if ((sizeof(SCOTCH_Num) != sizeof(partId_t)) || (nVtx == 0)) { for (size_t i = 0; i < nVtx; i++) partList[i] = partloctab[i]; delete [] partloctab; } ArrayRCP<const gno_t> gnos = arcpFromArrayView(vtxID); solution->setParts(gnos, partList, true); env->memory("Zoltan2-Scotch: After creating solution"); //if (me == 0) cout << " done." << endl; // Clean up Zoltan2 //TODO if (vwtdim) delete [] velotab; //TODO if (ewtdim) delete [] edlotab; // Clean up copies made due to differing data sizes. SCOTCH_Num_Traits<lno_t>::DELETE_SCOTCH_NUM_ARRAY(&vertloctab); SCOTCH_Num_Traits<gno_t>::DELETE_SCOTCH_NUM_ARRAY(&edgeloctab); #else // DO NOT HAVE_MPI // TODO: Handle serial case with calls to Scotch. // TODO: For now, assign everything to rank 0 and assume only one part. // TODO: Can probably use the code above for loading solution, // TODO: instead of duplicating it here. // TODO // TODO: Actual logic should call Scotch when number of processes == 1. ArrayView<const gno_t> vtxID; ArrayView<StridedData<lno_t, scalar_t> > xyz; ArrayView<StridedData<lno_t, scalar_t> > vtxWt; size_t nVtx = model->getVertexList(vtxID, xyz, vtxWt); for (size_t i = 0; i < nVtx; i++) partList[i] = 0; #endif // DO NOT HAVE_MPI }