MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_ClassicalMapFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef MUELU_CLASSICALMAPFACTORY_DEF_HPP_
11#define MUELU_CLASSICALMAPFACTORY_DEF_HPP_
12
13#include <Teuchos_Array.hpp>
14#include <Teuchos_ArrayRCP.hpp>
15
16#ifdef HAVE_MPI
17#include <Teuchos_DefaultMpiComm.hpp>
18#endif
19
20#include <Xpetra_Vector.hpp>
21#include <Xpetra_StridedMapFactory.hpp>
22#include <Xpetra_VectorFactory.hpp>
23#include <Xpetra_Import.hpp>
24#include <Xpetra_IO.hpp>
25
27#include "MueLu_Level.hpp"
28#include "MueLu_LWGraph.hpp"
29#include "MueLu_MasterList.hpp"
30#include "MueLu_Monitor.hpp"
31
32#include "MueLu_LWGraph.hpp"
33
34#ifdef HAVE_MUELU_ZOLTAN2
36#include <Zoltan2_XpetraCrsGraphAdapter.hpp>
37#include <Zoltan2_ColoringProblem.hpp>
38#include <Zoltan2_ColoringSolution.hpp>
39
40#endif
41
42#include "MueLu_LWGraph_kokkos.hpp"
43#include <KokkosGraph_Distance1ColorHandle.hpp>
44#include <KokkosGraph_Distance1Color.hpp>
45
46namespace MueLu {
47
48template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
50 RCP<ParameterList> validParamList = rcp(new ParameterList());
51#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
52 SET_VALID_ENTRY("aggregation: deterministic");
53 SET_VALID_ENTRY("aggregation: coloring algorithm");
54 SET_VALID_ENTRY("aggregation: coloring: use color graph");
55#undef SET_VALID_ENTRY
56 validParamList->set<RCP<const FactoryBase> >("A", Teuchos::null, "Generating factory of the matrix A");
57 validParamList->set<RCP<const FactoryBase> >("UnAmalgamationInfo", Teuchos::null, "Generating factory of UnAmalgamationInfo");
58 validParamList->set<RCP<const FactoryBase> >("Graph", null, "Generating factory of the graph");
59 validParamList->set<RCP<const FactoryBase> >("Coloring Graph", null, "Generating factory of the graph");
60 validParamList->set<RCP<const FactoryBase> >("DofsPerNode", null, "Generating factory for variable \'DofsPerNode\', usually the same as for \'Graph\'");
61
62 return validParamList;
63}
64
65template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
67 Input(currentLevel, "A");
68 Input(currentLevel, "UnAmalgamationInfo");
69 Input(currentLevel, "Graph");
70
71 const ParameterList& pL = GetParameterList();
72 bool use_color_graph = pL.get<bool>("aggregation: coloring: use color graph");
73 if (use_color_graph)
74 Input(currentLevel, "Coloring Graph");
75}
76
77template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
79 FactoryMonitor m(*this, "Build", currentLevel);
80 const ParameterList& pL = GetParameterList();
81 RCP<const Matrix> A = Get<RCP<Matrix> >(currentLevel, "A");
82
83 RCP<const LWGraph> graph;
84 bool use_color_graph = pL.get<bool>("aggregation: coloring: use color graph");
85 if (use_color_graph)
86 graph = Get<RCP<LWGraph> >(currentLevel, "Coloring Graph");
87 else
88 graph = Get<RCP<LWGraph> >(currentLevel, "Graph");
89
90 /* ============================================================= */
91 /* Phase 1 : Compute an initial MIS */
92 /* ============================================================= */
93 ArrayRCP<LO> myColors;
94 LO numColors = 0;
95
96 RCP<LocalOrdinalVector> fc_splitting;
97 std::string coloringAlgo = pL.get<std::string>("aggregation: coloring algorithm");
98
99 // Switch to Zoltan2 if we're parallel and Tpetra (and not file)
100#ifdef HAVE_MUELU_ZOLTAN2
101 int numProcs = A->getRowMap()->getComm()->getSize();
102 if (coloringAlgo != "file" && numProcs > 1 && graph->GetDomainMap()->lib() == Xpetra::UseTpetra)
103 coloringAlgo = "Zoltan2";
104#endif
105
106 //#define CMS_DUMP
107#ifdef CMS_DUMP
108 {
109 int rank = graph->GetDomainMap()->getComm()->getRank();
110
111 printf("[%d,%d] graph local size = %dx%d\n", rank, currentLevel.GetLevelID(), (int)graph->GetDomainMap()->getLocalNumElements(), (int)graph->GetImportMap()->getLocalNumElements());
112
113 std::ofstream ofs(std::string("m_dropped_graph_") + std::to_string(currentLevel.GetLevelID()) + std::string("_") + std::to_string(rank) + std::string(".dat"), std::ofstream::out);
114 RCP<Teuchos::FancyOStream> fancy = Teuchos::fancyOStream(Teuchos::rcpFromRef(ofs));
115 graph->print(*fancy, Debug);
116 }
117 {
118 A->getRowMap()->getComm()->barrier();
119 }
120
121#endif
122
123 // Switch to MIS if we're in Epetra (and not file)
124 if (coloringAlgo != "file" && graph->GetDomainMap()->lib() == Xpetra::UseEpetra)
125 coloringAlgo = "MIS";
126
127 if (coloringAlgo == "file") {
128 // Read the CF splitting from disk
129 // NOTE: For interoperability reasons, this is dependent on the point_type enum not changing
130 std::string map_file = std::string("map_fcsplitting_") + std::to_string(currentLevel.GetLevelID()) + std::string(".m");
131 std::string color_file = std::string("fcsplitting_") + std::to_string(currentLevel.GetLevelID()) + std::string(".m");
132
133 FILE* mapfile = fopen(map_file.c_str(), "r");
134 using real_type = typename Teuchos::ScalarTraits<SC>::magnitudeType;
135 using RealValuedMultiVector = typename Xpetra::MultiVector<real_type, LO, GO, NO>;
136 RCP<RealValuedMultiVector> mv;
137
138 GetOStream(Statistics1) << "Reading FC splitting from " << color_file << ", using map file " << map_file << ". On rank " << A->getRowMap()->getComm()->getRank() << " local size is " << A->getRowMap()->getLocalNumElements() << std::endl;
139 if (mapfile) {
140 fclose(mapfile);
141 RCP<const Map> colorMap = Xpetra::IO<Scalar, LocalOrdinal, GlobalOrdinal, Node>::ReadMap(map_file, A->getRowMap()->lib(), A->getRowMap()->getComm());
142 TEUCHOS_TEST_FOR_EXCEPTION(!colorMap->isCompatible(*A->getRowMap()), std::invalid_argument, "Coloring on disk has incompatible map with A");
143
144 mv = Xpetra::IO<real_type, LocalOrdinal, GlobalOrdinal, Node>::ReadMultiVector(color_file, colorMap);
145 } else {
146 // Use A's rowmap and hope it matches
147 mv = Xpetra::IO<real_type, LocalOrdinal, GlobalOrdinal, Node>::ReadMultiVector(color_file, A->getRowMap());
148 }
149 TEUCHOS_TEST_FOR_EXCEPTION(mv.is_null(), std::invalid_argument, "Coloring on disk cannot be read");
150 fc_splitting = LocalOrdinalVectorFactory::Build(A->getRowMap());
151 TEUCHOS_TEST_FOR_EXCEPTION(mv->getLocalLength() != fc_splitting->getLocalLength(), std::invalid_argument, "Coloring map mismatch");
152
153 // Overlay the Dirichlet Points (and copy out the rest)
154 auto boundaryNodes = graph->GetBoundaryNodeMap();
155 ArrayRCP<const real_type> mv_data = mv->getData(0);
156 ArrayRCP<LO> fc_data = fc_splitting->getDataNonConst(0);
157 for (LO i = 0; i < (LO)fc_data.size(); i++) {
158 if (boundaryNodes[i])
159 fc_data[i] = DIRICHLET_PT;
160 else
161 fc_data[i] = Teuchos::as<LO>(mv_data[i]);
162 }
163 }
164#ifdef HAVE_MUELU_ZOLTAN2
165 else if (coloringAlgo.find("Zoltan2") != std::string::npos && graph->GetDomainMap()->lib() == Xpetra::UseTpetra) {
166 SubFactoryMonitor sfm(*this, "DistributedGraphColoring", currentLevel);
167 DoDistributedGraphColoring(graph, myColors, numColors);
168 }
169#endif
170 else if (coloringAlgo == "MIS" || graph->GetDomainMap()->lib() == Xpetra::UseTpetra) {
171 SubFactoryMonitor sfm(*this, "MIS", currentLevel);
172 TEUCHOS_TEST_FOR_EXCEPTION(A->getRowMap()->getComm()->getSize() != 1, std::invalid_argument, "MIS on more than 1 MPI rank is not supported");
173 DoMISNaive(*graph, myColors, numColors);
174 } else {
175 SubFactoryMonitor sfm(*this, "GraphColoring", currentLevel);
176 TEUCHOS_TEST_FOR_EXCEPTION(A->getRowMap()->getComm()->getSize() != 1, std::invalid_argument, "KokkosKernels graph coloring on more than 1 MPI rank is not supported");
177 DoGraphColoring(*graph, myColors, numColors);
178 }
179
180#ifdef CMS_DUMP
181 {
182 int rank = graph->GetDomainMap()->getComm()->getRank();
183
184 printf("[%d,%d] num colors %d\n", rank, currentLevel.GetLevelID(), numColors);
185
186 std::ofstream ofs(std::string("m_colors_") + std::to_string(currentLevel.GetLevelID()) + std::string("_") + std::to_string(rank) + std::string(".dat"), std::ofstream::out);
187 RCP<Teuchos::FancyOStream> fancy = Teuchos::fancyOStream(Teuchos::rcpFromRef(ofs));
188 *fancy << myColors();
189 }
190 {
191 A->getRowMap()->getComm()->barrier();
192 }
193
194#endif
195
196 /* ============================================================= */
197 /* Phase 2 : Mark the C-Points */
198 /* ============================================================= */
199 LO num_c_points = 0, num_d_points = 0, num_f_points = 0;
200 if (fc_splitting.is_null()) {
201 // We just have a coloring, so we need to generate a splitting
202 auto boundaryNodes = graph->GetBoundaryNodeMap();
203 fc_splitting = LocalOrdinalVectorFactory::Build(A->getRowMap());
204 ArrayRCP<LO> myPointType = fc_splitting->getDataNonConst(0);
205 for (LO i = 0; i < (LO)myColors.size(); i++) {
206 if (boundaryNodes[i]) {
207 myPointType[i] = DIRICHLET_PT;
208 num_d_points++;
209 } else if ((LO)myColors[i] == 1) {
210 myPointType[i] = C_PT;
211 num_c_points++;
212 } else
213 myPointType[i] = F_PT;
214 }
215 num_f_points = (LO)myColors.size() - num_d_points - num_c_points;
216 } else {
217 // If we read the splitting off disk, we just need to count
218 ArrayRCP<LO> myPointType = fc_splitting->getDataNonConst(0);
219
220 for (LO i = 0; i < (LO)myPointType.size(); i++) {
221 if (myPointType[i] == DIRICHLET_PT)
222 num_d_points++;
223 else if (myPointType[i] == C_PT)
224 num_c_points++;
225 }
226 num_f_points = (LO)myPointType.size() - num_d_points - num_c_points;
227 }
228
229 /* Output statistics on c/f/d points */
230 if (GetVerbLevel() & Statistics1) {
231 // NOTE: We batch the communication here
232 GO l_counts[] = {(GO)num_c_points, (GO)num_f_points, (GO)num_d_points};
233 GO g_counts[3];
234
235 RCP<const Teuchos::Comm<int> > comm = A->getRowMap()->getComm();
236 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, 3, l_counts, g_counts);
237 GetOStream(Statistics1) << "ClassicalMapFactory(" << coloringAlgo << "): C/F/D = " << g_counts[0] << "/" << g_counts[1] << "/" << g_counts[2] << std::endl;
238 }
239
240 /* Generate the Coarse map */
241 RCP<const Map> coarseMap;
242 {
243 SubFactoryMonitor sfm(*this, "Coarse Map", currentLevel);
244 GenerateCoarseMap(*A->getRowMap(), num_c_points, coarseMap);
245 }
246
247 Set(currentLevel, "FC Splitting", fc_splitting);
248 Set(currentLevel, "CoarseMap", coarseMap);
249}
250
251/* ************************************************************************* */
252template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
254 GenerateCoarseMap(const Map& fineMap, LO num_c_points, RCP<const Map>& coarseMap) const {
255 // FIXME: Assumes scalar PDE
256 std::vector<size_t> stridingInfo_(1);
257 stridingInfo_[0] = 1;
258 GO domainGIDOffset = 0;
259
260 coarseMap = StridedMapFactory::Build(fineMap.lib(),
261 Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
262 num_c_points,
263 fineMap.getIndexBase(),
264 stridingInfo_,
265 fineMap.getComm(),
266 domainGIDOffset);
267}
268
269/* ************************************************************************* */
270template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
272 DoGraphColoring(const LWGraph& graph, ArrayRCP<LO>& myColors_out, LO& numColors) const {
273 const ParameterList& pL = GetParameterList();
274 using graph_t = typename LWGraph_kokkos::local_graph_type;
275 using KernelHandle = KokkosKernels::Experimental::
276 KokkosKernelsHandle<typename graph_t::row_map_type::value_type,
277 typename graph_t::entries_type::value_type,
278 typename graph_t::entries_type::value_type,
279 typename graph_t::device_type::execution_space,
280 typename graph_t::device_type::memory_space,
281 typename graph_t::device_type::memory_space>;
282 KernelHandle kh;
283
284 // Leave gc algorithm choice as the default
285 kh.create_graph_coloring_handle();
286
287 // Get the distance-1 graph coloring handle
288 auto coloringHandle = kh.get_graph_coloring_handle();
289
290 // Set the distance-1 coloring algorithm to use
291 if (pL.get<bool>("aggregation: deterministic") == true) {
292 coloringHandle->set_algorithm(KokkosGraph::COLORING_SERIAL);
293 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
294 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "serial") {
295 coloringHandle->set_algorithm(KokkosGraph::COLORING_SERIAL);
296 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
297 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "vertex based") {
298 coloringHandle->set_algorithm(KokkosGraph::COLORING_VB);
299 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based" << std::endl;
300 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "vertex based bit array") {
301 coloringHandle->set_algorithm(KokkosGraph::COLORING_VBBIT);
302 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based bit array" << std::endl;
303 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "vertex based color set") {
304 coloringHandle->set_algorithm(KokkosGraph::COLORING_VBCS);
305 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based color set" << std::endl;
306 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "vertex based deterministic") {
307 coloringHandle->set_algorithm(KokkosGraph::COLORING_VBD);
308 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based deterministic" << std::endl;
309 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "vertex based deterministic bit array") {
310 coloringHandle->set_algorithm(KokkosGraph::COLORING_VBDBIT);
311 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based deterministic bit array" << std::endl;
312 } else if (pL.get<std::string>("aggregation: coloring algorithm") == "edge based") {
313 coloringHandle->set_algorithm(KokkosGraph::COLORING_EB);
314 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: edge based" << std::endl;
315 } else {
316 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unrecognized distance 1 coloring algorithm");
317 }
318
319 // Create device views for graph rowptrs/colinds
320 size_t numRows = graph.GetNodeNumVertices();
321 // auto graphLWK = dynamic_cast<const LWGraph_kokkos*>(&graph);
322 auto graphLW = dynamic_cast<const LWGraph*>(&graph);
323 TEUCHOS_TEST_FOR_EXCEPTION(!graphLW, std::invalid_argument, "Graph is not a LWGraph object");
324 // Run d1 graph coloring
325 // Assume that the graph is symmetric so row map/entries and col map/entries are the same
326
327 // if (graphLWK) {
328 // KokkosGraph::Experimental::graph_color(&kh,
329 // numRows,
330 // numRows, // FIXME: This should be the number of columns
331 // graphLWK->getRowPtrs(),
332 // graphLWK->getEntries(),
333 // true);
334 // } else
335 if (graphLW) {
336 auto rowptrs = graphLW->getRowPtrs();
337 auto entries = graphLW->getEntries();
338 KokkosGraph::Experimental::graph_color(&kh,
339 numRows,
340 numRows, // FIXME: This should be the number of columns
341 rowptrs,
342 entries,
343 true);
344 }
345
346 // Extract the colors and store them in the aggregates
347 auto myColors_d = coloringHandle->get_vertex_colors();
348 numColors = static_cast<LO>(coloringHandle->get_num_colors());
349
350 // Copy back to host
351 auto myColors_h = Kokkos::create_mirror_view(myColors_d);
352 myColors_out.resize(myColors_h.size());
353 Kokkos::View<LO*, Kokkos::LayoutLeft, Kokkos::HostSpace> myColors_v(&myColors_out[0], myColors_h.size());
354 Kokkos::deep_copy(myColors_v, myColors_h);
355
356 // clean up coloring handle
357 kh.destroy_graph_coloring_handle();
358
359} // end DoGraphColoring
360
361/* ************************************************************************* */
362template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
364 DoMISNaive(const LWGraph& graph, ArrayRCP<LO>& myColors, LO& numColors) const {
365 // This is a fall-back routine for when we don't have Kokkos or when it isn't initialized
366 // We just do greedy MIS because this is easy to write.
367
368 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
369 LO MIS = Teuchos::ScalarTraits<LO>::one();
370
371 // FIXME: Not efficient
372 myColors.resize(0);
373 myColors.resize(graph.GetNodeNumVertices(), LO_INVALID);
374 auto boundaryNodes = graph.GetBoundaryNodeMap();
375 LO Nrows = (LO)graph.GetNodeNumVertices();
376
377 for (LO row = 0; row < Nrows; row++) {
378 if (boundaryNodes[row])
379 continue;
380 auto indices = graph.getNeighborVertices(row);
381 bool has_colored_neighbor = false;
382 for (LO j = 0; !has_colored_neighbor && j < (LO)indices.length; j++) {
383 // FIXME: This does not handle ghosting correctly
384 if (myColors[indices(j)] == MIS)
385 has_colored_neighbor = true;
386 }
387 if (!has_colored_neighbor)
388 myColors[row] = MIS;
389 }
390 numColors = 1;
391}
392
393/* ************************************************************************* */
394template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
396 DoDistributedGraphColoring(RCP<const LWGraph>& graph, ArrayRCP<LO>& myColors_out, LO& numColors) const {
397#ifdef HAVE_MUELU_ZOLTAN2
398 // const ParameterList& pL = GetParameterList();
399 Teuchos::ParameterList params;
400 params.set("color_choice", "FirstFit");
401 params.set("color_method", "D1");
402 // params.set("color_choice", colorMethod);
403 // params.set("color_method", colorAlg);
404 // params.set("verbose", verbose);
405 // params.set("serial_threshold",serialThreshold);
406 // params.set("recolor_degrees",recolorDegrees);
407
408 // Do the coloring via Zoltan2
409 using GraphAdapter = MueLuGraphBaseAdapter<LWGraph>;
410 GraphAdapter z_adapter(graph);
411
412 // We need to provide the MPI Comm, or else we wind up using the default (eep!)
413 Zoltan2::ColoringProblem<GraphAdapter> problem(&z_adapter, &params, graph->GetDomainMap()->getComm());
414 problem.solve();
415 Zoltan2::ColoringSolution<GraphAdapter>* soln = problem.getSolution();
416 ArrayRCP<int> colors = soln->getColorsRCP();
417 numColors = (LO)soln->getNumColors();
418
419 // Assign the Array RCP or Copy Out
420 // FIXME: This probably won't work if LO!=int
421 if (std::is_same<LO, int>::value)
422 myColors_out = colors;
423 else {
424 myColors_out.resize(colors.size());
425 for (LO i = 0; i < (LO)myColors_out.size(); i++)
426 myColors_out[i] = (LO)colors[i];
427 }
428
429 /*
430
431 printf("CMS: numColors = %d\ncolors = ",numColors);
432 for(int i=0;i<colors.size(); i++)
433 printf("%d ",colors[i]);
434 printf("\n");
435
436 */
437#endif // ifdef HAVE_MUELU_ZOLTAN2
438}
439
440} // namespace MueLu
441
442#endif /* MUELU_CLASSICALMAPFACTORY_DEF_HPP_ */
#define SET_VALID_ENTRY(name)
RCP< const ParameterList > GetValidParameterList() const override
Return a const parameter list of valid parameters that setParameterList() will accept.
virtual void GenerateCoarseMap(const Map &fineMap, LO num_c_points, Teuchos::RCP< const Map > &coarseMap) const
virtual void DoGraphColoring(const LWGraph &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
void DeclareInput(Level &currentLevel) const override
Specifies the data that this class needs, and the factories that generate that data.
virtual void DoDistributedGraphColoring(RCP< const LWGraph > &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
virtual void DoMISNaive(const LWGraph &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
void Build(Level &currentLevel) const override
Build an object with this factory.
Timer to be used in factories. Similar to Monitor but with additional timers.
typename std::conditional< OnHost, typename local_graph_device_type::HostMirror, local_graph_device_type >::type local_graph_type
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
KOKKOS_INLINE_FUNCTION const boundary_nodes_type GetBoundaryNodeMap() const
Returns map with global ids of boundary nodes.
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex 'v'.
Lightweight MueLu representation of a compressed row storage graph.
Class that holds all level-specific information.
int GetLevelID() const
Return level number.
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
Namespace for MueLu classes and methods.
@ Debug
Print additional debugging information.
@ Statistics1
Print more statistics.