ViennaCL - The Vienna Computing Library: viennacl/misc/cuthill_mckee.hpp Source File

ViennaCL - The Vienna Computing Library 1.7.1 Free open-source GPU-accelerated linear algebra and solver library. |

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cuthill_mckee.hpp
Go to the documentation of this file.
1 #ifndef VIENNACL_MISC_CUTHILL_MCKEE_HPP
2 #define VIENNACL_MISC_CUTHILL_MCKEE_HPP
3
4 /* =========================================================================
5 Copyright (c) 2010-2016, Institute for Microelectronics,
6 Institute for Analysis and Scientific Computing,
7 TU Wien.
8 Portions of this software are copyright by UChicago Argonne, LLC.
9
10 -----------------
11 ViennaCL - The Vienna Computing Library
12 -----------------
13
14 Project Head: Karl Rupp rupp@iue.tuwien.ac.at
15
16 (A list of authors and contributors can be found in the manual)
17
18 License: MIT (X11), see file LICENSE in the base directory
19 ============================================================================= */
20
21
28 #include <iostream>
29 #include <iterator>
30 #include <fstream>
31 #include <string>
32 #include <algorithm>
33 #include <map>
34 #include <vector>
35 #include <deque>
36 #include <cmath>
37
38 #include "viennacl/forwards.h"
39
40 namespace viennacl
41 {
42 namespace detail
43 {
44
45 // Calculate the bandwidth of a reordered matrix
46 template<typename IndexT, typename ValueT>
47 IndexT calc_reordered_bw(std::vector< std::map<IndexT, ValueT> > const & matrix,
48 std::vector<bool> & dof_assigned_to_node,
49 std::vector<IndexT> const & permutation)
50 {
51 IndexT bw = 0;
52
53 for (vcl_size_t i = 0; i < permutation.size(); i++)
54 {
55 if (!dof_assigned_to_node[i])
56 continue;
57
58 IndexT min_index = static_cast<IndexT>(matrix.size());
59 IndexT max_index = 0;
60 for (typename std::map<IndexT, ValueT>::const_iterator it = matrix[i].begin(); it != matrix[i].end(); it++)
61 {
62 vcl_size_t col_index = static_cast<vcl_size_t>(it->first);
63 if (!dof_assigned_to_node[col_index])
64 continue;
65
66 if (permutation[col_index] > max_index)
67 max_index = permutation[col_index];
68 if (permutation[col_index] < min_index)
69 min_index = permutation[col_index];
70 }
71 if (max_index > min_index)
72 bw = std::max(bw, max_index - min_index);
73 }
74
75 return bw;
76 }
77
78
79
80 // function to calculate the increment of combination comb.
81 // parameters:
82 // comb: pointer to vector<int> of size m, m <= n
83 // 1 <= comb[i] <= n for 0 <= i < m
84 // comb[i] < comb[i+1] for 0 <= i < m - 1
85 // comb represents an ordered selection of m values out of n
86 // n: int
87 // total number of values out of which comb is taken as selection
88 template<typename IndexT>
89 bool comb_inc(std::vector<IndexT> & comb, vcl_size_t n)
90 {
91 vcl_size_t m;
92 vcl_size_t k;
93
94 m = static_cast<vcl_size_t>(comb.size());
95 // calculate k as highest possible index such that (*comb)[k-1] can be incremented
96 k = m;
97 while ( (k > 0) && ( ((k == m) && (comb[k-1] == static_cast<IndexT>(n)-1)) ||
98 ((k < m) && (comb[k-1] == comb[k] - 1) )) )
99 {
100 k--;
101 }
102
103 if (k == 0) // no further increment of comb possible -> return false
104 return false;
105
106 comb[k-1] += 1;
107
108 // and all higher index positions of comb are calculated just as directly following integer values
109 // Example (1, 4, 7) -> (1, 5, 6) -> (1, 5, 7) -> (1, 6, 7) -> done for n=7
110 for (vcl_size_t i = k; i < m; i++)
111 comb[i] = comb[k-1] + IndexT(i - k);
112 return true;
113 }
114
115
120 // node s
121 template<typename MatrixT, typename IndexT>
122 void generate_layering(MatrixT const & matrix,
123 std::vector< std::vector<IndexT> > & layer_list)
124 {
125 std::vector<bool> node_visited_already(matrix.size(), false);
126
127 //
128 // Step 1: Set root nodes to visited
129 //
130 for (vcl_size_t i=0; i<layer_list.size(); ++i)
131 {
132 for (typename std::vector<IndexT>::iterator it = layer_list[i].begin();
133 it != layer_list[i].end();
134 it++)
135 node_visited_already[*it] = true;
136 }
137
138 //
139 // Step 2: Fill next layers
140 //
141 while (layer_list.back().size() > 0)
142 {
143 vcl_size_t layer_index = layer_list.size(); //parent nodes are at layer 0
144 layer_list.push_back(std::vector<IndexT>());
145
146 for (typename std::vector<IndexT>::iterator it = layer_list[layer_index].begin();
147 it != layer_list[layer_index].end();
148 it++)
149 {
150 for (typename MatrixT::value_type::const_iterator it2 = matrix[*it].begin();
151 it2 != matrix[*it].end();
152 it2++)
153 {
154 if (it2->first == *it) continue;
155 if (node_visited_already[it2->first]) continue;
156
157 layer_list.back().push_back(it2->first);
158 node_visited_already[it2->first] = true;
159 }
160 }
161 }
162
163 // remove last (empty) nodelist:
164 layer_list.resize(layer_list.size()-1);
165 }
166
167
168 // function to generate a node layering as a tree structure rooted at node s
169 template<typename MatrixType>
170 void generate_layering(MatrixType const & matrix,
171 std::vector< std::vector<int> > & l,
172 int s)
173 {
174 vcl_size_t n = matrix.size();
175 //std::vector< std::vector<int> > l;
176 std::vector<bool> inr(n, false);
177 std::vector<int> nlist;
178
179 nlist.push_back(s);
180 inr[static_cast<vcl_size_t>(s)] = true;
181 l.push_back(nlist);
182
183 for (;;)
184 {
185 nlist.clear();
186 for (std::vector<int>::iterator it = l.back().begin();
187 it != l.back().end();
188 it++)
189 {
190 for (typename MatrixType::value_type::const_iterator it2 = matrix[static_cast<vcl_size_t>(*it)].begin();
191 it2 != matrix[static_cast<vcl_size_t>(*it)].end();
192 it2++)
193 {
194 if (it2->first == *it) continue;
195 if (inr[static_cast<vcl_size_t>(it2->first)]) continue;
196
197 nlist.push_back(it2->first);
198 inr[static_cast<vcl_size_t>(it2->first)] = true;
199 }
200 }
201
202 if (nlist.size() == 0)
203 break;
204
205 l.push_back(nlist);
206 }
207
208 }
209
214 template<typename MatrixT, typename IndexT>
215 void nodes_of_strongly_connected_component(MatrixT const & matrix,
216 std::vector<IndexT> & node_list)
217 {
218 std::vector<bool> node_visited_already(matrix.size(), false);
219 std::deque<IndexT> node_queue;
220
221 //
222 // Step 1: Push root nodes to queue:
223 //
224 for (typename std::vector<IndexT>::iterator it = node_list.begin();
225 it != node_list.end();
226 it++)
227 {
228 node_queue.push_back(*it);
229 }
230 node_list.resize(0);
231
232 //
233 // Step 2: Fill with remaining nodes of strongly connected compontent
234 //
235 while (!node_queue.empty())
236 {
237 vcl_size_t node_id = static_cast<vcl_size_t>(node_queue.front());
238 node_queue.pop_front();
239
240 if (!node_visited_already[node_id])
241 {
242 node_list.push_back(IndexT(node_id));
243 node_visited_already[node_id] = true;
244
245 for (typename MatrixT::value_type::const_iterator it = matrix[node_id].begin();
246 it != matrix[node_id].end();
247 it++)
248 {
249 vcl_size_t neighbor_node_id = static_cast<vcl_size_t>(it->first);
250 if (neighbor_node_id == node_id) continue;
251 if (node_visited_already[neighbor_node_id]) continue;
252
253 node_queue.push_back(IndexT(neighbor_node_id));
254 }
255 }
256 }
257
258 }
259
260
261 // comparison function for comparing two vector<int> values by their
262 // [1]-element
263 inline bool cuthill_mckee_comp_func(std::vector<int> const & a,
264 std::vector<int> const & b)
265 {
266 return (a[1] < b[1]);
267 }
268
269 template<typename IndexT>
270 bool cuthill_mckee_comp_func_pair(std::pair<IndexT, IndexT> const & a,
271 std::pair<IndexT, IndexT> const & b)
272 {
273 return (a.second < b.second);
274 }
275
286 template<typename IndexT, typename ValueT>
287 vcl_size_t cuthill_mckee_on_strongly_connected_component(std::vector< std::map<IndexT, ValueT> > const & matrix,
288 std::deque<IndexT> & node_assignment_queue,
289 std::vector<bool> & dof_assigned_to_node,
290 std::vector<IndexT> & permutation,
291 vcl_size_t current_dof)
292 {
293 typedef std::pair<IndexT, IndexT> NodeIdDegreePair; //first member is the node ID, second member is the node degree
294
295 std::vector< NodeIdDegreePair > local_neighbor_nodes(matrix.size());
296
297 while (!node_assignment_queue.empty())
298 {
299 // Grab first node from queue
300 vcl_size_t node_id = static_cast<vcl_size_t>(node_assignment_queue.front());
301 node_assignment_queue.pop_front();
302
303 // Assign dof if a new dof hasn't been assigned yet
304 if (!dof_assigned_to_node[node_id])
305 {
306 permutation[node_id] = static_cast<IndexT>(current_dof); //TODO: Invert this!
307 ++current_dof;
308 dof_assigned_to_node[node_id] = true;
309
310 //
311 // Get all neighbors of that node:
312 //
313 vcl_size_t num_neighbors = 0;
314 for (typename std::map<IndexT, ValueT>::const_iterator neighbor_it = matrix[node_id].begin();
315 neighbor_it != matrix[node_id].end();
316 ++neighbor_it)
317 {
318 vcl_size_t neighbor_node_index = static_cast<vcl_size_t>(neighbor_it->first);
319 if (!dof_assigned_to_node[neighbor_node_index])
320 {
321 local_neighbor_nodes[num_neighbors] = NodeIdDegreePair(neighbor_it->first, static_cast<IndexT>(matrix[neighbor_node_index].size()));
322 ++num_neighbors;
323 }
324 }
325
326 // Sort neighbors by increasing node degree
327 std::sort(local_neighbor_nodes.begin(),
328 local_neighbor_nodes.begin() + static_cast<typename std::vector< NodeIdDegreePair >::difference_type>(num_neighbors),
329 detail::cuthill_mckee_comp_func_pair<IndexT>);
330
331 // Push neighbors to queue
332 for (vcl_size_t i=0; i<num_neighbors; ++i)
333 node_assignment_queue.push_back(local_neighbor_nodes[i].first);
334
335 } // if node doesn't have a new dof yet
336
337 } // while nodes in queue
338
339 return current_dof;
340
341 }
342
343 } //namespace detail
344
345 //
346 // Part 1: The original Cuthill-McKee algorithm
347 //
348
350 struct cuthill_mckee_tag {};
351
366 template<typename IndexT, typename ValueT>
367 std::vector<IndexT> reorder(std::vector< std::map<IndexT, ValueT> > const & matrix, cuthill_mckee_tag)
368 {
369 std::vector<IndexT> permutation(matrix.size());
370 std::vector<bool> dof_assigned_to_node(matrix.size(), false); //flag vector indicating whether node i has received a new dof
371 std::deque<IndexT> node_assignment_queue;
372
373 vcl_size_t current_dof = 0; //the dof to be assigned
374
375 while (current_dof < matrix.size()) //outer loop for each strongly connected component (there may be more than one)
376 {
377 //
378 // preprocessing: Determine node degrees for nodes which have not been assigned
379 //
380 vcl_size_t current_min_degree = matrix.size();
381 vcl_size_t node_with_minimum_degree = 0;
382 bool found_unassigned_node = false;
383 for (vcl_size_t i=0; i<matrix.size(); ++i)
384 {
385 if (!dof_assigned_to_node[i])
386 {
387 if (matrix[i].size() == 1) //This is an isolated node, so assign DOF right away
388 {
389 permutation[i] = static_cast<IndexT>(current_dof);
390 dof_assigned_to_node[i] = true;
391 ++current_dof;
392 continue;
393 }
394
395 if (!found_unassigned_node) //initialize minimum degree on first node without new dof
396 {
397 current_min_degree = matrix[i].size();
398 node_with_minimum_degree = i;
399 found_unassigned_node = true;
400 }
401
402 if (matrix[i].size() < current_min_degree) //found a node with smaller degree
403 {
404 current_min_degree = matrix[i].size();
405 node_with_minimum_degree = i;
406 }
407 }
408 }
409
410 //
411 // Stage 2: Distribute dofs on this closely connected (sub-)graph in a breath-first manner using one root node
412 //
413 if (found_unassigned_node) // there's work to be done
414 {
415 node_assignment_queue.push_back(static_cast<IndexT>(node_with_minimum_degree));
416 current_dof = detail::cuthill_mckee_on_strongly_connected_component(matrix, node_assignment_queue, dof_assigned_to_node, permutation, current_dof);
417 }
418 }
419
420 return permutation;
421 }
422
423
424 //
425 // Part 2: Advanced Cuthill McKee
426 //
427
429 class advanced_cuthill_mckee_tag
430 {
431 public:
450 advanced_cuthill_mckee_tag(double a = 0.0, vcl_size_t gmax = 1) : starting_node_param_(a), max_root_nodes_(gmax) {}
451
452 double starting_node_param() const { return starting_node_param_;}
453 void starting_node_param(double a) { if (a >= 0) starting_node_param_ = a; }
454
455 vcl_size_t max_root_nodes() const { return max_root_nodes_; }
456 void max_root_nodes(vcl_size_t gmax) { max_root_nodes_ = gmax; }
457
458 private:
459 double starting_node_param_;
460 vcl_size_t max_root_nodes_;
461 };
462
463
464
473 template<typename IndexT, typename ValueT>
474 std::vector<IndexT> reorder(std::vector< std::map<IndexT, ValueT> > const & matrix,
475 advanced_cuthill_mckee_tag const & tag)
476 {
477 vcl_size_t n = matrix.size();
478 double a = tag.starting_node_param();
479 vcl_size_t gmax = tag.max_root_nodes();
480 std::vector<IndexT> permutation(n);
481 std::vector<bool> dof_assigned_to_node(n, false);
482 std::vector<IndexT> nodes_in_strongly_connected_component;
483 std::vector<IndexT> parent_nodes;
484 vcl_size_t deg_min;
485 vcl_size_t deg_max;
486 vcl_size_t deg_a;
487 vcl_size_t deg;
488 std::vector<IndexT> comb;
489
490 nodes_in_strongly_connected_component.reserve(n);
491 parent_nodes.reserve(n);
492 comb.reserve(n);
493
494 vcl_size_t current_dof = 0;
495
496 while (current_dof < matrix.size()) // for all strongly connected components
497 {
498 // get all nodes of the strongly connected component:
499 nodes_in_strongly_connected_component.resize(0);
500 for (vcl_size_t i = 0; i < n; i++)
501 {
502 if (!dof_assigned_to_node[i])
503 {
504 nodes_in_strongly_connected_component.push_back(static_cast<IndexT>(i));
505 detail::nodes_of_strongly_connected_component(matrix, nodes_in_strongly_connected_component);
506 break;
507 }
508 }
509
510 // determine minimum and maximum node degree
511 deg_min = 0;
512 deg_max = 0;
513 for (typename std::vector<IndexT>::iterator it = nodes_in_strongly_connected_component.begin();
514 it != nodes_in_strongly_connected_component.end();
515 it++)
516 {
517 deg = matrix[static_cast<vcl_size_t>(*it)].size();
518 if (deg_min == 0 || deg < deg_min)
519 deg_min = deg;
520 if (deg_max == 0 || deg > deg_max)
521 deg_max = deg;
522 }
523 deg_a = deg_min + static_cast<vcl_size_t>(a * double(deg_max - deg_min));
524
525 // fill array of parent nodes:
526 parent_nodes.resize(0);
527 for (typename std::vector<IndexT>::iterator it = nodes_in_strongly_connected_component.begin();
528 it != nodes_in_strongly_connected_component.end();
529 it++)
530 {
531 if (matrix[static_cast<vcl_size_t>(*it)].size() <= deg_a)
532 parent_nodes.push_back(*it);
533 }
534
535 //
536 // backup current state in order to restore for every new combination of parent nodes below
537 //
538 std::vector<bool> dof_assigned_to_node_backup = dof_assigned_to_node;
539 std::vector<bool> dof_assigned_to_node_best;
540
541 std::vector<IndexT> permutation_backup = permutation;
542 std::vector<IndexT> permutation_best = permutation;
543
544 vcl_size_t current_dof_backup = current_dof;
545
546 vcl_size_t g = 1;
547 comb.resize(1);
548 comb[0] = 0;
549
550 IndexT bw_best = 0;
551
552 //
553 // Loop over all combinations of g <= gmax root nodes
554 //
555
556 for (;;)
557 {
558 dof_assigned_to_node = dof_assigned_to_node_backup;
559 permutation = permutation_backup;
560 current_dof = current_dof_backup;
561
562 std::deque<IndexT> node_queue;
563
564 // add the selected root nodes according to actual combination comb to q
565 for (typename std::vector<IndexT>::iterator it = comb.begin(); it != comb.end(); it++)
566 node_queue.push_back(parent_nodes[static_cast<vcl_size_t>(*it)]);
567
568 current_dof = detail::cuthill_mckee_on_strongly_connected_component(matrix, node_queue, dof_assigned_to_node, permutation, current_dof);
569
570 // calculate resulting bandwith for root node combination
571 // comb for current numbered component of the node graph
572 IndexT bw = detail::calc_reordered_bw(matrix, dof_assigned_to_node, permutation);
573
574 // remember best ordering:
575 if (bw_best == 0 || bw < bw_best)
576 {
577 permutation_best = permutation;
578 bw_best = bw;
579 dof_assigned_to_node_best = dof_assigned_to_node;
580 }
581
582 // calculate next combination comb, if not existing
583 // increment g if g stays <= gmax, or else terminate loop
584 if (!detail::comb_inc(comb, parent_nodes.size()))
585 {
586 ++g;
587 if ( (gmax > 0 && g > gmax) || g > parent_nodes.size())
588 break;
589
590 comb.resize(g);
591 for (vcl_size_t i = 0; i < g; i++)
592 comb[i] = static_cast<IndexT>(i);
593 }
594 }
595
596 //
597 // restore best permutation
598 //
599 permutation = permutation_best;
600 dof_assigned_to_node = dof_assigned_to_node_best;
601
602 }
603
604 return permutation;
605 }
606
607
608 } //namespace viennacl
609
610
611 #endif
viennacl::reorder
std::vector< IndexT > reorder(std::vector< std::map< IndexT, ValueT > > const &matrix, cuthill_mckee_tag)
Function for the calculation of a node number permutation to reduce the bandwidth of an incidence mat...
Definition: cuthill_mckee.hpp:367
viennacl::detail::nodes_of_strongly_connected_component
void nodes_of_strongly_connected_component(MatrixT const &matrix, std::vector< IndexT > &node_list)
Fills the provided nodelist with all nodes of the same strongly connected component as the nodes in t...
Definition: cuthill_mckee.hpp:215
viennacl::advanced_cuthill_mckee_tag::advanced_cuthill_mckee_tag
advanced_cuthill_mckee_tag(double a=0.0, vcl_size_t gmax=1)
CTOR which may take the additional parameters for the advanced algorithm.
Definition: cuthill_mckee.hpp:450
forwards.h
This file provides the forward declarations for the main types used within ViennaCL.
viennacl::matrix
A dense matrix class.
Definition: forwards.h:375
viennacl::linalg::detail::max
T max(const T &lhs, const T &rhs)
Maximum.
Definition: util.hpp:59
viennacl::detail::generate_layering
void generate_layering(MatrixT const &matrix, std::vector< std::vector< IndexT > > &layer_list)
Function to generate a node layering as a tree structure.
Definition: cuthill_mckee.hpp:122
viennacl::matrix::resize
void resize(size_type rows, size_type columns, bool preserve=true)
Resizes the matrix. Existing entries can optionally be preserved.
Definition: matrix.hpp:813
viennacl::advanced_cuthill_mckee_tag::starting_node_param
void starting_node_param(double a)
Definition: cuthill_mckee.hpp:453
viennacl::advanced_cuthill_mckee_tag::starting_node_param
double starting_node_param() const
Definition: cuthill_mckee.hpp:452
viennacl::traits::size
vcl_size_t size(VectorType const &vec)
Generic routine for obtaining the size of a vector (ViennaCL, uBLAS, etc.)
Definition: size.hpp:239
viennacl::advanced_cuthill_mckee_tag::max_root_nodes
void max_root_nodes(vcl_size_t gmax)
Definition: cuthill_mckee.hpp:456
viennacl::detail::cuthill_mckee_on_strongly_connected_component
vcl_size_t cuthill_mckee_on_strongly_connected_component(std::vector< std::map< IndexT, ValueT > > const &matrix, std::deque< IndexT > &node_assignment_queue, std::vector< bool > &dof_assigned_to_node, std::vector< IndexT > &permutation, vcl_size_t current_dof)
Runs the Cuthill-McKee algorithm on a strongly connected component of a graph.
Definition: cuthill_mckee.hpp:287
viennacl::vcl_size_t
std::size_t vcl_size_t
Definition: forwards.h:75
viennacl::advanced_cuthill_mckee_tag::max_root_nodes
vcl_size_t max_root_nodes() const
Definition: cuthill_mckee.hpp:455
viennacl::detail::calc_reordered_bw
IndexT calc_reordered_bw(std::vector< std::map< IndexT, ValueT > > const &matrix, std::vector< bool > &dof_assigned_to_node, std::vector< IndexT > const &permutation)
Definition: cuthill_mckee.hpp:47
viennacl::detail::comb_inc
bool comb_inc(std::vector< IndexT > &comb, vcl_size_t n)
Definition: cuthill_mckee.hpp:89
viennacl::detail::cuthill_mckee_comp_func_pair
bool cuthill_mckee_comp_func_pair(std::pair< IndexT, IndexT > const &a, std::pair< IndexT, IndexT > const &b)
Definition: cuthill_mckee.hpp:270
viennacl::advanced_cuthill_mckee_tag
Tag for the advanced Cuthill-McKee algorithm (i.e. running the 'standard' Cuthill-McKee algorithm for...
Definition: cuthill_mckee.hpp:429
viennacl::cuthill_mckee_tag
A tag class for selecting the Cuthill-McKee algorithm for reducing the bandwidth of a sparse matrix...
Definition: cuthill_mckee.hpp:350
viennacl::detail::cuthill_mckee_comp_func
bool cuthill_mckee_comp_func(std::vector< int > const &a, std::vector< int > const &b)
Definition: cuthill_mckee.hpp:263