Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Zoltan2_AlgRCM.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 
10 #ifndef _ZOLTAN2_ALGRCM_HPP_
11 #define _ZOLTAN2_ALGRCM_HPP_
12 
13 #include <Zoltan2_Algorithm.hpp>
14 #include <Zoltan2_GraphModel.hpp>
16 #include <Zoltan2_Sort.hpp>
17 #include <queue>
18 
19 #include <KokkosKernels_Utils.hpp>
20 #include <KokkosCompat_View.hpp>
21 
25 
26 
27 namespace Zoltan2{
28 
29 template <typename Adapter>
30 class AlgRCM : public Algorithm<Adapter>
31 {
32  private:
33 
34  const RCP<const typename Adapter::base_adapter_t> adapter;
35  const RCP<Teuchos::ParameterList> pl;
36  const RCP<const Teuchos::Comm<int> > comm;
37  RCP<const Environment> env;
38  modelFlag_t graphFlags;
39 
40  public:
41 
42  typedef typename Adapter::lno_t lno_t;
43  typedef typename Adapter::gno_t gno_t;
44  typedef typename Adapter::offset_t offset_t;
45  typedef typename Adapter::scalar_t scalar_t;
46 
48  const RCP<const typename Adapter::base_adapter_t> &adapter__,
49  const RCP<Teuchos::ParameterList> &pl__,
50  const RCP<const Teuchos::Comm<int> > &comm__,
51  RCP<const Environment> &env__,
52  const modelFlag_t &graphFlags__
53  ) : adapter(adapter__), pl(pl__), comm(comm__), env(env__), graphFlags(graphFlags__)
54  {
55  }
56 
57  int globalOrder(const RCP<GlobalOrderingSolution<gno_t> > &/* solution */)
58  {
59  throw std::logic_error("AlgRCM does not yet support global ordering.");
60  }
61 
62  int localOrder(const RCP<LocalOrderingSolution<lno_t> > &solution)
63  {
64  int ierr= 0;
65 
66  HELLO;
67 
68  // Get local graph.
69  ArrayView<const gno_t> edgeIds;
70  ArrayView<const offset_t> offsets;
71  ArrayView<StridedData<lno_t, scalar_t> > wgts;
72 
73  const auto model = rcp(new GraphModel<Adapter>(adapter, env, comm, graphFlags));
74  const size_t nVtx = model->getLocalNumVertices();
75  model->getEdgeList(edgeIds, offsets, wgts);
76  const int numWeightsPerEdge = model->getNumWeightsPerEdge();
77  if (numWeightsPerEdge > 1){
78  throw std::runtime_error("Multiple weights not supported.");
79  }
80 
81 #if 0
82  // Debug
83  cout << "Debug: Local graph from getLocalEdgeList" << endl;
84  cout << "rank " << comm->getRank() << ": nVtx= " << nVtx << endl;
85  cout << "rank " << comm->getRank() << ": edgeIds: " << edgeIds << endl;
86  cout << "rank " << comm->getRank() << ": offsets: " << offsets << endl;
87 #endif
88 
89  bool symmetrize = pl->get("symmetrize", false);
90  using device = Kokkos::Device<Kokkos::Serial, Kokkos::HostSpace>;
91  using local_graph_type = KokkosSparse::StaticCrsGraph<const gno_t, Kokkos::LayoutLeft, Kokkos::HostSpace, void, const offset_t>;
92 
93  typename local_graph_type::row_map_type::non_const_type symRowmap;
94  typename local_graph_type::entries_type::non_const_type symEntries;
95  if (symmetrize) {
96  Kokkos::View<const gno_t *, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> edgeIdsKokkos(edgeIds.data(), edgeIds.size());
97  Kokkos::View<const offset_t *, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> offsetsKokkos(offsets.data(), offsets.size());
98 
99  local_graph_type localGraph(edgeIdsKokkos, offsetsKokkos);
100 
101  KokkosKernels::Impl::symmetrize_graph_symbolic_hashmap<typename local_graph_type::row_map_type, typename local_graph_type::entries_type,
102  typename local_graph_type::row_map_type::non_const_type, typename local_graph_type::entries_type::non_const_type,
103  Kokkos::Serial>(localGraph.numRows(), localGraph.row_map, localGraph.entries, symRowmap, symEntries);
104  edgeIds = Kokkos::Compat::getConstArrayView(symEntries);
105  offsets = Kokkos::Compat::getConstArrayView(symRowmap);
106  }
107 
108  // RCM constructs invPerm, not perm
109  const ArrayRCP<lno_t> invPerm = solution->getPermutationRCP(true);
110  const ArrayRCP<lno_t> tmpPerm(invPerm.size()); //temporary array used in reversing order
111 
112  // Check if there are actually edges to reorder.
113  // If there are not, then just use the natural ordering.
114  if (offsets[nVtx] == 0) {
115  for (size_t i = 0; i < nVtx; ++i) {
116  invPerm[i] = i;
117  }
118  solution->setHaveInverse(true);
119  return 0;
120  }
121 
122  // Set the label of each vertex to invalid.
123  Tpetra::global_size_t INVALID = Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid();
124  for (size_t i = 0; i < nVtx; ++i) {
125  invPerm[i] = INVALID;
126  }
127 
128  // Loop over all connected components.
129  // Do BFS within each component.
130  gno_t root = 0;
131  std::queue<gno_t> Q;
132  size_t count = 0; // CM label, reversed later
133  size_t next = 0; // next unmarked vertex
134  Teuchos::Array<std::pair<gno_t, offset_t> > children; // children and their degrees
135 
136  while (count < nVtx) {
137 
138  // Find suitable root vertex for this component.
139  // First find an unmarked vertex, use to find root in next component.
140  while ((next < nVtx) && (static_cast<Tpetra::global_size_t>(invPerm[next]) != INVALID)) next++;
141 
142  // Select root method. Pseudoperipheral usually gives the best
143  // ordering, but the user may choose a faster method.
144  std::string root_method = pl->get("root_method", "pseudoperipheral");
145  if (root_method == std::string("first"))
146  root = next;
147  else if (root_method == std::string("smallest_degree"))
148  root = findSmallestDegree(next, nVtx, edgeIds, offsets);
149  else if (root_method == std::string("pseudoperipheral"))
150  root = findPseudoPeripheral(next, nVtx, edgeIds, offsets);
151  else {
152  // This should never happen if pl was validated.
153  throw std::runtime_error("invalid root_method");
154  }
155 
156  // Label connected component starting at root
157  Q.push(root);
158  //cout << "Debug: invPerm[" << root << "] = " << count << endl;
159  invPerm[root] = count++;
160  tmpPerm[invPerm[root]] = root;
161 
162  while (Q.size()){
163  // Get a vertex from the queue
164  gno_t v = Q.front();
165  Q.pop();
166  //cout << "Debug: v= " << v << ", offsets[v] = " << offsets[v] << endl;
167 
168  // Add unmarked children to list of pairs, to be added to queue.
169  children.resize(0);
170  for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
171  gno_t child = edgeIds[ptr];
172  if (static_cast<Tpetra::global_size_t>(invPerm[child]) == INVALID){
173  // Not visited yet; add child to list of pairs.
174  std::pair<gno_t,offset_t> newchild;
175  newchild.first = child;
176  newchild.second = offsets[child+1] - offsets[child];
177  children.push_back(newchild);
178  }
179  }
180  // Sort children by increasing degree
181  // TODO: If edge weights, sort children by decreasing weight,
183  zort.sort(children);
184 
185  typename Teuchos::Array<std::pair<gno_t,offset_t> >::iterator it = children.begin ();
186  for ( ; it != children.end(); ++it){
187  // Push children on the queue in sorted order.
188  gno_t child = it->first;
189  invPerm[child] = count++; // Label as we push on Q
190  tmpPerm[invPerm[child]] = child;
191  Q.push(child);
192  //cout << "Debug: invPerm[" << child << "] = " << count << endl;
193  }
194  }
195  }
196 
197  // Old row tmpPerm[i] is now the new row i.
198 
199  // Reverse labels for RCM.
200  bool reverse = true; // TODO: Make parameter
201  if (reverse) {
202  lno_t temp;
203  for (size_t i=0; i < nVtx/2; ++i) {
204  // This effectively does the work of two loops:
205  // 1) for (i=1; i< nVtx/2; ++i)
206  // swap of tmpPerm[i] and tmpPerm[nVtx-1-i]
207  // 2) for (i=0; i < nVtx; ++i)
208  // invPerm[tmpPerm[i]] = i;
209  temp = tmpPerm[i];
210  invPerm[tmpPerm[nVtx-1-i]] = i;
211  invPerm[temp] = nVtx-1-i;
212  }
213 
214  }
215 
216  for (size_t i = 0; i < nVtx; ++i) {
217  if (static_cast<Tpetra::global_size_t>(invPerm[i]) == INVALID) {
218  throw std::runtime_error("An invalid permutation index had been detected in the RCM solution. This can occur if RCM is run on a non-symmetric matrix without setting \"symmetrize\"=\"true\".");
219  }
220  }
221 
222  solution->setHaveInverse(true);
223  return ierr;
224  }
225 
226  private:
227  // Find a smallest degree vertex in component containing v
228  gno_t findSmallestDegree(
229  gno_t v,
230  lno_t nVtx,
231  ArrayView<const gno_t> edgeIds,
232  ArrayView<const offset_t> offsets)
233  {
234  std::queue<gno_t> Q;
235  Teuchos::Array<bool> mark(nVtx);
236 
237  // Do BFS and compute smallest degree as we go
238  offset_t smallestDegree = nVtx;
239  gno_t smallestVertex = 0;
240 
241  // Clear mark array - nothing marked yet
242  for (int i=0; i<nVtx; i++)
243  mark[i] = false;
244 
245  // Start from v
246  Q.push(v);
247  while (Q.size()){
248  // Get first vertex from the queue
249  v = Q.front();
250  Q.pop();
251  // Check degree of v
252  offset_t deg = offsets[v+1] - offsets[v];
253  if (deg < smallestDegree){
254  smallestDegree = deg;
255  smallestVertex = v;
256  }
257  // Add unmarked children to queue
258  for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
259  gno_t child = edgeIds[ptr];
260  if (!mark[child]){
261  mark[child] = true;
262  Q.push(child);
263  }
264  }
265  }
266  return smallestVertex;
267  }
268 
269  // Find a pseudoperipheral vertex in component containing v
270  gno_t findPseudoPeripheral(
271  gno_t v,
272  lno_t nVtx,
273  ArrayView<const gno_t> edgeIds,
274  ArrayView<const offset_t> offsets)
275  {
276  std::queue<gno_t> Q;
277  Teuchos::Array<bool> mark(nVtx);
278 
279  // Do BFS a couple times, pick vertex last visited (furthest away)
280  const int numBFS = 2;
281  for (int bfs=0; bfs<numBFS; bfs++){
282  // Clear mark array - nothing marked yet
283  for (int i=0; i<nVtx; i++)
284  mark[i] = false;
285  // Start from v
286  Q.push(v);
287  while (Q.size()){
288  // Get first vertex from the queue
289  v = Q.front();
290  Q.pop();
291  // Add unmarked children to queue
292  for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
293  gno_t child = edgeIds[ptr];
294  if (!mark[child]){
295  mark[child] = true;
296  Q.push(child);
297  }
298  }
299  }
300  }
301  return v;
302  }
303 
304 };
305 }
306 #endif
#define HELLO
#define INVALID(STR)
int localOrder(const RCP< LocalOrderingSolution< lno_t > > &solution)
Ordering method.
AlgRCM(const RCP< const typename Adapter::base_adapter_t > &adapter__, const RCP< Teuchos::ParameterList > &pl__, const RCP< const Teuchos::Comm< int > > &comm__, RCP< const Environment > &env__, const modelFlag_t &graphFlags__)
std::bitset< NUM_MODEL_FLAGS > modelFlag_t
map_t::global_ordinal_type gno_t
Definition: mapRemotes.cpp:27
Defines the OrderingSolution class.
Adapter::offset_t offset_t
tuple root
Definition: validXML.py:24
Algorithm defines the base class for all algorithms.
map_t::local_ordinal_type lno_t
Definition: mapRemotes.cpp:26
int globalOrder(const RCP< GlobalOrderingSolution< gno_t > > &)
Ordering method.
Sort vector of pairs (key, value) by value.
Adapter::scalar_t scalar_t
GraphModel defines the interface required for graph models.
Tpetra::global_size_t global_size_t
Defines the GraphModel interface.
Adapter::lno_t lno_t
void sort(Teuchos::Array< std::pair< key_t, value_t > > &listofPairs, bool inc=true)
Adapter::gno_t gno_t