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