Zoltan2
Loading...
Searching...
No Matches
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>
16#include <Zoltan2_Sort.hpp>
17#include <queue>
18
19#include <KokkosKernels_Utils.hpp>
20#include <KokkosCompat_View.hpp>
21
25
26
27namespace Zoltan2{
28
29template <typename Adapter>
30class 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 local_graph_type = KokkosSparse::StaticCrsGraph<const gno_t, Kokkos::LayoutLeft, Kokkos::HostSpace, void, const offset_t>;
91
92 typename local_graph_type::row_map_type::non_const_type symRowmap;
93 typename local_graph_type::entries_type::non_const_type symEntries;
94 if (symmetrize) {
95 Kokkos::View<const gno_t *, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> edgeIdsKokkos(edgeIds.data(), edgeIds.size());
96 Kokkos::View<const offset_t *, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>> offsetsKokkos(offsets.data(), offsets.size());
97
98 local_graph_type localGraph(edgeIdsKokkos, offsetsKokkos);
99
100 KokkosKernels::Impl::symmetrize_graph_symbolic_hashmap<typename local_graph_type::row_map_type, typename local_graph_type::entries_type,
101 typename local_graph_type::row_map_type::non_const_type, typename local_graph_type::entries_type::non_const_type,
102 Kokkos::Serial>(localGraph.numRows(), localGraph.row_map, localGraph.entries, symRowmap, symEntries);
103 edgeIds = Kokkos::Compat::getConstArrayView(symEntries);
104 offsets = Kokkos::Compat::getConstArrayView(symRowmap);
105 }
106
107 // RCM constructs invPerm, not perm
108 const ArrayRCP<lno_t> invPerm = solution->getPermutationRCP(true);
109 const ArrayRCP<lno_t> tmpPerm(invPerm.size()); //temporary array used in reversing order
110
111 // Check if there are actually edges to reorder.
112 // If there are not, then just use the natural ordering.
113 if (offsets[nVtx] == 0) {
114 for (size_t i = 0; i < nVtx; ++i) {
115 invPerm[i] = i;
116 }
117 solution->setHaveInverse(true);
118 return 0;
119 }
120
121 // Set the label of each vertex to invalid.
122 Tpetra::global_size_t INVALID = Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid();
123 for (size_t i = 0; i < nVtx; ++i) {
124 invPerm[i] = INVALID;
125 }
126
127 // Loop over all connected components.
128 // Do BFS within each component.
129 gno_t root = 0;
130 std::queue<gno_t> Q;
131 size_t count = 0; // CM label, reversed later
132 size_t next = 0; // next unmarked vertex
133 Teuchos::Array<std::pair<gno_t, offset_t> > children; // children and their degrees
134
135 while (count < nVtx) {
136
137 // Find suitable root vertex for this component.
138 // First find an unmarked vertex, use to find root in next component.
139 while ((next < nVtx) && (static_cast<Tpetra::global_size_t>(invPerm[next]) != INVALID)) next++;
140
141 // Select root method. Pseudoperipheral usually gives the best
142 // ordering, but the user may choose a faster method.
143 std::string root_method = pl->get("root_method", "pseudoperipheral");
144 if (root_method == std::string("first"))
145 root = next;
146 else if (root_method == std::string("smallest_degree"))
147 root = findSmallestDegree(next, nVtx, edgeIds, offsets);
148 else if (root_method == std::string("pseudoperipheral"))
149 root = findPseudoPeripheral(next, nVtx, edgeIds, offsets);
150 else {
151 // This should never happen if pl was validated.
152 throw std::runtime_error("invalid root_method");
153 }
154
155 // Label connected component starting at root
156 Q.push(root);
157 //cout << "Debug: invPerm[" << root << "] = " << count << endl;
158 invPerm[root] = count++;
159 tmpPerm[invPerm[root]] = root;
160
161 while (Q.size()){
162 // Get a vertex from the queue
163 gno_t v = Q.front();
164 Q.pop();
165 //cout << "Debug: v= " << v << ", offsets[v] = " << offsets[v] << endl;
166
167 // Add unmarked children to list of pairs, to be added to queue.
168 children.resize(0);
169 for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
170 gno_t child = edgeIds[ptr];
171 if (static_cast<Tpetra::global_size_t>(invPerm[child]) == INVALID){
172 // Not visited yet; add child to list of pairs.
173 std::pair<gno_t,offset_t> newchild;
174 newchild.first = child;
175 newchild.second = offsets[child+1] - offsets[child];
176 children.push_back(newchild);
177 }
178 }
179 // Sort children by increasing degree
180 // TODO: If edge weights, sort children by decreasing weight,
182 zort.sort(children);
183
184 typename Teuchos::Array<std::pair<gno_t,offset_t> >::iterator it = children.begin ();
185 for ( ; it != children.end(); ++it){
186 // Push children on the queue in sorted order.
187 gno_t child = it->first;
188 invPerm[child] = count++; // Label as we push on Q
189 tmpPerm[invPerm[child]] = child;
190 Q.push(child);
191 //cout << "Debug: invPerm[" << child << "] = " << count << endl;
192 }
193 }
194 }
195
196 // Old row tmpPerm[i] is now the new row i.
197
198 // Reverse labels for RCM.
199 bool reverse = true; // TODO: Make parameter
200 if (reverse) {
201 lno_t temp;
202 for (size_t i=0; i < nVtx/2; ++i) {
203 // This effectively does the work of two loops:
204 // 1) for (i=1; i< nVtx/2; ++i)
205 // swap of tmpPerm[i] and tmpPerm[nVtx-1-i]
206 // 2) for (i=0; i < nVtx; ++i)
207 // invPerm[tmpPerm[i]] = i;
208 temp = tmpPerm[i];
209 invPerm[tmpPerm[nVtx-1-i]] = i;
210 invPerm[temp] = nVtx-1-i;
211 }
212
213 }
214
215 for (size_t i = 0; i < nVtx; ++i) {
216 if (static_cast<Tpetra::global_size_t>(invPerm[i]) == INVALID) {
217 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\".");
218 }
219 }
220
221 solution->setHaveInverse(true);
222 return ierr;
223 }
224
225 private:
226 // Find a smallest degree vertex in component containing v
227 gno_t findSmallestDegree(
228 gno_t v,
229 lno_t nVtx,
230 ArrayView<const gno_t> edgeIds,
231 ArrayView<const offset_t> offsets)
232 {
233 std::queue<gno_t> Q;
234 Teuchos::Array<bool> mark(nVtx);
235
236 // Do BFS and compute smallest degree as we go
237 offset_t smallestDegree = nVtx;
238 gno_t smallestVertex = 0;
239
240 // Clear mark array - nothing marked yet
241 for (int i=0; i<nVtx; i++)
242 mark[i] = false;
243
244 // Start from v
245 Q.push(v);
246 while (Q.size()){
247 // Get first vertex from the queue
248 v = Q.front();
249 Q.pop();
250 // Check degree of v
251 offset_t deg = offsets[v+1] - offsets[v];
252 if (deg < smallestDegree){
253 smallestDegree = deg;
254 smallestVertex = v;
255 }
256 // Add unmarked children to queue
257 for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
258 gno_t child = edgeIds[ptr];
259 if (!mark[child]){
260 mark[child] = true;
261 Q.push(child);
262 }
263 }
264 }
265 return smallestVertex;
266 }
267
268 // Find a pseudoperipheral vertex in component containing v
269 gno_t findPseudoPeripheral(
270 gno_t v,
271 lno_t nVtx,
272 ArrayView<const gno_t> edgeIds,
273 ArrayView<const offset_t> offsets)
274 {
275 std::queue<gno_t> Q;
276 Teuchos::Array<bool> mark(nVtx);
277
278 // Do BFS a couple times, pick vertex last visited (furthest away)
279 const int numBFS = 2;
280 for (int bfs=0; bfs<numBFS; bfs++){
281 // Clear mark array - nothing marked yet
282 for (int i=0; i<nVtx; i++)
283 mark[i] = false;
284 // Start from v
285 Q.push(v);
286 while (Q.size()){
287 // Get first vertex from the queue
288 v = Q.front();
289 Q.pop();
290 // Add unmarked children to queue
291 for (offset_t ptr = offsets[v]; ptr < offsets[v+1]; ++ptr){
292 gno_t child = edgeIds[ptr];
293 if (!mark[child]){
294 mark[child] = true;
295 Q.push(child);
296 }
297 }
298 }
299 }
300 return v;
301 }
302
303};
304}
305#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
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
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__)
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
map_t::global_ordinal_type gno_t
Created by mbenlioglu on Aug 31, 2020.
std::bitset< NUM_MODEL_FLAGS > modelFlag_t