MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_AggregationPhase1Algorithm_def.hpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // MueLu: A package for multigrid based preconditioning
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
39 // Jonathan Hu (jhu@sandia.gov)
40 // Andrey Prokopenko (aprokop@sandia.gov)
41 // Ray Tuminaro (rstumin@sandia.gov)
42 //
43 // ***********************************************************************
44 //
45 // @HEADER
46 #ifndef MUELU_AGGREGATIONPHASE1ALGORITHM_DEF_HPP_
47 #define MUELU_AGGREGATIONPHASE1ALGORITHM_DEF_HPP_
48 
49 #include <queue>
50 
51 #include <Teuchos_Comm.hpp>
52 #include <Teuchos_CommHelpers.hpp>
53 
54 #include <Xpetra_Vector.hpp>
55 
57 
58 #include "MueLu_LWGraph.hpp"
59 #include "MueLu_Aggregates.hpp"
60 #include "MueLu_Exceptions.hpp"
61 #include "MueLu_Monitor.hpp"
62 
63 #include "Kokkos_Sort.hpp"
64 #include <Kokkos_ScatterView.hpp>
65 
66 namespace MueLu {
67 
68 template <class LocalOrdinal, class GlobalOrdinal, class Node>
71  LO& numNonAggregatedNodes) const {
72  Monitor m(*this, "BuildAggregatesNonKokkos");
73 
74  std::string orderingStr = params.get<std::string>("aggregation: ordering");
75  int maxNeighAlreadySelected = params.get<int>("aggregation: max selected neighbors");
76  int minNodesPerAggregate = params.get<int>("aggregation: min agg size");
77  int maxNodesPerAggregate = params.get<int>("aggregation: max agg size");
78  bool matchMLBehavior = params.get<bool>("aggregation: match ML phase1");
79 
80  TEUCHOS_TEST_FOR_EXCEPTION(maxNodesPerAggregate < minNodesPerAggregate, Exceptions::RuntimeError,
81  "MueLu::UncoupledAggregationAlgorithm::BuildAggregatesNonKokkos: minNodesPerAggregate must be smaller or equal to MaxNodePerAggregate!");
82 
83  enum {
84  O_NATURAL,
85  O_RANDOM,
86  O_GRAPH
87  } ordering;
88  ordering = O_NATURAL; // initialize variable (fix CID 143665)
89  if (orderingStr == "natural") ordering = O_NATURAL;
90  if (orderingStr == "random") ordering = O_RANDOM;
91  if (orderingStr == "graph") ordering = O_GRAPH;
92 
93  const LO numRows = graph.GetNodeNumVertices();
94  const int myRank = graph.GetComm()->getRank();
95 
96  ArrayRCP<LO> vertex2AggId = aggregates.GetVertex2AggId()->getDataNonConst(0);
97  ArrayRCP<LO> procWinner = aggregates.GetProcWinner()->getDataNonConst(0);
98 
99  LO numLocalAggregates = aggregates.GetNumAggregates();
100 
101  ArrayRCP<LO> randomVector;
102  if (ordering == O_RANDOM) {
103  randomVector = arcp<LO>(numRows);
104  for (LO i = 0; i < numRows; i++)
105  randomVector[i] = i;
106  RandomReorder(randomVector);
107  }
108 
109  int aggIndex = -1;
110  size_t aggSize = 0;
111  std::vector<int> aggList(graph.getLocalMaxNumRowEntries());
112 
113  std::queue<LO> graphOrderQueue;
114 
115  // Main loop over all local rows of graph(A)
116  for (LO i = 0; i < numRows; i++) {
117  // Step 1: pick the next node to aggregate
118  LO rootCandidate = 0;
119  if (ordering == O_NATURAL)
120  rootCandidate = i;
121  else if (ordering == O_RANDOM)
122  rootCandidate = randomVector[i];
123  else if (ordering == O_GRAPH) {
124  if (graphOrderQueue.size() == 0) {
125  // Current queue is empty for "graph" ordering, populate with one READY node
126  for (LO jnode = 0; jnode < numRows; jnode++)
127  if (aggStat[jnode] == READY) {
128  graphOrderQueue.push(jnode);
129  break;
130  }
131  }
132  if (graphOrderQueue.size() == 0) {
133  // There are no more ready nodes, end the phase
134  break;
135  }
136  rootCandidate = graphOrderQueue.front(); // take next node from graph ordering queue
137  graphOrderQueue.pop(); // delete this node in list
138  }
139 
140  if (aggStat[rootCandidate] != READY)
141  continue;
142 
143  // Step 2: build tentative aggregate
144  aggSize = 0;
145  aggList[aggSize++] = rootCandidate;
146 
147  auto neighOfINode = graph.getNeighborVertices(rootCandidate);
148 
149  // If the number of neighbors is less than the minimum number of nodes
150  // per aggregate, we know this is not going to be a valid root, and we
151  // may skip it, but only for "natural" and "random" (for "graph" we still
152  // need to fetch the list of local neighbors to continue)
153  if ((ordering == O_NATURAL || ordering == O_RANDOM) &&
154  neighOfINode.length < minNodesPerAggregate) {
155  continue;
156  }
157 
158  LO numAggregatedNeighbours = 0;
159 
160  for (int j = 0; j < neighOfINode.length; j++) {
161  LO neigh = neighOfINode(j);
162 
163  if (neigh != rootCandidate && graph.isLocalNeighborVertex(neigh)) {
164  if (aggStat[neigh] == READY || aggStat[neigh] == NOTSEL) {
165  // If aggregate size does not exceed max size, add node to the
166  // tentative aggregate
167  // NOTE: We do not exit the loop over all neighbours since we have
168  // still to count all aggregated neighbour nodes for the
169  // aggregation criteria
170  // NOTE: We check here for the maximum aggregation size. If we
171  // would do it below with all the other check too big aggregates
172  // would not be accepted at all.
173  if (aggSize < as<size_t>(maxNodesPerAggregate))
174  aggList[aggSize++] = neigh;
175 
176  } else if (!matchMLBehavior || aggStat[neigh] != IGNORED) {
177  // NOTE: ML checks against BOUNDARY here, but boundary nodes are flagged as IGNORED by
178  // the time we get to Phase 1, so we check IGNORED instead
179  numAggregatedNeighbours++;
180  }
181  }
182  }
183 
184  // Step 3: check if tentative aggregate is acceptable
185  if ((numAggregatedNeighbours <= maxNeighAlreadySelected) && // too many connections to other aggregates
186  (aggSize >= as<size_t>(minNodesPerAggregate))) { // too few nodes in the tentative aggregate
187  // Accept new aggregate
188  // rootCandidate becomes the root of the newly formed aggregate
189  aggregates.SetIsRoot(rootCandidate);
190  aggIndex = numLocalAggregates++;
191 
192  for (size_t k = 0; k < aggSize; k++) {
193  aggStat[aggList[k]] = AGGREGATED;
194  vertex2AggId[aggList[k]] = aggIndex;
195  procWinner[aggList[k]] = myRank;
196  }
197 
198  numNonAggregatedNodes -= aggSize;
199 
200  } else {
201  // Aggregate is not accepted
202  aggStat[rootCandidate] = NOTSEL;
203 
204  // Need this for the "graph" ordering below
205  // The original candidate is always aggList[0]
206  aggSize = 1;
207  }
208 
209  if (ordering == O_GRAPH) {
210  // Add candidates to the list of nodes
211  // NOTE: the code have slightly different meanings depending on context:
212  // - if aggregate was accepted, we add neighbors of neighbors of the original candidate
213  // - if aggregate was not accepted, we add neighbors of the original candidate
214  for (size_t k = 0; k < aggSize; k++) {
215  auto neighOfJNode = graph.getNeighborVertices(aggList[k]);
216 
217  for (int j = 0; j < neighOfJNode.length; j++) {
218  LO neigh = neighOfJNode(j);
219 
220  if (graph.isLocalNeighborVertex(neigh) && aggStat[neigh] == READY)
221  graphOrderQueue.push(neigh);
222  }
223  }
224  }
225  }
226 
227  // Reset all NOTSEL vertices to READY
228  // This simplifies other algorithms
229  for (LO i = 0; i < numRows; i++)
230  if (aggStat[i] == NOTSEL)
231  aggStat[i] = READY;
232 
233  // update aggregate object
234  aggregates.SetNumAggregates(numLocalAggregates);
235 }
236 
237 template <class LocalOrdinal, class GlobalOrdinal, class Node>
239  // TODO: replace int
240  int n = list.size();
241  for (int i = 0; i < n - 1; i++)
242  std::swap(list[i], list[RandomOrdinal(i, n - 1)]);
243 }
244 
245 template <class LocalOrdinal, class GlobalOrdinal, class Node>
247  return min + as<int>((max - min + 1) * (static_cast<double>(std::rand()) / (RAND_MAX + 1.0)));
248 }
249 
250 template <class LocalOrdinal, class GlobalOrdinal, class Node>
253  const LWGraph_kokkos& graph,
254  Aggregates& aggregates,
256  LO& numNonAggregatedNodes) const {
257  int minNodesPerAggregate = params.get<int>("aggregation: min agg size");
258  int maxNodesPerAggregate = params.get<int>("aggregation: max agg size");
259 
260  TEUCHOS_TEST_FOR_EXCEPTION(maxNodesPerAggregate < minNodesPerAggregate,
262  "MueLu::UncoupledAggregationAlgorithm::BuildAggregates: minNodesPerAggregate must be smaller or equal to MaxNodePerAggregate!");
263 
264  // Distance-2 gives less control than serial uncoupled phase 1
265  // no custom row reordering because would require making deep copy
266  // of local matrix entries and permuting it can only enforce
267  // max aggregate size
268  {
269  if (params.get<bool>("aggregation: deterministic")) {
270  Monitor m(*this, "BuildAggregatesDeterministic");
271  BuildAggregatesDeterministic(maxNodesPerAggregate, graph,
272  aggregates, aggStat, numNonAggregatedNodes);
273  } else {
274  Monitor m(*this, "BuildAggregatesRandom");
275  BuildAggregatesRandom(maxNodesPerAggregate, graph,
276  aggregates, aggStat, numNonAggregatedNodes);
277  }
278  }
279 }
280 
281 template <class LocalOrdinal, class GlobalOrdinal, class Node>
283  BuildAggregatesRandom(const LO maxAggSize,
284  const LWGraph_kokkos& graph,
285  Aggregates& aggregates,
287  LO& numNonAggregatedNodes) const {
288  using device_type = typename LWGraph_kokkos::device_type;
289  using execution_space = typename LWGraph_kokkos::execution_space;
290 
291  const LO numRows = graph.GetNodeNumVertices();
292  const int myRank = graph.GetComm()->getRank();
293 
294  // Extract data from aggregates
295  auto vertex2AggId = aggregates.GetVertex2AggId()->getDeviceLocalView(Xpetra::Access::ReadWrite);
296  auto procWinner = aggregates.GetProcWinner()->getDeviceLocalView(Xpetra::Access::ReadWrite);
297  auto colors = aggregates.GetGraphColors();
298 
299  auto lclLWGraph = graph;
300 
301  LO numAggregatedNodes = 0;
302  LO numLocalAggregates = aggregates.GetNumAggregates();
303  Kokkos::View<LO, device_type> aggCount("aggCount");
304  Kokkos::deep_copy(aggCount, numLocalAggregates);
305  Kokkos::parallel_for(
306  "Aggregation Phase 1: initial reduction over color == 1",
307  Kokkos::RangePolicy<LO, execution_space>(0, numRows),
308  KOKKOS_LAMBDA(const LO nodeIdx) {
309  if (colors(nodeIdx) == 1 && aggStat(nodeIdx) == READY) {
310  const LO aggIdx = Kokkos::atomic_fetch_add(&aggCount(), 1);
311  vertex2AggId(nodeIdx, 0) = aggIdx;
312  aggStat(nodeIdx) = AGGREGATED;
313  procWinner(nodeIdx, 0) = myRank;
314  }
315  });
316  // Truely we wish to compute: numAggregatedNodes = aggCount - numLocalAggregates
317  // before updating the value of numLocalAggregates.
318  // But since we also do not want to create a host mirror of aggCount we do some trickery...
319  numAggregatedNodes -= numLocalAggregates;
320  Kokkos::deep_copy(numLocalAggregates, aggCount);
321  numAggregatedNodes += numLocalAggregates;
322 
323  // Compute the initial size of the aggregates.
324  // Note lbv 12-21-17: I am pretty sure that the aggregates will always be of size 1
325  // at this point so we could simplify the code below a lot if this
326  // assumption is correct...
327  Kokkos::View<LO*, device_type> aggSizesView("aggSizes", numLocalAggregates);
328  {
329  // Here there is a possibility that two vertices assigned to two different threads contribute
330  // to the same aggregate if somethings happened before phase 1?
331  auto aggSizesScatterView = Kokkos::Experimental::create_scatter_view(aggSizesView);
332  Kokkos::parallel_for(
333  "Aggregation Phase 1: compute initial aggregates size",
334  Kokkos::RangePolicy<LO, execution_space>(0, numRows),
335  KOKKOS_LAMBDA(const LO nodeIdx) {
336  auto aggSizesScatterViewAccess = aggSizesScatterView.access();
337  if (vertex2AggId(nodeIdx, 0) >= 0)
338  aggSizesScatterViewAccess(vertex2AggId(nodeIdx, 0)) += 1;
339  });
340  Kokkos::Experimental::contribute(aggSizesView, aggSizesScatterView);
341  }
342 
343  LO tmpNumAggregatedNodes = 0;
344  Kokkos::parallel_reduce(
345  "Aggregation Phase 1: main parallel_reduce over aggSizes",
346  Kokkos::RangePolicy<size_t, execution_space>(0, numRows),
347  KOKKOS_LAMBDA(const size_t nodeIdx, LO& lNumAggregatedNodes) {
348  if (colors(nodeIdx) != 1 && (aggStat(nodeIdx) == READY || aggStat(nodeIdx) == NOTSEL)) {
349  // Get neighbors of vertex i and look for local, aggregated,
350  // color 1 neighbor (valid root).
351  auto neighbors = lclLWGraph.getNeighborVertices(nodeIdx);
352  for (LO j = 0; j < neighbors.length; ++j) {
353  auto nei = neighbors.colidx(j);
354  if (lclLWGraph.isLocalNeighborVertex(nei) && colors(nei) == 1 && aggStat(nei) == AGGREGATED) {
355  // This atomic guarentees that any other node trying to
356  // join aggregate agg has the correct size.
357  LO agg = vertex2AggId(nei, 0);
358  const LO aggSize = Kokkos::atomic_fetch_add(&aggSizesView(agg),
359  1);
360  if (aggSize < maxAggSize) {
361  // assign vertex i to aggregate with root j
362  vertex2AggId(nodeIdx, 0) = agg;
363  procWinner(nodeIdx, 0) = myRank;
364  aggStat(nodeIdx) = AGGREGATED;
365  ++lNumAggregatedNodes;
366  break;
367  } else {
368  // Decrement back the value of aggSizesView(agg)
369  Kokkos::atomic_decrement(&aggSizesView(agg));
370  }
371  }
372  }
373  }
374  // if(aggStat(nodeIdx) != AGGREGATED) {
375  // lNumNonAggregatedNodes++;
376  if (aggStat(nodeIdx) == NOTSEL) {
377  aggStat(nodeIdx) = READY;
378  }
379  // }
380  },
381  tmpNumAggregatedNodes);
382  numAggregatedNodes += tmpNumAggregatedNodes;
383  numNonAggregatedNodes -= numAggregatedNodes;
384 
385  // update aggregate object
386  aggregates.SetNumAggregates(numLocalAggregates);
387 }
388 
389 template <class LocalOrdinal, class GlobalOrdinal, class Node>
392  const LWGraph_kokkos& graph,
393  Aggregates& aggregates,
395  LO& numNonAggregatedNodes) const {
396  using device_type = typename LWGraph_kokkos::device_type;
397  using execution_space = typename LWGraph_kokkos::execution_space;
398 
399  const LO numRows = graph.GetNodeNumVertices();
400  const int myRank = graph.GetComm()->getRank();
401 
402  auto vertex2AggId = aggregates.GetVertex2AggId()->getDeviceLocalView(Xpetra::Access::ReadWrite);
403  auto procWinner = aggregates.GetProcWinner()->getDeviceLocalView(Xpetra::Access::ReadWrite);
404  auto colors = aggregates.GetGraphColors();
405 
406  auto lclLWGraph = graph;
407 
408  LO numLocalAggregates = aggregates.GetNumAggregates();
409  Kokkos::View<LO, device_type> numLocalAggregatesView("Num aggregates");
410  {
411  auto h_nla = Kokkos::create_mirror_view(numLocalAggregatesView);
412  h_nla() = numLocalAggregates;
413  Kokkos::deep_copy(numLocalAggregatesView, h_nla);
414  }
415 
416  Kokkos::View<LO*, device_type> newRoots("New root LIDs", numNonAggregatedNodes);
417  Kokkos::View<LO, device_type> numNewRoots("Number of new aggregates of current color");
418  auto h_numNewRoots = Kokkos::create_mirror_view(numNewRoots);
419 
420  // first loop build the set of new roots
421  Kokkos::parallel_for(
422  "Aggregation Phase 1: building list of new roots",
423  Kokkos::RangePolicy<execution_space>(0, numRows),
424  KOKKOS_LAMBDA(const LO i) {
425  if (colors(i) == 1 && aggStat(i) == READY) {
426  // i will become a root
427  newRoots(Kokkos::atomic_fetch_add(&numNewRoots(), 1)) = i;
428  }
429  });
430  Kokkos::deep_copy(h_numNewRoots, numNewRoots);
431  // sort new roots by LID to guarantee determinism in agg IDs
432  Kokkos::sort(newRoots, 0, h_numNewRoots());
433  LO numAggregated = 0;
434  Kokkos::parallel_reduce(
435  "Aggregation Phase 1: aggregating nodes",
436  Kokkos::RangePolicy<execution_space>(0, h_numNewRoots()),
437  KOKKOS_LAMBDA(const LO rootIndex, LO& lnumAggregated) {
438  LO root = newRoots(rootIndex);
439  LO aggID = numLocalAggregatesView() + rootIndex;
440  LO aggSize = 1;
441  vertex2AggId(root, 0) = aggID;
442  procWinner(root, 0) = myRank;
443  aggStat(root) = AGGREGATED;
444  auto neighOfRoot = lclLWGraph.getNeighborVertices(root);
445  for (LO n = 0; n < neighOfRoot.length; n++) {
446  LO neigh = neighOfRoot(n);
447  if (lclLWGraph.isLocalNeighborVertex(neigh) && aggStat(neigh) == READY) {
448  // add neigh to aggregate
449  vertex2AggId(neigh, 0) = aggID;
450  procWinner(neigh, 0) = myRank;
451  aggStat(neigh) = AGGREGATED;
452  aggSize++;
453  if (aggSize == maxAggSize) {
454  // can't add any more nodes
455  break;
456  }
457  }
458  }
459  lnumAggregated += aggSize;
460  },
461  numAggregated);
462  numNonAggregatedNodes -= numAggregated;
463  // update aggregate object
464  aggregates.SetNumAggregates(numLocalAggregates + h_numNewRoots());
465 }
466 
467 } // namespace MueLu
468 
469 #endif /* MUELU_AGGREGATIONPHASE1ALGORITHM_DEF_HPP_ */
Kokkos::View< unsigned *, typename LWGraphHostType::device_type > AggStatHostType
LocalOrdinal RandomOrdinal(LocalOrdinal min, LocalOrdinal max)
void BuildAggregatesRandom(const LO maxAggSize, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
Lightweight MueLu representation of a compressed row storage graph.
void BuildAggregatesNonKokkos(const ParameterList &params, const LWGraph &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatHostType &aggStat, LO &numNonAggregatedNodes) const
Local aggregation.
const RCP< LOVector > & GetProcWinner() const
Returns constant vector that maps local node IDs to owning processor IDs.
Container class for aggregation information.
KOKKOS_INLINE_FUNCTION LO GetNumAggregates() const
T & get(const std::string &name, T def_value)
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
LocalOrdinal LO
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
size_type size() const
void SetIsRoot(LO i, bool value=true)
Set root node information.
KOKKOS_INLINE_FUNCTION size_type getLocalMaxNumRowEntries() const
Returns the maximum number of entries across all rows/columns on this node.
colors_view_type & GetGraphColors()
Get a distance 2 coloring of the underlying graph. The coloring is computed and set during Phase1 of ...
void BuildAggregatesDeterministic(const LO maxAggSize, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
KOKKOS_INLINE_FUNCTION bool isLocalNeighborVertex(LO i) const
Return true if vertex with local id &#39;v&#39; is on current process.
void RandomReorder(ArrayRCP< LO > list) const
Utility to take a list of integers and reorder them randomly (by using a local permutation).
const RCP< LOMultiVector > & GetVertex2AggId() const
Returns constant vector that maps local node IDs to local aggregates IDs.
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex &#39;v&#39;.
void BuildAggregates(const Teuchos::ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
Timer to be used in non-factories.
void RandomReorder(Teuchos::Array< LocalOrdinal > &list)
Lightweight MueLu representation of a compressed row storage graph.
Exception throws to report errors in the internal logical of the program.
typename std::conditional< OnHost, Kokkos::Device< Kokkos::Serial, Kokkos::HostSpace >, typename Node::device_type >::type device_type
int RandomOrdinal(int min, int max) const
Generate a random number in the range [min, max].
const RCP< const Teuchos::Comm< int > > GetComm() const
Kokkos::View< unsigned *, typename LWGraphType::device_type > AggStatType
void SetNumAggregates(LO nAggregates)
Set number of local aggregates on current processor.