10 #ifndef _ZOLTAN2_ALGPULP_HPP_
11 #define _ZOLTAN2_ALGPULP_HPP_
24 #ifndef HAVE_ZOLTAN2_PULP
30 template <
typename Adapter>
36 const RCP<
const Comm<int> > &,
37 const RCP<const base_adapter_t> &
40 throw std::runtime_error(
41 "BUILD ERROR: PuLP requested but not compiled into Zoltan2.\n"
42 "Please set CMake flag TPL_ENABLE_PuLP:BOOL=ON.");
59 #ifdef HAVE_ZOLTAN2_PULP
65 #ifndef HAVE_ZOLTAN2_MPI
73 template <
typename Adapter>
74 class AlgPuLP :
public Algorithm<Adapter>
80 typedef typename Adapter::offset_t offset_t;
81 typedef typename Adapter::scalar_t
scalar_t;
84 typedef typename Adapter::userCoord_t userCoord_t;
96 AlgPuLP(
const RCP<const Environment> &env__,
97 const RCP<
const Comm<int> > &problemComm__,
98 const RCP<
const IdentifierAdapter<user_t> > &adapter__) :
99 env(env__), problemComm(problemComm__), adapter(adapter__)
101 std::string errStr =
"cannot build GraphModel from IdentifierAdapter, ";
102 errStr +=
"PuLP requires Graph, Matrix, or Mesh Adapter";
103 throw std::runtime_error(errStr);
106 AlgPuLP(
const RCP<const Environment> &env__,
107 const RCP<
const Comm<int> > &problemComm__,
108 const RCP<
const VectorAdapter<user_t> > &adapter__) :
109 env(env__), problemComm(problemComm__), adapter(adapter__)
111 std::string errStr =
"cannot build GraphModel from VectorAdapter, ";
112 errStr +=
"PuLP requires Graph, Matrix, or Mesh Adapter";
113 throw std::runtime_error(errStr);
116 AlgPuLP(
const RCP<const Environment> &env__,
117 const RCP<
const Comm<int> > &problemComm__,
118 const RCP<
const GraphAdapter<user_t,userCoord_t> > &adapter__) :
119 env(env__), problemComm(problemComm__), adapter(adapter__)
127 AlgPuLP(
const RCP<const Environment> &env__,
128 const RCP<
const Comm<int> > &problemComm__,
129 const RCP<
const MatrixAdapter<user_t,userCoord_t> > &adapter__) :
130 env(env__), problemComm(problemComm__), adapter(adapter__)
135 const ParameterList &pl = env->getParameters();
136 const Teuchos::ParameterEntry *pe;
138 std::string defString(
"default");
139 std::string objectOfInterest(defString);
140 pe = pl.getEntryPtr(
"objects_to_partition");
142 objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
144 if (objectOfInterest == defString ||
145 objectOfInterest == std::string(
"matrix_rows") )
147 else if (objectOfInterest == std::string(
"matrix_columns"))
149 else if (objectOfInterest == std::string(
"matrix_nonzeros"))
155 AlgPuLP(
const RCP<const Environment> &env__,
156 const RCP<
const Comm<int> > &problemComm__,
157 const RCP<
const MeshAdapter<user_t> > &adapter__) :
158 env(env__), problemComm(problemComm__), adapter(adapter__)
163 const ParameterList &pl = env->getParameters();
164 const Teuchos::ParameterEntry *pe;
166 std::string defString(
"default");
167 std::string objectOfInterest(defString);
168 pe = pl.getEntryPtr(
"objects_to_partition");
170 objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
172 if (objectOfInterest == defString ||
173 objectOfInterest == std::string(
"mesh_nodes") )
175 else if (objectOfInterest == std::string(
"mesh_elements"))
185 pl.set(
"pulp_vert_imbalance", 1.1,
"vertex imbalance tolerance, ratio of "
186 "maximum load over average load",
189 pl.set(
"pulp_edge_imbalance", 1.1,
"edge imbalance tolerance, ratio of "
190 "maximum load over average load",
193 pl.set(
"pulp_imbalance", 1.1,
"multiweight imbalance tolerance, ratio of "
194 "maximum load over average load",
198 pl.set(
"pulp_lp_init",
false,
"perform label propagation-based "
202 pl.set(
"pulp_minimize_maxcut",
false,
"perform per-part max cut "
206 pl.set(
"pulp_verbose",
false,
"verbose output",
210 pl.set(
"pulp_do_repart",
false,
"perform repartitioning",
216 void partition(
const RCP<PartitioningSolution<Adapter> > &solution);
222 int* scale_weights(
size_t n,
int nWgt,
223 ArrayView<StridedData<lno_t, scalar_t> > fwgts);
225 const RCP<const Environment> env;
226 const RCP<const Comm<int> > problemComm;
227 const RCP<const base_adapter_t> adapter;
228 RCP<const GraphModel<base_adapter_t> > model;
233 template <
typename Adapter>
234 void AlgPuLP<Adapter>::buildModel(
modelFlag_t &flags)
236 const ParameterList &pl = env->getParameters();
237 const Teuchos::ParameterEntry *pe;
239 std::string defString(
"default");
240 std::string symParameter(defString);
241 pe = pl.getEntryPtr(
"symmetrize_graph");
243 symParameter = pe->getValue<std::string>(&symParameter);
244 if (symParameter == std::string(
"transpose"))
246 else if (symParameter == std::string(
"bipartite"))
249 bool sgParameter =
false;
250 pe = pl.getEntryPtr(
"subset_graph");
252 sgParameter = pe->getValue(&sgParameter);
258 #ifndef HAVE_ZOLTAN2_MPI
262 this->model = rcp(
new GraphModel<base_adapter_t>(this->adapter, this->env,
263 this->problemComm, flags));
272 template <
typename Adapter>
274 const RCP<PartitioningSolution<Adapter> > &solution
279 size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
281 int num_parts = (int)numGlobalParts;
288 int np = problemComm->getSize();
291 const size_t modelVerts = model->getLocalNumVertices();
292 const size_t modelEdges = model->getLocalNumEdges();
293 int num_verts = (int)modelVerts;
294 long num_edges = (long)modelEdges;
299 ArrayView<const gno_t> vtxIDs;
300 ArrayView<StridedData<lno_t, scalar_t> > vwgts;
301 size_t nVtx = model->getVertexList(vtxIDs, vwgts);
302 int nVwgts = model->getNumWeightsPerVertex();
305 #ifndef HAVE_ZOLTAN2_MPI
308 std::cerr <<
"Warning: NumWeightsPerVertex is " << nVwgts
309 <<
" but PuLP allows only one weight. "
310 <<
" Zoltan2 will use only the first weight per vertex."
314 std::unique_ptr<int[]> vertex_weights;
315 long vertex_weights_sum = 0;
318 vertex_weights = std::unique_ptr<int[]>(scale_weights(nVtx, nVwgts, vwgts));
320 for (
int i = 0; i < num_verts; ++i)
321 vertex_weights_sum += (
long)vertex_weights[i];
323 vertex_weights = std::unique_ptr<int[]>(
nullptr);
327 std::unique_ptr<int[]> vertex_weights;
329 vertex_weights = std::unique_ptr<int[]>(scale_weights(nVtx, nVwgts, vwgts));
331 vertex_weights = std::unique_ptr<int[]>(
nullptr);
336 ArrayView<const gno_t> adjs;
337 ArrayView<const offset_t> offsets;
338 ArrayView<StridedData<lno_t, scalar_t> > ewgts;
339 size_t nEdge = model->getEdgeList(adjs, offsets, ewgts);
340 int nEwgts = model->getNumWeightsPerEdge();
342 std::cerr <<
"Warning: NumWeightsPerEdge is " << nEwgts
343 <<
" but PuLP/XtraPuLP allows only one weight. "
344 <<
" Zoltan2 will use only the first weight per edge."
348 std::unique_ptr<int[]> edge_weights;
351 edge_weights = std::unique_ptr<int[]>(scale_weights(nEdge, nEwgts, ewgts));
355 vertex_weights = std::unique_ptr<int[]>(
new int[nVtx]);
357 for (
size_t i = 0; i < nVtx; ++i) {
358 vertex_weights[i] = 1;
364 edge_weights = std::unique_ptr<int[]>(
new int[nEdge]);
365 for (
size_t i = 0; i < nEdge; ++i) {
369 edge_weights = std::unique_ptr<int[]>(
nullptr);
372 #ifndef HAVE_ZOLTAN2_MPI
374 int* out_edges =
nullptr;
375 long* out_offsets =
nullptr;
379 pulp_graph_t g = {num_verts, num_edges,
380 out_edges, out_offsets,
381 vertex_weights.get(), edge_weights.get(),
386 unsigned long* out_edges =
nullptr;
387 unsigned long* out_offsets =
nullptr;
391 const size_t modelVertsGlobal = model->getGlobalNumVertices();
392 const size_t modelEdgesGlobal = model->getGlobalNumEdges();
393 unsigned long num_verts_global = (
unsigned long)modelVertsGlobal;
394 unsigned long num_edges_global = (
unsigned long)modelEdgesGlobal;
396 unsigned long* global_ids =
nullptr;
399 ArrayView<size_t> vtxDist;
400 model->getVertexDist(vtxDist);
401 unsigned long* verts_per_rank =
new unsigned long[np+1];
402 for (
int i = 0; i < np+1; ++i)
403 verts_per_rank[i] = vtxDist[i];
406 create_xtrapulp_dist_graph(&g, num_verts_global, num_edges_global,
407 (
unsigned long)num_verts, (
unsigned long)num_edges,
408 out_edges, out_offsets, global_ids, verts_per_rank,
409 nVwgts, vertex_weights.get(), edge_weights.get());
415 ArrayRCP<part_t> partList(
new part_t[num_verts], 0, num_verts,
true);
416 int* parts =
nullptr;
417 if (num_verts && (
sizeof(
int) ==
sizeof(
part_t))) {
419 parts = (
int *)partList.getRawPtr();
423 parts =
new int[num_verts];
430 const Teuchos::ParameterList &pl = env->getParameters();
431 const Teuchos::ParameterEntry *pe;
439 bool do_lp_init =
false;
440 bool do_bfs_init =
true;
441 bool do_edge_bal =
false;
442 bool do_repart =
false;
443 bool do_maxcut_min =
false;
444 bool verbose_output =
false;
447 pe = pl.getEntryPtr(
"pulp_lp_init");
448 if (pe) do_lp_init = pe->getValue(&do_lp_init);
449 if (do_lp_init) do_bfs_init =
false;
452 pe = pl.getEntryPtr(
"pulp_minimize_maxcut");
454 do_maxcut_min = pe->getValue(&do_maxcut_min);
457 if (do_maxcut_min) do_edge_bal =
true;
460 pe = pl.getEntryPtr(
"pulp_do_repart");
462 do_repart = pe->getValue(&do_repart);
470 std::string errStr =
"repartitioning within (Xtra)PuLP is ";
471 errStr +=
"currently unsupported.";
472 throw std::runtime_error(errStr);
477 double vert_imbalance = 1.1;
478 double edge_imbalance = 1.1;
479 double imbalance = 1.1;
481 pe = pl.getEntryPtr(
"pulp_vert_imbalance");
482 if (pe) vert_imbalance = pe->getValue<
double>(&vert_imbalance);
483 pe = pl.getEntryPtr(
"pulp_edge_imbalance");
485 edge_imbalance = pe->getValue<
double>(&edge_imbalance);
489 pe = pl.getEntryPtr(
"pulp_imbalance");
490 if (pe) imbalance = pe->getValue<
double>(&imbalance);
492 if (vert_imbalance < 1.0)
493 throw std::runtime_error(
"pulp_vert_imbalance must be '1.0' or greater.");
494 if (edge_imbalance < 1.0)
495 throw std::runtime_error(
"pulp_edge_imbalance must be '1.0' or greater.");
497 throw std::runtime_error(
"pulp_imbalance must be '1.0' or greater.");
501 pe = pl.getEntryPtr(
"pulp_verbose");
502 if (pe) verbose_output = pe->getValue(&verbose_output);
505 int pulp_seed = rand();
506 pe = pl.getEntryPtr(
"pulp_seed");
507 if (pe) pulp_seed = pe->getValue(&pulp_seed);
510 if (verbose_output) {
511 printf(
"procid: %d, n: %i, m: %li, vb: %f, eb: %f, imb: %f, p: %i\n",
512 problemComm->getRank(),
513 num_verts, num_edges,
514 vert_imbalance, edge_imbalance, imbalance, num_parts);
518 #ifndef HAVE_ZOLTAN2_MPI
520 pulp_part_control_t ppc = {vert_imbalance, edge_imbalance,
521 do_lp_init, do_bfs_init, do_repart,
522 do_edge_bal, do_maxcut_min,
523 verbose_output, pulp_seed};
525 ierr = pulp_run(&g, &ppc, parts, num_parts);
527 env->globalInputAssertion(__FILE__, __LINE__,
"pulp_run",
531 std::unique_ptr<double[]> constraints;
533 constraints = std::unique_ptr<double[]>(
new double[nVwgts]);
534 for (
int i = 0; i < nVwgts; ++i) {
535 constraints[i] = imbalance;
538 constraints = std::unique_ptr<double[]>(
nullptr);
542 pulp_part_control_t ppc = {
543 vert_imbalance, edge_imbalance,
544 constraints.get(), nVwgts,
545 do_lp_init, do_bfs_init, do_repart,
546 do_edge_bal, do_maxcut_min,
547 verbose_output, pulp_seed};
549 ierr = xtrapulp_run(&g, &ppc, parts, num_parts);
551 env->globalInputAssertion(__FILE__, __LINE__,
"xtrapulp_run",
557 if ( (
sizeof(
int) !=
sizeof(
part_t)) || (num_verts == 0) ) {
558 for (
int i = 0; i < num_verts; i++)
559 partList[i] = parts[i];
563 solution->setParts(partList);
565 env->memory(
"Zoltan2-(Xtra)PuLP: After creating solution");
568 #ifndef HAVE_ZOLTAN2_MPI
592 template <
typename Adapter>
593 int* AlgPuLP<Adapter>::scale_weights(
594 size_t nVtx,
int nWgt,
596 typename Adapter::scalar_t> > fwgts
599 const double INT_EPSILON = 1e-5;
601 int *iwgts =
new int[nVtx*nWgt];
602 int *nonint_local =
new int[nWgt+nWgt];
603 int *nonint = nonint_local + nWgt;
605 double *sum_wgt_local =
new double[nWgt*4];
606 double *max_wgt_local = sum_wgt_local + nWgt;
607 double *sum_wgt = max_wgt_local + nWgt;
608 double *max_wgt = sum_wgt + nWgt;
610 for (
int i = 0; i < nWgt; i++) {
612 sum_wgt_local[i] = 0.0;
613 max_wgt_local[i] = 0.0;
618 for (
int j = 0; j < nWgt; j++) {
619 for (
size_t i = 0; i < nVtx; i++) {
620 double fw = double(fwgts[j][i]);
621 if (!nonint_local[j]) {
622 int tmp = (int) floor(fw + .5);
623 if (fabs((
double)tmp-fw) > INT_EPSILON) {
627 sum_wgt_local[j] += fw;
628 if (fw > max_wgt_local[j]) max_wgt_local[j] = fw;
632 Teuchos::reduceAll<int,int> (*problemComm, Teuchos::REDUCE_MAX, nWgt,
633 nonint_local, nonint);
634 Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, nWgt,
635 sum_wgt_local, sum_wgt);
636 Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, nWgt,
637 max_wgt_local, max_wgt);
639 const double max_wgt_sum = double(std::numeric_limits<int>::max()/8);
640 for (
int j = 0; j < nWgt; j++) {
645 if (nonint[j] || (max_wgt[j]<=INT_EPSILON) || (sum_wgt[j]>max_wgt_sum)) {
647 if (sum_wgt[j] != 0.) scale = max_wgt_sum/sum_wgt[j];
651 for (
size_t i = 0; i < nVtx; i++)
652 iwgts[i*nWgt+j] = (
int) ceil(
double(fwgts[j][i])*scale);
655 delete [] nonint_local;
656 delete [] sum_wgt_local;
664 #endif // HAVE_ZOLTAN2_PULP
use columns as graph vertices
Zoltan2::BaseAdapter< userTypes_t > base_adapter_t
use mesh nodes as vertices
virtual void partition(const RCP< PartitioningSolution< Adapter > > &)
Partitioning method.
fast typical checks for valid arguments
Adapter::base_adapter_t base_adapter_t
static RCP< Teuchos::BoolParameterEntryValidator > getBoolValidator()
Exists to make setting up validators less cluttered.
algorithm requires consecutive ids
std::bitset< NUM_MODEL_FLAGS > modelFlag_t
model must symmetrize input
map_t::global_ordinal_type gno_t
model must symmetrize input
Defines the PartitioningSolution class.
static RCP< Teuchos::AnyNumberParameterEntryValidator > getAnyDoubleValidator()
Exists to make setting up validators less cluttered.
sub-steps, each method's entry and exit
SparseMatrixAdapter_t::part_t part_t
use matrix rows as graph vertices
algorithm requires no self edges
Adapter::scalar_t scalar_t
use nonzeros as graph vertices
Algorithm defines the base class for all algorithms.
map_t::local_ordinal_type lno_t
static RCP< Teuchos::AnyNumberParameterEntryValidator > getAnyIntValidator()
Exists to make setting up validators less cluttered.
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS's idx_t...
use mesh elements as vertices
static void ASSIGN_ARRAY(first_t **a, ArrayView< second_t > &b)
Defines the GraphModel interface.
A gathering of useful namespace methods.
static void DELETE_ARRAY(first_t **a)
model represents graph within only one rank
static void getValidParameters(ParameterList &)
Set up validators specific to this algorithm.
AlgPuLP(const RCP< const Environment > &, const RCP< const Comm< int > > &, const RCP< const base_adapter_t > &)
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t