Zoltan2
Zoltan2_AlgPuLP.hpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Zoltan2: A package of combinatorial algorithms for scientific computing
6 // Copyright 2012 Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Karen Devine (kddevin@sandia.gov)
39 // Erik Boman (egboman@sandia.gov)
40 // Siva Rajamanickam (srajama@sandia.gov)
41 //
42 // ***********************************************************************
43 //
44 // @HEADER
45 #ifndef _ZOLTAN2_ALGPULP_HPP_
46 #define _ZOLTAN2_ALGPULP_HPP_
47 
48 #include <Zoltan2_GraphModel.hpp>
49 #include <Zoltan2_Algorithm.hpp>
51 #include <Zoltan2_Util.hpp>
52 #include <Zoltan2_TPLTraits.hpp>
53 
57 
59 #ifndef HAVE_ZOLTAN2_PULP
60 
61 namespace Zoltan2 {
62 // Error handling for when PuLP is requested
63 // but Zoltan2 not built with PuLP.
64 
65 template <typename Adapter>
66 class AlgPuLP : public Algorithm<Adapter>
67 {
68 public:
70  AlgPuLP(const RCP<const Environment> &env,
71  const RCP<const Comm<int> > &problemComm,
72  const RCP<const base_adapter_t> &adapter
73  )
74  {
75  throw std::runtime_error(
76  "BUILD ERROR: PuLP requested but not compiled into Zoltan2.\n"
77  "Please set CMake flag TPL_ENABLE_PuLP:BOOL=ON.");
78  }
79 
82  static void getValidParameters(ParameterList & pl)
83  {
84  // No parameters needed in this error-handling version of AlgPuLP
85  }
86 };
87 
88 }
89 #endif
90 
93 
94 #ifdef HAVE_ZOLTAN2_PULP
95 
96 namespace Zoltan2 {
97 
98 extern "C" {
99 // TODO: XtraPuLP
100 #ifndef HAVE_ZOLTAN2_MPI
101 #include "pulp.h"
102 #else
103 #include "xtrapulp.h"
104 #endif
105 }
106 
107 
108 
109 
110 template <typename Adapter>
111 class AlgPuLP : public Algorithm<Adapter>
112 {
113 public:
114  typedef typename Adapter::base_adapter_t base_adapter_t;
115  typedef typename Adapter::lno_t lno_t;
116  typedef typename Adapter::gno_t gno_t;
117  typedef typename Adapter::scalar_t scalar_t;
118  typedef typename Adapter::part_t part_t;
119  typedef typename Adapter::user_t user_t;
120  typedef typename Adapter::userCoord_t userCoord_t;
121 
132  AlgPuLP(const RCP<const Environment> &env__,
133  const RCP<const Comm<int> > &problemComm__,
134  const RCP<const IdentifierAdapter<user_t> > &adapter__) :
135  env(env__), problemComm(problemComm__), adapter(adapter__)
136  {
137  std::string errStr = "cannot build GraphModel from IdentifierAdapter, ";
138  errStr += "PuLP requires Graph, Matrix, or Mesh Adapter";
139  throw std::runtime_error(errStr);
140  }
141 
142  AlgPuLP(const RCP<const Environment> &env__,
143  const RCP<const Comm<int> > &problemComm__,
144  const RCP<const VectorAdapter<user_t> > &adapter__) :
145  env(env__), problemComm(problemComm__), adapter(adapter__)
146  {
147  std::string errStr = "cannot build GraphModel from VectorAdapter, ";
148  errStr += "PuLP requires Graph, Matrix, or Mesh Adapter";
149  throw std::runtime_error(errStr);
150  }
151 
152  AlgPuLP(const RCP<const Environment> &env__,
153  const RCP<const Comm<int> > &problemComm__,
154  const RCP<const GraphAdapter<user_t,userCoord_t> > &adapter__) :
155  env(env__), problemComm(problemComm__), adapter(adapter__)
156  {
157  modelFlag_t flags;
158  flags.reset();
159 
160  buildModel(flags);
161  }
162 
163  AlgPuLP(const RCP<const Environment> &env__,
164  const RCP<const Comm<int> > &problemComm__,
165  const RCP<const MatrixAdapter<user_t,userCoord_t> > &adapter__) :
166  env(env__), problemComm(problemComm__), adapter(adapter__)
167  {
168  modelFlag_t flags;
169  flags.reset();
170 
171  const ParameterList &pl = env->getParameters();
172  const Teuchos::ParameterEntry *pe;
173 
174  std::string defString("default");
175  std::string objectOfInterest(defString);
176  pe = pl.getEntryPtr("objects_to_partition");
177  if (pe)
178  objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
179 
180  if (objectOfInterest == defString ||
181  objectOfInterest == std::string("matrix_rows") )
182  flags.set(VERTICES_ARE_MATRIX_ROWS);
183  else if (objectOfInterest == std::string("matrix_columns"))
184  flags.set(VERTICES_ARE_MATRIX_COLUMNS);
185  else if (objectOfInterest == std::string("matrix_nonzeros"))
186  flags.set(VERTICES_ARE_MATRIX_NONZEROS);
187 
188  buildModel(flags);
189  }
190 
191  AlgPuLP(const RCP<const Environment> &env__,
192  const RCP<const Comm<int> > &problemComm__,
193  const RCP<const MeshAdapter<user_t> > &adapter__) :
194  env(env__), problemComm(problemComm__), adapter(adapter__)
195  {
196  modelFlag_t flags;
197  flags.reset();
198 
199  const ParameterList &pl = env->getParameters();
200  const Teuchos::ParameterEntry *pe;
201 
202  std::string defString("default");
203  std::string objectOfInterest(defString);
204  pe = pl.getEntryPtr("objects_to_partition");
205  if (pe)
206  objectOfInterest = pe->getValue<std::string>(&objectOfInterest);
207 
208  if (objectOfInterest == defString ||
209  objectOfInterest == std::string("mesh_nodes") )
210  flags.set(VERTICES_ARE_MESH_NODES);
211  else if (objectOfInterest == std::string("mesh_elements"))
212  flags.set(VERTICES_ARE_MESH_ELEMENTS);
213 
214  buildModel(flags);
215  }
216 
219  static void getValidParameters(ParameterList & pl)
220  {
221  pl.set("pulp_vert_imbalance", 1.1, "vertex imbalance tolerance, ratio of "
222  "maximum load over average load",
224 
225  pl.set("pulp_edge_imbalance", 1.1, "edge imbalance tolerance, ratio of "
226  "maximum load over average load",
228 
229  // bool parameter
230  pl.set("pulp_lp_init", false, "perform label propagation-based "
231  "initialization", Environment::getBoolValidator() );
232 
233  // bool parameter
234  pl.set("pulp_minimize_maxcut", false, "perform per-part max cut "
235  "minimization", Environment::getBoolValidator() );
236 
237  // bool parameter
238  pl.set("pulp_verbose", false, "verbose output",
240 
241  // bool parameter
242  pl.set("pulp_do_repart", false, "perform repartitioning",
244 
245  pl.set("pulp_seed", 0, "set pulp seed", Environment::getAnyIntValidator());
246  }
247 
248  void partition(const RCP<PartitioningSolution<Adapter> > &solution);
249 
250 private:
251 
252  void buildModel(modelFlag_t &flags);
253 
254  void scale_weights(size_t n, StridedData<lno_t, scalar_t> &fwgts,
255  int *iwgts);
256 
257  const RCP<const Environment> env;
258  const RCP<const Comm<int> > problemComm;
259  const RCP<const base_adapter_t> adapter;
260  RCP<const GraphModel<base_adapter_t> > model;
261 };
262 
263 
265 template <typename Adapter>
267 {
268  const ParameterList &pl = env->getParameters();
269  const Teuchos::ParameterEntry *pe;
270 
271  std::string defString("default");
272  std::string symParameter(defString);
273  pe = pl.getEntryPtr("symmetrize_graph");
274  if (pe){
275  symParameter = pe->getValue<std::string>(&symParameter);
276  if (symParameter == std::string("transpose"))
277  flags.set(SYMMETRIZE_INPUT_TRANSPOSE);
278  else if (symParameter == std::string("bipartite"))
279  flags.set(SYMMETRIZE_INPUT_BIPARTITE); }
280 
281  bool sgParameter = false;
282  pe = pl.getEntryPtr("subset_graph");
283  if (pe)
284  sgParameter = pe->getValue(&sgParameter);
285  if (sgParameter)
286  flags.set(BUILD_SUBSET_GRAPH);
287 
288  flags.set(REMOVE_SELF_EDGES);
289  flags.set(GENERATE_CONSECUTIVE_IDS);
290 #ifndef HAVE_ZOLTAN2_MPI
291  flags.set(BUILD_LOCAL_GRAPH);
292 #endif
293  this->env->debug(DETAILED_STATUS, " building graph model");
294  this->model = rcp(new GraphModel<base_adapter_t>(this->adapter, this->env,
295  this->problemComm, flags));
296  this->env->debug(DETAILED_STATUS, " graph model built");
297 }
298 
299 /*
300 NOTE:
301  Assumes installed PuLP library is version pulp-0.2
302 */
303 template <typename Adapter>
305  const RCP<PartitioningSolution<Adapter> > &solution
306 )
307 {
308  HELLO;
309 
310  size_t numGlobalParts = solution->getTargetGlobalNumberOfParts();
311 
312  int num_parts = (int)numGlobalParts;
313  //TPL_Traits<int, size_t>::ASSIGN(num_parts, numGlobalParts, env);
314 
315  //#ifdef HAVE_ZOLTAN2_MPI
316  // TODO: XtraPuLP
317 
318  int ierr = 0;
319  int np = problemComm->getSize();
320 
321  // Get number of vertices and edges
322  const size_t modelVerts = model->getLocalNumVertices();
323  const size_t modelEdges = model->getLocalNumEdges();
324  int num_verts = (int)modelVerts;
325  long num_edges = (long)modelEdges;
326  //TPL_Traits<int, size_t>::ASSIGN(num_verts, modelVerts, env);
327  //TPL_Traits<long, size_t>::ASSIGN(num_edges, modelEdges, env);
328 
329  // Get vertex info
330  ArrayView<const gno_t> vtxIDs;
331  ArrayView<StridedData<lno_t, scalar_t> > vwgts;
332  size_t nVtx = model->getVertexList(vtxIDs, vwgts);
333  int nVwgts = model->getNumWeightsPerVertex();
334  if (nVwgts > 1) {
335  std::cerr << "Warning: NumWeightsPerVertex is " << nVwgts
336  << " but PuLP allows only one weight. "
337  << " Zoltan2 will use only the first weight per vertex."
338  << std::endl;
339  }
340 
341  int* vertex_weights = NULL;
342  long vertex_weights_sum = 0;
343  if (nVwgts) {
344  vertex_weights = new int[nVtx];
345  scale_weights(nVtx, vwgts[0], vertex_weights);
346  for (int i = 0; i < num_verts; ++i)
347  vertex_weights_sum += vertex_weights[i];
348  }
349 
350  // Get edge info
351  ArrayView<const gno_t> adjs;
352  ArrayView<const lno_t> offsets;
353  ArrayView<StridedData<lno_t, scalar_t> > ewgts;
354  size_t nEdge = model->getEdgeList(adjs, offsets, ewgts);
355  int nEwgts = model->getNumWeightsPerEdge();
356  if (nEwgts > 1) {
357  std::cerr << "Warning: NumWeightsPerEdge is " << nEwgts
358  << " but PuLP allows only one weight. "
359  << " Zoltan2 will use only the first weight per edge."
360  << std::endl;
361  }
362 
363  int* edge_weights = NULL;
364  if (nEwgts) {
365  edge_weights = new int[nEdge];
366  scale_weights(nEdge, ewgts[0], edge_weights);
367  }
368 
369 #ifndef HAVE_ZOLTAN2_MPI
370  // Create PuLP's graph structure
371  int* out_edges = NULL;
372  long* out_offsets = NULL;
374  TPL_Traits<long, const lno_t>::ASSIGN_ARRAY(&out_offsets, offsets);
375 
376  pulp_graph_t g = {num_verts, num_edges,
377  out_edges, out_offsets,
378  vertex_weights, edge_weights, vertex_weights_sum};
379 
380 #else
381  // Create XtraPuLP's graph structure
382  unsigned long* out_edges = NULL;
383  unsigned long* out_offsets = NULL;
386 
387  const size_t modelVertsGlobal = model->getGlobalNumVertices();
388  const size_t modelEdgesGlobal = model->getGlobalNumEdges();
389  unsigned long num_verts_global = (unsigned long)modelVertsGlobal;
390  unsigned long num_edges_global = (unsigned long)modelEdgesGlobal;
391 
392  unsigned long* global_ids = NULL;
394 
395  ArrayView<size_t> vtxDist;
396  model->getVertexDist(vtxDist);
397  unsigned long* verts_per_rank = new unsigned long[np+1];
398  for (int i = 0; i < np+1; ++i)
399  verts_per_rank[i] = vtxDist[i];
400 
401  dist_graph_t g;
402  create_xtrapulp_dist_graph(&g, num_verts_global, num_edges_global,
403  (unsigned long)num_verts, (unsigned long)num_edges,
404  out_edges, out_offsets, global_ids, verts_per_rank,
405  vertex_weights, edge_weights);
406 #endif
407 
408 
409  // Create array for PuLP to return results in.
410  // Or write directly into solution parts array
411  ArrayRCP<part_t> partList(new part_t[num_verts], 0, num_verts, true);
412  int* parts = NULL;
413  if (num_verts && (sizeof(int) == sizeof(part_t))) {
414  // Can write directly into the solution's memory
415  parts = (int *) partList.getRawPtr();
416  }
417  else {
418  // Can't use solution memory directly; will have to copy later.
419  parts = new int[num_verts];
420  }
421 
422  // TODO
423  // Implement target part sizes
424 
425  // Grab options from parameter list
426  const Teuchos::ParameterList &pl = env->getParameters();
427  const Teuchos::ParameterEntry *pe;
428 
429  // figure out which parts of the algorithm we're going to run
430  // Default to PuLP with BFS init
431  // PuLP - do_edge_min = false, do_maxcut_min = false
432  // PuLP-M - do_edge_bal = true, do_maxcut_min = false
433  // PuLP-MM - do_edge_bal = true/false, do_maxcut_min = true
434  bool do_lp_init = false;
435  bool do_bfs_init = true;
436  bool do_edge_bal = false;
437  bool do_repart = false;
438  bool do_maxcut_min = false;
439  bool verbose_output = false;
440 
441  // Do label propagation initialization instead of bfs?
442  pe = pl.getEntryPtr("pulp_lp_init");
443  if (pe) do_lp_init = pe->getValue(&do_lp_init);
444  if (do_lp_init) do_bfs_init = false;
445 
446  // Now look at additional objective
447  pe = pl.getEntryPtr("pulp_minimize_maxcut");
448  if (pe) {
449  do_maxcut_min = pe->getValue(&do_maxcut_min);
450  // If we're doing the secondary objective,
451  // set the additional constraint as well
452  if (do_maxcut_min) do_edge_bal = true;
453  }
454 
455  pe = pl.getEntryPtr("pulp_do_repart");
456  if (pe) {
457  do_repart = pe->getValue(&do_repart);
458  // Do repartitioning with input parts
459  do_bfs_init = false;
460  do_lp_init = false;
461  // TODO: read in current parts
462  // for (int i = 0; i < num_verts; ++i)
463  // parts[i] = something;
464  }
465 
466  // Now grab vertex and edge imbalances, defaults at 10%
467  double vert_imbalance = 1.1;
468  double edge_imbalance = 1.1;
469 
470  pe = pl.getEntryPtr("pulp_vert_imbalance");
471  if (pe) vert_imbalance = pe->getValue<double>(&vert_imbalance);
472  pe = pl.getEntryPtr("pulp_edge_imbalance");
473  if (pe) {
474  edge_imbalance = pe->getValue<double>(&edge_imbalance);
475  // if manually set edge imbalance, add do_edge_bal flag to true
476  do_edge_bal = 1;
477  }
478 
479  if (vert_imbalance < 1.0)
480  throw std::runtime_error("pulp_vert_imbalance must be '1.0' or greater.");
481  if (edge_imbalance < 1.0)
482  throw std::runtime_error("pulp_edge_imbalance must be '1.0' or greater.");
483 
484  // verbose output?
485  // TODO: fully implement verbose flag throughout PuLP
486  pe = pl.getEntryPtr("pulp_verbose");
487  if (pe) verbose_output = pe->getValue(&verbose_output);
488 
489  // using pulp seed?
490  int pulp_seed = rand();
491  pe = pl.getEntryPtr("pulp_seed");
492  if (pe) pulp_seed = pe->getValue(&pulp_seed);
493 
494  // Create PuLP's partitioning data structure
495  pulp_part_control_t ppc = {vert_imbalance, edge_imbalance,
496  do_lp_init, do_bfs_init, do_repart,
497  do_edge_bal, do_maxcut_min,
498  verbose_output, pulp_seed};
499 
500 
501  if (verbose_output) {
502  printf("procid: %d, n: %i, m: %li, vb: %f, eb: %f, p: %i\n",
503  problemComm->getRank(),
504  num_verts, num_edges, vert_imbalance, edge_imbalance, num_parts);
505  }
506 
507  // Call partitioning; result returned in parts array
508 #ifndef HAVE_ZOLTAN2_MPI
509  ierr = pulp_run(&g, &ppc, parts, num_parts);
510 
511  env->globalInputAssertion(__FILE__, __LINE__, "pulp_run",
512  !ierr, BASIC_ASSERTION, problemComm);
513 #else
514  ierr = xtrapulp_run(&g, &ppc, parts, num_parts);
515  env->globalInputAssertion(__FILE__, __LINE__, "xtrapulp_run",
516  !ierr, BASIC_ASSERTION, problemComm);
517 #endif
518 
519 
520 
521  // Load answer into the solution if necessary
522  if ((sizeof(int) != sizeof(part_t)) || (num_verts == 0)) {
523  for (int i = 0; i < num_verts; i++) partList[i] = parts[i];
524  delete [] parts;
525  }
526 
527  solution->setParts(partList);
528 
529  env->memory("Zoltan2-(Xtra)PuLP: After creating solution");
530 
531  // Clean up copies made due to differing data sizes.
532 #ifndef HAVE_ZOLTAN2_MPI
535 #else
539 #endif
540 
541 
542 //#endif // DO NOT HAVE_MPI
543 }
544 
546 // Scale and round scalar_t weights (typically float or double) to
547 // PuLP int
548 // subject to sum(weights) <= max_wgt_sum.
549 // Scale only if deemed necessary.
550 //
551 // Note that we use ceil() instead of round() to avoid
552 // rounding to zero weights.
553 // Based on Zoltan's scale_round_weights, mode 1
554 template <typename Adapter>
556  size_t n,
558  int *iwgts
559 )
560 {
561  const double INT_EPSILON = 1e-5;
562  const double MAX_NUM = 1e9;
563 
564  int nonint = 0;
565  double sum_wgt = 0.0;
566  double max_wgt = 0.0;
567 
568  // Compute local sums of the weights
569  // Check whether all weights are integers
570  for (size_t i = 0; i < n; i++) {
571  double fw = double(fwgts[i]);
572  if (!nonint){
573  int tmp = (int) floor(fw + .5); /* Nearest int */
574  if (fabs((double)tmp-fw) > INT_EPSILON) {
575  nonint = 1;
576  }
577  }
578  sum_wgt += fw;
579  if (fw > max_wgt) max_wgt = fw;
580  }
581 
582  // Get agreement across processors
583  double gmax_wgt;
584  double ltmp[2], gtmp[2];
585  ltmp[0] = nonint;
586  ltmp[1] = sum_wgt;
587  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_SUM, 2,
588  ltmp, gtmp);
589  Teuchos::reduceAll<int,double>(*problemComm, Teuchos::REDUCE_MAX, 1,
590  &max_wgt, &gmax_wgt);
591  nonint = gtmp[0];
592  sum_wgt = gtmp[1];
593  max_wgt = gmax_wgt;
594 
595  // Scaling needed if weights are not integers or weights'
596  // range is not sufficient
597  double scale = 1.0;
598  if (nonint || (max_wgt <= INT_EPSILON) || (sum_wgt > MAX_NUM)) {
599  /* Calculate scale factor */
600  if (sum_wgt != 0.0) scale = MAX_NUM/sum_wgt;
601  }
602 
603  /* Convert weights to positive integers using the computed scale factor */
604  for (size_t i = 0; i < n; i++)
605  iwgts[i] = (int) ceil(double(fwgts[i])*scale);
606 
607 }
608 
609 
610 } // namespace Zoltan2
611 
612 #endif // HAVE_ZOLTAN2_PULP
613 
615 
616 
617 #endif
618 
619 
620 
621 
622 
623 
use columns as graph vertices
#define HELLO
Zoltan2::BaseAdapter< userTypes_t > base_adapter_t
IdentifierAdapter defines the interface for identifiers.
ignore invalid neighbors
use mesh nodes as vertices
fast typical checks for valid arguments
MatrixAdapter defines the adapter interface for matrices.
Adapter::base_adapter_t base_adapter_t
GraphAdapter defines the interface for graph-based user data.
static RCP< Teuchos::BoolParameterEntryValidator > getBoolValidator()
Exists to make setting up validators less cluttered.
algorithm requires consecutive ids
MeshAdapter defines the interface for mesh input.
std::bitset< NUM_MODEL_FLAGS > modelFlag_t
model must symmetrize input
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&#39;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
A PartitioningSolution is a solution to a partitioning problem.
use nonzeros as graph vertices
Adapter::part_t part_t
VectorAdapter defines the interface for vector input.
The StridedData class manages lists of weights or coordinates.
Algorithm defines the base class for all algorithms.
static RCP< Teuchos::AnyNumberParameterEntryValidator > getAnyIntValidator()
Exists to make setting up validators less cluttered.
virtual void partition(const RCP< PartitioningSolution< Adapter > > &solution)
Partitioning method.
Traits class to handle conversions between gno_t/lno_t and TPL data types (e.g., ParMETIS&#39;s idx_t...
static void getValidParameters(ParameterList &pl)
Set up validators specific to this algorithm.
use mesh elements as vertices
GraphModel defines the interface required for graph models.
static void ASSIGN_ARRAY(first_t **a, ArrayView< second_t > &b)
Defines the GraphModel interface.
AlgPuLP(const RCP< const Environment > &env, const RCP< const Comm< int > > &problemComm, const RCP< const base_adapter_t > &adapter)
A gathering of useful namespace methods.
static void DELETE_ARRAY(first_t **a)
model represents graph within only one rank