MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_AggregationPhase3Algorithm_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_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_
47 #define MUELU_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_
48 
49 #include <Teuchos_Comm.hpp>
50 #include <Teuchos_CommHelpers.hpp>
51 
52 #include <Xpetra_Vector.hpp>
53 
55 
56 #include "MueLu_Aggregates.hpp"
57 #include "MueLu_Exceptions.hpp"
58 #include "MueLu_LWGraph.hpp"
59 #include "MueLu_Monitor.hpp"
60 
61 namespace MueLu {
62 
63 // Try to stick unaggregated nodes into a neighboring aggregate if they are
64 // not already too big. Otherwise, make a new aggregate
65 template <class LocalOrdinal, class GlobalOrdinal, class Node>
67  Monitor m(*this, "BuildAggregatesNonKokkos");
68 
69  bool makeNonAdjAggs = false;
70  bool error_on_isolated = false;
71  if (params.isParameter("aggregation: error on nodes with no on-rank neighbors"))
72  error_on_isolated = params.get<bool>("aggregation: error on nodes with no on-rank neighbors");
73  if (params.isParameter("aggregation: phase3 avoid singletons"))
74  makeNonAdjAggs = params.get<bool>("aggregation: phase3 avoid singletons");
75 
76  size_t numSingletons = 0;
77 
78  const LO numRows = graph.GetNodeNumVertices();
79  const int myRank = graph.GetComm()->getRank();
80 
81  ArrayRCP<LO> vertex2AggId = aggregates.GetVertex2AggId()->getDataNonConst(0);
82  ArrayRCP<LO> procWinner = aggregates.GetProcWinner()->getDataNonConst(0);
83 
84  LO numLocalAggregates = aggregates.GetNumAggregates();
85 
86  for (LO i = 0; i < numRows; i++) {
87  if (aggStat[i] == AGGREGATED || aggStat[i] == IGNORED)
88  continue;
89 
90  auto neighOfINode = graph.getNeighborVertices(i);
91 
92  // We don't want a singleton. So lets see if there is an unaggregated
93  // neighbor that we can also put with this point.
94  bool isNewAggregate = false;
95  bool failedToAggregate = true;
96  for (int j = 0; j < neighOfINode.length; j++) {
97  LO neigh = neighOfINode(j);
98 
99  if (neigh != i && graph.isLocalNeighborVertex(neigh) && aggStat[neigh] == READY) {
100  isNewAggregate = true;
101 
102  aggStat[neigh] = AGGREGATED;
103  vertex2AggId[neigh] = numLocalAggregates;
104  procWinner[neigh] = myRank;
105 
106  numNonAggregatedNodes--;
107  }
108  }
109 
110  if (isNewAggregate) {
111  // Create new aggregate (not singleton)
112  aggStat[i] = AGGREGATED;
113  procWinner[i] = myRank;
114  numNonAggregatedNodes--;
115  aggregates.SetIsRoot(i);
116  vertex2AggId[i] = numLocalAggregates++;
117 
118  failedToAggregate = false;
119  } else {
120  // We do not want a singleton, but there are no non-aggregated
121  // neighbors. Lets see if we can connect to any other aggregates
122  // NOTE: This is very similar to phase 2b, but simplier: we stop with
123  // the first found aggregate
124  int j = 0;
125  for (; j < neighOfINode.length; j++) {
126  LO neigh = neighOfINode(j);
127 
128  // We don't check (neigh != rootCandidate), as it is covered by checking (aggStat[neigh] == AGGREGATED)
129  if (graph.isLocalNeighborVertex(neigh) && aggStat[neigh] == AGGREGATED)
130  break;
131  }
132 
133  if (j < neighOfINode.length) {
134  // Assign to an adjacent aggregate
135  vertex2AggId[i] = vertex2AggId[neighOfINode(j)];
136  numNonAggregatedNodes--;
137  failedToAggregate = false;
138  }
139  }
140 
141  if (failedToAggregate && makeNonAdjAggs) {
142  // it we are still didn't find an aggregate home for i (i.e., we have
143  // a potential singleton), we are desperate. Basically, we seek to
144  // group i with any other local point to form an aggregate (even if
145  // it is not a neighbor of i. Either we find a vertex that is already
146  // aggregated or not aggregated.
147  // 1) if found vertex is aggregated, then assign i to this aggregate
148  // 2) if found vertex is not aggregated, create new aggregate
149 
150  for (LO ii = 0; ii < numRows; ii++) { // look for anyone else
151  if ((ii != i) && (aggStat[ii] != IGNORED)) {
152  failedToAggregate = false; // found someone so start
153  aggStat[i] = AGGREGATED; // marking i as aggregated
154  procWinner[i] = myRank;
155 
156  if (aggStat[ii] == AGGREGATED)
157  vertex2AggId[i] = vertex2AggId[ii];
158  else {
159  vertex2AggId[i] = numLocalAggregates;
160  vertex2AggId[ii] = numLocalAggregates;
161  aggStat[ii] = AGGREGATED;
162  procWinner[ii] = myRank;
163  numNonAggregatedNodes--; // acounts for ii now being aggregated
164  aggregates.SetIsRoot(i);
165  numLocalAggregates++;
166  }
167  numNonAggregatedNodes--; // accounts for i now being aggregated
168  break;
169  } // if ( (ii != i) && (aggStat[ii] != IGNORED ...
170  } // for (LO ii = 0; ...
171  }
172  if (failedToAggregate) {
173  if (error_on_isolated) {
174  // Error on this isolated node, as the user has requested
175  std::ostringstream oss;
176  oss << "MueLu::AggregationPhase3Algorithm::BuildAggregatesNonKokkos: MueLu has detected a non-Dirichlet node that has no on-rank neighbors and is terminating (by user request). " << std::endl;
177  oss << "If this error is being generated at level 0, this is due to an initial partitioning problem in your matrix." << std::endl;
178  oss << "If this error is being generated at any other level, try turning on repartitioning, which may fix this problem." << std::endl;
179  throw Exceptions::RuntimeError(oss.str());
180  } else {
181  // Create new aggregate (singleton)
182  // this->GetOStream(Warnings1) << "Found singleton: " << i << std::endl;
183  numSingletons++;
184 
185  aggregates.SetIsRoot(i);
186  vertex2AggId[i] = numLocalAggregates++;
187  numNonAggregatedNodes--;
188  }
189  }
190 
191  // One way or another, the node is aggregated (possibly into a singleton)
192  aggStat[i] = AGGREGATED;
193  procWinner[i] = myRank;
194 
195  } // loop over numRows
196 
197  if (numSingletons > 0)
198  this->GetOStream(Runtime0) << " WARNING Rank " << myRank << " singletons :" << numSingletons << " (phase)" << std::endl;
199 
200  // update aggregate object
201  aggregates.SetNumAggregates(numLocalAggregates);
202 }
203 
204 // Try to stick unaggregated nodes into a neighboring aggregate if they are
205 // not already too big. Otherwise, make a new aggregate
206 template <class LocalOrdinal, class GlobalOrdinal, class Node>
209  const LWGraph_kokkos& graph,
210  Aggregates& aggregates,
212  LO& numNonAggregatedNodes) const {
213  // So far we only have the non-deterministic version of the algorithm...
214  if (params.get<bool>("aggregation: deterministic")) {
215  Monitor m(*this, "BuildAggregatesDeterministic");
216  BuildAggregatesRandom(params, graph, aggregates, aggStat, numNonAggregatedNodes);
217  } else {
218  Monitor m(*this, "BuildAggregatesRandom");
219  BuildAggregatesRandom(params, graph, aggregates, aggStat, numNonAggregatedNodes);
220  }
221 }
222 
223 // Try to stick unaggregated nodes into a neighboring aggregate if they are
224 // not already too big. Otherwise, make a new aggregate
225 template <class LocalOrdinal, class GlobalOrdinal, class Node>
228  const LWGraph_kokkos& graph,
229  Aggregates& aggregates,
231  LO& numNonAggregatedNodes) const {
232  using device_type = typename LWGraph_kokkos::device_type;
233  using execution_space = typename LWGraph_kokkos::execution_space;
234 
235  bool error_on_isolated = params.get<bool>("aggregation: error on nodes with no on-rank neighbors");
236  bool makeNonAdjAggs = params.get<bool>("aggregation: phase3 avoid singletons");
237 
238  const LO numRows = graph.GetNodeNumVertices();
239  const int myRank = graph.GetComm()->getRank();
240 
241  auto vertex2AggId = aggregates.GetVertex2AggId()->getDeviceLocalView(Xpetra::Access::ReadWrite);
242  auto procWinner = aggregates.GetProcWinner()->getDeviceLocalView(Xpetra::Access::ReadWrite);
243  auto colors = aggregates.GetGraphColors();
244  const LO numColors = aggregates.GetGraphNumColors();
245 
246  auto lclLWGraph = graph;
247 
248  Kokkos::View<LO, device_type> numAggregates("numAggregates");
249  Kokkos::deep_copy(numAggregates, aggregates.GetNumAggregates());
250 
251  Kokkos::View<unsigned*, device_type> aggStatOld(Kokkos::ViewAllocateWithoutInitializing("Initial aggregation status"), aggStat.extent(0));
252  Kokkos::deep_copy(aggStatOld, aggStat);
253  Kokkos::View<LO, device_type> numNonAggregated("numNonAggregated");
254  Kokkos::deep_copy(numNonAggregated, numNonAggregatedNodes);
255  for (int color = 1; color < numColors + 1; ++color) {
256  Kokkos::parallel_for(
257  "Aggregation Phase 3: aggregates clean-up",
258  Kokkos::RangePolicy<execution_space>(0, numRows),
259  KOKKOS_LAMBDA(const LO nodeIdx) {
260  // Check if node has already been treated?
261  if ((colors(nodeIdx) != color) ||
262  (aggStatOld(nodeIdx) == AGGREGATED) ||
263  (aggStatOld(nodeIdx) == IGNORED)) {
264  return;
265  }
266 
267  // Grab node neighbors
268  auto neighbors = lclLWGraph.getNeighborVertices(nodeIdx);
269  LO neighIdx;
270 
271  // We don't want a singleton.
272  // So lets see if any neighbors can be used to form a new aggregate?
273  bool isNewAggregate = false;
274  for (int neigh = 0; neigh < neighbors.length; ++neigh) {
275  neighIdx = neighbors(neigh);
276 
277  if ((neighIdx != nodeIdx) &&
278  lclLWGraph.isLocalNeighborVertex(neighIdx) &&
279  (aggStatOld(neighIdx) == READY)) {
280  isNewAggregate = true;
281  break;
282  }
283  }
284 
285  // We can form a new non singleton aggregate!
286  if (isNewAggregate) {
287  // If this is the aggregate root
288  // we need to process the nodes in the aggregate
289  const LO aggId = Kokkos::atomic_fetch_add(&numAggregates(), 1);
290  aggStat(nodeIdx) = AGGREGATED;
291  procWinner(nodeIdx, 0) = myRank;
292  vertex2AggId(nodeIdx, 0) = aggId;
293  // aggregates.SetIsRoot(nodeIdx);
294  Kokkos::atomic_decrement(&numNonAggregated());
295  for (int neigh = 0; neigh < neighbors.length; ++neigh) {
296  neighIdx = neighbors(neigh);
297  if ((neighIdx != nodeIdx) &&
298  lclLWGraph.isLocalNeighborVertex(neighIdx) &&
299  (aggStatOld(neighIdx) == READY)) {
300  aggStat(neighIdx) = AGGREGATED;
301  procWinner(neighIdx, 0) = myRank;
302  vertex2AggId(neighIdx, 0) = aggId;
303  Kokkos::atomic_decrement(&numNonAggregated());
304  }
305  }
306  return;
307  }
308 
309  // Getting a little desperate!
310  // Let us try to aggregate into a neighboring aggregate
311  for (int neigh = 0; neigh < neighbors.length; ++neigh) {
312  neighIdx = neighbors(neigh);
313  if (lclLWGraph.isLocalNeighborVertex(neighIdx) &&
314  (aggStatOld(neighIdx) == AGGREGATED)) {
315  aggStat(nodeIdx) = AGGREGATED;
316  procWinner(nodeIdx, 0) = myRank;
317  vertex2AggId(nodeIdx, 0) = vertex2AggId(neighIdx, 0);
318  Kokkos::atomic_decrement(&numNonAggregated());
319  return;
320  }
321  }
322 
323  // Getting quite desperate!
324  // Let us try to make a non contiguous aggregate
325  if (makeNonAdjAggs) {
326  for (LO otherNodeIdx = 0; otherNodeIdx < numRows; ++otherNodeIdx) {
327  if ((otherNodeIdx != nodeIdx) &&
328  (aggStatOld(otherNodeIdx) == AGGREGATED)) {
329  aggStat(nodeIdx) = AGGREGATED;
330  procWinner(nodeIdx, 0) = myRank;
331  vertex2AggId(nodeIdx, 0) = vertex2AggId(otherNodeIdx, 0);
332  Kokkos::atomic_decrement(&numNonAggregated());
333  return;
334  }
335  }
336  }
337 
338  // Total deperation!
339  // Let us make a singleton
340  if (!error_on_isolated) {
341  const LO aggId = Kokkos::atomic_fetch_add(&numAggregates(), 1);
342  aggStat(nodeIdx) = AGGREGATED;
343  procWinner(nodeIdx, 0) = myRank;
344  vertex2AggId(nodeIdx, 0) = aggId;
345  Kokkos::atomic_decrement(&numNonAggregated());
346  }
347  });
348  // LBV on 09/27/19: here we could copy numNonAggregated to host
349  // and check for it to be equal to 0 in which case we can stop
350  // looping over the different colors...
351  Kokkos::deep_copy(aggStatOld, aggStat);
352  } // loop over colors
353 
354  auto numNonAggregated_h = Kokkos::create_mirror_view(numNonAggregated);
355  Kokkos::deep_copy(numNonAggregated_h, numNonAggregated);
356  numNonAggregatedNodes = numNonAggregated_h();
357  if ((error_on_isolated) && (numNonAggregatedNodes > 0)) {
358  // Error on this isolated node, as the user has requested
359  std::ostringstream oss;
360  oss << "MueLu::AggregationPhase3Algorithm::BuildAggregates: MueLu has detected a non-Dirichlet node that has no on-rank neighbors and is terminating (by user request). " << std::endl;
361  oss << "If this error is being generated at level 0, this is due to an initial partitioning problem in your matrix." << std::endl;
362  oss << "If this error is being generated at any other level, try turning on repartitioning, which may fix this problem." << std::endl;
363  throw Exceptions::RuntimeError(oss.str());
364  }
365 
366  // update aggregate object
367  auto numAggregates_h = Kokkos::create_mirror_view(numAggregates);
368  Kokkos::deep_copy(numAggregates_h, numAggregates);
369  aggregates.SetNumAggregates(numAggregates_h());
370 }
371 
372 } // namespace MueLu
373 
374 #endif /* MUELU_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_ */
Kokkos::View< unsigned *, typename LWGraphHostType::device_type > AggStatHostType
Lightweight MueLu representation of a compressed row storage graph.
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
void BuildAggregates(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
T & get(const std::string &name, T def_value)
LocalOrdinal LO
One-liner description of what is happening.
void BuildAggregatesNonKokkos(const ParameterList &params, const LWGraph &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatHostType &aggStat, LO &numNonAggregatedNodes) const
Local aggregation.
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
void SetIsRoot(LO i, bool value=true)
Set root node information.
bool isParameter(const std::string &name) const
LO GetGraphNumColors()
Get the number of colors needed by the distance 2 coloring.
colors_view_type & GetGraphColors()
Get a distance 2 coloring of the underlying graph. The coloring is computed and set during Phase1 of ...
KOKKOS_INLINE_FUNCTION bool isLocalNeighborVertex(LO i) const
Return true if vertex with local id &#39;v&#39; is on current process.
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;.
Timer to be used in non-factories.
Lightweight MueLu representation of a compressed row storage graph.
Exception throws to report errors in the internal logical of the program.
void BuildAggregatesRandom(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
typename std::conditional< OnHost, Kokkos::Device< Kokkos::Serial, Kokkos::HostSpace >, typename Node::device_type >::type device_type
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.