MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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
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 
46 namespace MueLu {
47 
48 template <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 
65 template <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 
77 template <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>;
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 
145  } else {
146  // Use A's rowmap and hope it matches
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 /* ************************************************************************* */
252 template <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(),
262  num_c_points,
263  fineMap.getIndexBase(),
264  stridingInfo_,
265  fineMap.getComm(),
266  domainGIDOffset);
267 }
268 
269 /* ************************************************************************* */
270 template <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 /* ************************************************************************* */
362 template <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 
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 /* ************************************************************************* */
394 template <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_ */
void Build(Level &currentLevel) const override
Build an object with this factory.
#define SET_VALID_ENTRY(name)
typename std::conditional< OnHost, typename local_graph_device_type::HostMirror, local_graph_device_type >::type local_graph_type
GlobalOrdinal GO
T & get(const std::string &name, T def_value)
void DeclareInput(Level &currentLevel) const override
Specifies the data that this class needs, and the factories that generate that data.
Timer to be used in factories. Similar to Monitor but with additional timers.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
virtual void DoDistributedGraphColoring(RCP< const LWGraph > &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
Print more statistics.
Print additional debugging information.
LocalOrdinal LO
ParameterList & set(std::string const &name, T &&value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
virtual void DoMISNaive(const LWGraph &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
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.
size_type size() const
void resize(const size_type n, const T &val=T())
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Class that holds all level-specific information.
Definition: MueLu_Level.hpp:63
static RCP< const Map > ReadMap(const std::string &fileName, Xpetra::UnderlyingLib lib, const RCP< const Teuchos::Comm< int > > &comm)
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static RCP< MultiVector > ReadMultiVector(const std::string &fileName, const RCP< const Map > &map)
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex &#39;v&#39;.
virtual void DoGraphColoring(const LWGraph &graph, Teuchos::ArrayRCP< LO > &myColors, LO &numColors) const
virtual void GenerateCoarseMap(const Map &fineMap, LO num_c_points, Teuchos::RCP< const Map > &coarseMap) const
const RCP< const Map > GetDomainMap() const
Lightweight MueLu representation of a compressed row storage graph.
RCP< const ParameterList > GetValidParameterList() const override
Return a const parameter list of valid parameters that setParameterList() will accept.
bool is_null() const