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 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 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