Zoltan2
Loading...
Searching...
No Matches
Zoltan2_CommGraphModel.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Zoltan2: A package of combinatorial algorithms for scientific computing
4//
5// Copyright 2012 NTESS and the Zoltan2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
24#ifndef _ZOLTAN2_COMMGRAPHMODEL_HPP_
25#define _ZOLTAN2_COMMGRAPHMODEL_HPP_
26
27#include <Zoltan2_Model.hpp>
37#include <unordered_map>
38
39namespace Zoltan2 {
40
42
54template <typename Adapter>
55class CommGraphModel : public Model<Adapter>
56{
57public:
58
59#ifndef DOXYGEN_SHOULD_SKIP_THIS
60 typedef typename Adapter::scalar_t scalar_t;
61 typedef typename Adapter::gno_t gno_t;
62 typedef typename Adapter::lno_t lno_t;
63 typedef typename Adapter::node_t node_t;
64 typedef typename Adapter::user_t user_t;
65 typedef typename Adapter::userCoord_t userCoord_t;
66 typedef StridedData<lno_t, scalar_t> input_t;
67 typedef typename Adapter::offset_t offset_t;
68#endif
69
72
84 const RCP<const Environment> &/* env */, const RCP<const Comm<int> > &/* comm */,
85 const modelFlag_t &modelflags = modelFlag_t())
86 {
87 throw std::runtime_error("CommGraphModel is not implemented for MatrixAdapter yet.");
88 }
89
91 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm,
92 const modelFlag_t &modelflags = modelFlag_t());
93
94 CommGraphModel(const RCP<const MeshAdapter<user_t> > &/* ia */,
95 const RCP<const Environment> &/* env */, const RCP<const Comm<int> > &/* comm */,
96 const modelFlag_t &modelflags = modelFlag_t())
97 {
98 throw std::runtime_error("CommGraphModel is not implemented for MeshAdapter yet.");
99 }
100
101 CommGraphModel(const RCP<const VectorAdapter<userCoord_t> > &/* ia */,
102 const RCP<const Environment> &/* env */, const RCP<const Comm<int> > &/* comm */,
103 const modelFlag_t &modelflags = modelFlag_t())
104 {
105 throw std::runtime_error("cannot build CommGraphModel from VectorAdapter");
106 }
107
108 CommGraphModel(const RCP<const IdentifierAdapter<user_t> > &/* ia */,
109 const RCP<const Environment> &/* env */, const RCP<const Comm<int> > &/* comm */,
110 const modelFlag_t &modelflags = modelFlag_t())
111 {
112 throw std::runtime_error("cannot build GraphModel from IdentifierAdapter");
113 }
114
117 const RCP<const Comm<int> > getComm() { return comm_; }
118
121 size_t getLocalNumVertices() const { return nLocalVertices_; }
122
125 size_t getGlobalNumVertices() const { return nGlobalVertices_; }
126
130 size_t getLocalNumEdges() const { return nLocalEdges_; }
131
135 size_t getGlobalNumEdges() const { return nGlobalEdges_; }
136
139 int getNumWeightsPerVertex() const { return nWeightsPerVertex_; }
140
143 int getNumWeightsPerEdge() const { return nWeightsPerEdge_; }
144
145
154 ArrayView<const gno_t> &Ids,
155 ArrayView<input_t> &wgts) const
156 {
157 Ids = vGids_.view(0, nLocalVertices_);
158 wgts = vWeights_.view(0, nWeightsPerVertex_);
159 return nLocalVertices_;
160 }
161
162 // Implied Vertex LNOs from getVertexList are used as indices to offsets
163 // array.
164 // Vertex GNOs are returned as neighbors in edgeIds.
165
166 size_t getEdgeList( ArrayView<const gno_t> &edgeIds,
167 ArrayView<const offset_t> &offsets,
168 ArrayView<input_t> &wgts) const
169 {
170 edgeIds = eGids_.view(0, nLocalEdges_);
171 offsets = eOffsets_.view(0, nLocalVertices_+1);
172 wgts = eWeights_.view(0, nWeightsPerEdge_);
173 return nLocalEdges_;
174 }
175
180 inline void getVertexDist(ArrayView<size_t> &vtxdist) const
181 {
182 vtxdist = vtxDist_();
183 if (vtxDist_.size() == 0) {
184 throw std::runtime_error("getVertexDist is available only "
185 "when consecutiveIdsRequired");
186 }
187 }
188
190 // The Model interface.
192
193 size_t getLocalNumObjects() const { return nLocalVertices_; }
194
195 size_t getGlobalNumObjects() const { return nGlobalVertices_; }
196
198 // Migration-related functions.
200
201 int getNumActiveRanks() const { return nActiveRanks_; }
202
203 int getDestinationRank() const { return destRank_; }
204
205 int getStartRank() const { return startRank_; }
206
207 int getEndRank() const { return endRank_; }
208
209private:
210
211 void print(); // For debugging
212 void migrateGraph(); // For debugging
213
214 const RCP<const Environment > env_;
215 const RCP<const Comm<int> > comm_;
216
217 int threshold_; // threshold on #vertices each rank stores post-migration
218 int nActiveRanks_ ; // # ranks for the small graph to be partitioned on
219 int destRank_, startRank_, endRank_;
220
221
222 size_t nLocalVertices_; // # local vertices in built graph
223 size_t nGlobalVertices_; // # global vertices in built graph
224 ArrayRCP<gno_t> vGids_; // vertices of graph built in model;
225 // may be same as adapter's input
226 // or may be renumbered 0 to (N-1).
227
228 int nWeightsPerVertex_;
229 ArrayRCP<input_t> vWeights_;
230
231 // Note: in some cases, size of these arrays
232 // may be larger than nLocalEdges_. So do not use .size().
233 // Use nLocalEdges_, nGlobalEdges_
234
235 size_t nLocalEdges_; // # local edges in built graph
236 size_t nGlobalEdges_; // # global edges in built graph
237 ArrayRCP<gno_t> eGids_; // edges of graph built in model
238 ArrayRCP<offset_t> eOffsets_; // edge offsets build in model
239 // May be same as adapter's input
240 // or may differ
241 // due to renumbering, self-edge
242 // removal, or local graph.
243
244 int nWeightsPerEdge_;
245 ArrayRCP<input_t> eWeights_; // edge weights in built graph
246 // May be same as adapter's input
247 // or may differ due to self-edge
248 // removal, or local graph.
249
250 ArrayRCP<size_t> vtxDist_; // If consecutiveIdsRequired,
251 // vtxDist (as needed by ParMETIS
252 // and Scotch) is also created.
253 // Otherwise, it is Teuchos::null.
254};
255
256
258// GraphModel from GraphAdapter
259template <typename Adapter>
261 const RCP<const GraphAdapter<user_t,userCoord_t> > &bia,
262 const RCP<const Environment> &env,
263 const RCP<const Comm<int> > &comm,
264 const modelFlag_t &/* modelflags */):
265 env_(env),
266 comm_(comm),
267 nLocalVertices_(0),
268 nGlobalVertices_(0),
269 vGids_(),
270 nWeightsPerVertex_(0),
271 vWeights_(),
272 nLocalEdges_(0),
273 nGlobalEdges_(0),
274 eGids_(),
275 eOffsets_(),
276 nWeightsPerEdge_(0),
277 eWeights_(),
278 vtxDist_()
279{
280 int commSize = comm_->getSize();
281
282 // Get XpetraCrsGraphAdapter from GraphAdapter
283 RCP<XpetraCrsGraphAdapter<user_t, userCoord_t>> ia;
284 try{
285 RCP<GraphAdapter<user_t, userCoord_t>> tmp =
286 rcp_const_cast<GraphAdapter<user_t, userCoord_t>>(bia);
287 ia = rcp_dynamic_cast<XpetraCrsGraphAdapter<user_t, userCoord_t>>(tmp);
288 }
290
291 // Get the graph from the input adapter
292 auto inGraph = ia->getXpetraGraph();
293
294 // Get the importer of the graph
295 auto imp = inGraph->getImporter();
296
297 // Identify nbor PIDs and number of entries sent per PID
298 std::map<int,double> exportpidmap;
299 auto exportpids = imp->getExportPIDs();
300 size_t nexportpids = imp->getNumExportIDs();
301 for (size_t i = 0; i < nexportpids; i++) {
302 int k = exportpids[i];
303 if (exportpidmap.find(k) != exportpidmap.end())
304 exportpidmap[k] = exportpidmap[k] + 1.;
305 else
306 exportpidmap[k] = 1.;
307 }
308
309 // Set sizes
310 // There is only one vertex in each rank
311 nLocalVertices_ = 1;
312 nLocalEdges_ = exportpidmap.size();
313
314 // Allocate space
315 vGids_ = arcp(new gno_t[nLocalVertices_],
316 0, nLocalVertices_, true);
317 eGids_ = arcp(new gno_t[nLocalEdges_],
318 0, nLocalEdges_, true);
319 eOffsets_ = arcp(new offset_t[nLocalVertices_+1],
320 0, nLocalVertices_+1, true);
321 scalar_t *wgts2 = new scalar_t [nLocalEdges_];
322
323 // Form the vertices
324 vGids_[0] = comm->getRank();
325
326 // Form the edges
327 size_t ptr = 0;
328 eOffsets_[0] = ptr;
329 for (std::map<int,double>::iterator it = exportpidmap.begin();
330 it != exportpidmap.end(); it++) {
331 eGids_[ptr] = it->first;
332 wgts2[ptr++] = it->second;
333 }
334 eOffsets_[nLocalVertices_] = ptr;
335
336 // Edge weights
337 nWeightsPerEdge_ = 1;
338 input_t *wgts = new input_t [nWeightsPerEdge_];
339 eWeights_ = arcp(wgts, 0, nWeightsPerEdge_, true);
340
341 for (int w=0; w < nWeightsPerEdge_; w++){
342 int stride=0;
343 ArrayRCP<const scalar_t> wgtArray = arcp(wgts2, 0, nLocalEdges_, true);
344 eWeights_[w] = input_t(wgtArray, stride);
345 }
346
347 // Vertex weights
348 nWeightsPerVertex_ = 1;
349 input_t *weightInfo = new input_t [nWeightsPerVertex_];
350
351 for (int idx=0; idx < nWeightsPerVertex_; idx++){
352 scalar_t *wgt = new scalar_t [nLocalVertices_];
353 wgt[0] = inGraph->getLocalNumEntries();
354 ArrayRCP<const scalar_t> wgtArray = arcp(wgt, 0, nLocalVertices_, true);
355 weightInfo[idx] = input_t(wgtArray, 1);
356 }
357
358 vWeights_ = arcp<input_t>(weightInfo, 0, nWeightsPerVertex_, true);
359
360 reduceAll<int, size_t>(*comm_, Teuchos::REDUCE_SUM, 1,
361 &nLocalVertices_, &nGlobalVertices_);
362 reduceAll<int, size_t>(*comm_, Teuchos::REDUCE_SUM, 1,
363 &nLocalEdges_, &nGlobalEdges_);
364
365 // Build vtxDist_ array starting with vGid on each rank
366 vtxDist_ = arcp(new size_t[commSize+1], 0, commSize+1, true);
367 vtxDist_[0] = 0;
368 Teuchos::gatherAll(*comm_, 1, &nLocalVertices_, commSize, &vtxDist_[1]);
369 for (int i = 0; i < commSize; i++)
370 vtxDist_[i+1] += vtxDist_[i];
371
372 // Migrate the quotient graph into smaller number of MPI ranks (active ranks)
373 migrateGraph();
374}
375
376template <typename Adapter>
378{
379
380 // Set default threshold for migration
381 threshold_ = 1024;
382
383 // Check if the user set the threshold value
384 const ParameterList &pl = env_->getParameters();
385 const Teuchos::ParameterEntry *pe = pl.getEntryPtr("quotient_threshold");
386 if (pe)
387 threshold_ = pe->getValue<int>(&threshold_);
388
389 // Compute the sizes of/in the new distribution
390 nActiveRanks_ = std::ceil((double) nGlobalVertices_ / threshold_);
391 size_t avgVertexShare = nGlobalVertices_ / nActiveRanks_;
392 size_t myVertexShare = 0;
393
394 int me = comm_->getRank();
395 int commSize = comm_->getSize();
396
397 // Save the original pointers
398 ArrayRCP<offset_t> old_eOffsets_ = eOffsets_;
399 ArrayRCP<gno_t> old_eGids_ = eGids_;
400 size_t old_nLocalEdges_ = nLocalEdges_;
401 ArrayRCP<input_t> old_vWeights_ = vWeights_;
402 ArrayRCP<input_t> old_eWeights_ = eWeights_;
403
404 // Compute whom to send to
405 destRank_ = me / (int) avgVertexShare;
406 if(destRank_ >= nActiveRanks_)
407 destRank_ = nActiveRanks_ - 1;
408
409 // Start with sending the size of the edge list
410 RCP<CommRequest<int>> *requests;
411 if(me < nActiveRanks_) {
412
413 // Determine the range of ranks to receive edges from
414 // Needs to be updated when chunks are introduced
415 startRank_ = me * static_cast<int>(avgVertexShare);
416 endRank_ = (me+1) * static_cast<int>(avgVertexShare);
417 if(me == nActiveRanks_ - 1 ) // Last rank gets the surplus
418 endRank_ = static_cast<int>(nGlobalVertices_);
419 myVertexShare = endRank_ - startRank_;
420
421 eOffsets_ = arcp(new offset_t[myVertexShare+1], 0, myVertexShare+1, true);
422 eOffsets_[0] = 0;
423
424 // Receive the sizes of their edge list
425 requests = new RCP<CommRequest<int>>[myVertexShare];
426 for(int i = startRank_; i < endRank_; i++) {
427 requests[i-startRank_] = Teuchos::ireceive<int, offset_t>(*comm_,
428 arcp(&eOffsets_[i-startRank_+1], 0, 1, false),
429 i);
430 }
431
432 // Send adjacency size even though this rank will remain active
433 Teuchos::send<int, offset_t>(*comm_, 1, &old_eOffsets_[nLocalVertices_], destRank_);
434
435 // Wait
436 Teuchos::waitAll<int>(*comm_, Teuchos::arrayView(requests, myVertexShare));
437
438 // Prefix sum over the offsets
439 for(size_t i = 1; i <= myVertexShare; i++)
440 eOffsets_[i] += eOffsets_[i-1];
441
442 // Recompute the number of local edges
443 nLocalEdges_ = eOffsets_[myVertexShare];
444
445 // Reallocate the adjacency array
446 eGids_ = arcp(new gno_t[nLocalEdges_], 0, nLocalEdges_, true);
447
448
449 // Receive the adjacency lists
450 for(int i = startRank_; i < endRank_; i++) {
451 offset_t adjStartRank_ = eOffsets_[i-startRank_];
452 offset_t adjSize = eOffsets_[i-startRank_+1] - adjStartRank_;
453 requests[i-startRank_] = Teuchos::ireceive<int, gno_t>(*comm_,
454 arcp(&eGids_[adjStartRank_], 0, adjSize, false),
455 i);
456 }
457
458 // Send adjacency even though this rank will remain active
459 Teuchos::send<int, gno_t>(*comm_, old_nLocalEdges_, &old_eGids_[0], destRank_);
460 Teuchos::waitAll<int>(*comm_, Teuchos::arrayView(requests, myVertexShare));
461
462
463 // Migrate vertex weights arrays
464 scalar_t *wgts = new scalar_t [myVertexShare];
465 for(int i = startRank_; i < endRank_; i++) {
466 requests[i-startRank_] = Teuchos::ireceive<int, scalar_t>(*comm_,
467 arcp(&wgts[i-startRank_], 0, 1, false), // assumes one vertex per rank
468 i);
469 }
470
471 const scalar_t *wPtr;
472 size_t wLen = 0;
473 int stride = 0;
474 old_vWeights_[0].getStridedList(wLen, wPtr, stride);
475 Teuchos::send<int, scalar_t>(*comm_, nLocalVertices_, wPtr, destRank_);
476
477 Teuchos::waitAll<int>(*comm_, Teuchos::arrayView(requests, myVertexShare));
478
479 input_t *weightInfo = new input_t [nWeightsPerVertex_];
480 for (int idx=0; idx < nWeightsPerVertex_; idx++){
481 ArrayRCP<const scalar_t> wgtArray = arcp(wgts, 0, myVertexShare, true);
482 weightInfo[idx] = input_t(wgtArray, 1);
483 }
484 vWeights_ = arcp<input_t>(weightInfo, 0, nWeightsPerVertex_, true);
485
486 // Migrate edge weights arrays
487 scalar_t *ewgts = new scalar_t [nLocalEdges_];
488 for(int i = startRank_; i < endRank_; i++) {
489 offset_t adjStartRank_ = eOffsets_[i-startRank_];
490 offset_t adjSize = eOffsets_[i-startRank_+1] - adjStartRank_;
491 requests[i-startRank_] = Teuchos::ireceive<int, scalar_t>(*comm_,
492 arcp(&ewgts[adjStartRank_], 0, adjSize, false), // assumes one vertex per rank
493 i);
494 }
495
496 old_eWeights_[0].getStridedList(wLen, wPtr, stride);
497 Teuchos::send<int, scalar_t>(*comm_, old_nLocalEdges_, wPtr, destRank_);
498
499 Teuchos::waitAll<int>(*comm_, Teuchos::arrayView(requests, myVertexShare));
500
501 input_t *eweightInfo = new input_t [nWeightsPerEdge_];
502 for (int idx=0; idx < nWeightsPerEdge_; idx++){
503 ArrayRCP<const scalar_t> ewgtArray = arcp(ewgts, 0, nLocalEdges_, true);
504 eweightInfo[idx] = input_t(ewgtArray, 1);
505 }
506 eWeights_ = arcp<input_t>(eweightInfo, 0, nWeightsPerEdge_, true);
507
508
509 // Finalize the migration
510 vGids_ = arcp(new gno_t[myVertexShare], 0, myVertexShare, true);
511 for(int i = startRank_; i < endRank_; i++)
512 vGids_[i-startRank_] = i;
513
514 nLocalVertices_ = myVertexShare;
515
516
517 }
518 else {
519
520 // Send adjacency size
521 Teuchos::send<int, offset_t>(*comm_, 1, &eOffsets_[nLocalVertices_], destRank_);
522
523 // Send adjacency list
524 Teuchos::send<int, gno_t>(*comm_, nLocalEdges_, &eGids_[0], destRank_);
525
526 // Send vertex weights list
527 const scalar_t *wPtr;
528 size_t wLen = 0;
529 int stride = 0;
530 vWeights_[0].getStridedList(wLen, wPtr, stride);
531 Teuchos::send<int, scalar_t>(*comm_, nLocalVertices_, wPtr, destRank_);
532
533 // Send edge weights list
534 eWeights_[0].getStridedList(wLen, wPtr, stride);
535 Teuchos::send<int, scalar_t>(*comm_, nLocalEdges_, wPtr, destRank_);
536
537 nLocalVertices_ = 0;
538 }
539
540 for (int i = 0; i <= commSize; i++)
541 vtxDist_[i] = 0;
542
543 Teuchos::gatherAll(*comm_, 1, &nLocalVertices_, commSize, &vtxDist_[1]);
544 for (int i = 0; i < commSize; i++)
545 vtxDist_[i+1] += vtxDist_[i];
546
547}
548
550template <typename Adapter>
551void CommGraphModel<Adapter>::print()
552{
553 std::ostream *os = env_->getDebugOStream();
554
555 int me = comm_->getRank();
556
557 *os << me
558 << " Nvtx " << nLocalVertices_
559 << " Nedge " << nLocalEdges_
560 << " NVWgt " << nWeightsPerVertex_
561 << " NEWgt " << nWeightsPerEdge_
562 << std::endl;
563
564 for (size_t i = 0; i < nLocalVertices_; i++) {
565 *os << me << " " << i << " GID " << vGids_[i] << ": ";
566 for (offset_t j = eOffsets_[i]; j < eOffsets_[i+1]; j++)
567 *os << eGids_[j] << " " ;
568 *os << std::endl;
569 }
570
571 if (nWeightsPerVertex_) {
572 for (size_t i = 0; i < nLocalVertices_; i++) {
573 *os << me << " " << i << " VWGTS " << vGids_[i] << ": ";
574 for (int j = 0; j < nWeightsPerVertex_; j++)
575 *os << vWeights_[j][i] << " ";
576 *os << std::endl;
577 }
578 }
579
580 if (nWeightsPerEdge_) {
581 for (size_t i = 0; i < nLocalVertices_; i++) {
582 *os << me << " " << i << " EWGTS " << vGids_[i] << ": ";
583 for (offset_t j = eOffsets_[i]; j < eOffsets_[i+1]; j++) {
584 *os << eGids_[j] << " (";
585 for (int w = 0; w < nWeightsPerEdge_; w++)
586 *os << eWeights_[w][j] << " ";
587 *os << ") ";
588 }
589 *os << std::endl;
590 }
591 }
592
593}
594
595} // namespace Zoltan2
596
597
598#endif
599
Zoltan2::BasicUserTypes< zscalar_t, zlno_t, zgno_t > user_t
Definition Metric.cpp:39
typename Zoltan2::InputTraits< ztcrsmatrix_t >::node_t node_t
#define Z2_FORWARD_EXCEPTIONS
Forward an exception back through call stack.
Defines the GraphAdapter interface.
Defines the IdentifierAdapter interface.
Traits for application input objects.
Defines the MatrixAdapter interface.
Defines the MeshAdapter interface.
Defines helper functions for use in the models.
Defines the Model interface.
This file defines the StridedData class.
Defines the VectorAdapter interface.
Defines XpetraCrsGraphAdapter class.
CommGraphModel defines the interface required for communication graph.
size_t getVertexList(ArrayView< const gno_t > &Ids, ArrayView< input_t > &wgts) const
Sets pointers to this process' vertex Ids and their weights.
size_t getGlobalNumEdges() const
Returns the global number edges. For local graphs, the number of global edges is the number of local ...
size_t getGlobalNumObjects() const
Return the global number of objects.
size_t getLocalNumEdges() const
Returns the number of edges on this process. In global or subset graphs, includes off-process edges.
size_t getLocalNumVertices() const
Returns the number vertices on this process.
int getNumWeightsPerEdge() const
Returns the number (0 or greater) of weights per edge.
CommGraphModel(const RCP< const MatrixAdapter< user_t, userCoord_t > > &, const RCP< const Environment > &, const RCP< const Comm< int > > &, const modelFlag_t &modelflags=modelFlag_t())
Constructor.
CommGraphModel(const RCP< const MeshAdapter< user_t > > &, const RCP< const Environment > &, const RCP< const Comm< int > > &, const modelFlag_t &modelflags=modelFlag_t())
void getVertexDist(ArrayView< size_t > &vtxdist) const
Return the vtxDist array Array of size comm->getSize() + 1 Array[n+1] - Array[n] is number of vertice...
const RCP< const Comm< int > > getComm()
Return the communicator used by the model.
CommGraphModel(const RCP< const VectorAdapter< userCoord_t > > &, const RCP< const Environment > &, const RCP< const Comm< int > > &, const modelFlag_t &modelflags=modelFlag_t())
CommGraphModel(const RCP< const IdentifierAdapter< user_t > > &, const RCP< const Environment > &, const RCP< const Comm< int > > &, const modelFlag_t &modelflags=modelFlag_t())
size_t getGlobalNumVertices() const
Returns the global number vertices.
int getNumWeightsPerVertex() const
Returns the number (0 or greater) of weights per vertex.
size_t getLocalNumObjects() const
Return the local number of objects.
size_t getEdgeList(ArrayView< const gno_t > &edgeIds, ArrayView< const offset_t > &offsets, ArrayView< input_t > &wgts) const
GraphAdapter defines the interface for graph-based user data.
IdentifierAdapter defines the interface for identifiers.
MatrixAdapter defines the adapter interface for matrices.
MeshAdapter defines the interface for mesh input.
The base class for all model classes.
The StridedData class manages lists of weights or coordinates.
VectorAdapter defines the interface for vector input.
map_t::local_ordinal_type lno_t
map_t::global_ordinal_type gno_t
Created by mbenlioglu on Aug 31, 2020.
std::bitset< NUM_MODEL_FLAGS > modelFlag_t