ViennaCL - The Vienna Computing Library  1.7.0
Free open-source GPU-accelerated linear algebra and solver library.
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-2015, 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>
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 
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 
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
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...
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...
advanced_cuthill_mckee_tag(double a=0.0, vcl_size_t gmax=1)
CTOR which may take the additional parameters for the advanced algorithm.
This file provides the forward declarations for the main types used within ViennaCL.
A dense matrix class.
Definition: forwards.h:375
T max(const T &lhs, const T &rhs)
Maximum.
Definition: util.hpp:59
void generate_layering(MatrixT const &matrix, std::vector< std::vector< IndexT > > &layer_list)
Function to generate a node layering as a tree structure.
Main namespace in ViennaCL. Holds all the basic types such as vector, matrix, etc. and defines operations upon them.
Definition: cpu_ram.hpp:34
vcl_size_t size(VectorType const &vec)
Generic routine for obtaining the size of a vector (ViennaCL, uBLAS, etc.)
Definition: size.hpp:235
Definition: blas3.hpp:36
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.
std::size_t vcl_size_t
Definition: forwards.h:75
IndexT calc_reordered_bw(std::vector< std::map< IndexT, ValueT > > const &matrix, std::vector< bool > &dof_assigned_to_node, std::vector< IndexT > const &permutation)
void resize(size_type rows, size_type columns, bool preserve=true)
Resizes the matrix. Existing entries can optionally be preserved.
Definition: matrix.hpp:813
bool comb_inc(std::vector< IndexT > &comb, vcl_size_t n)
bool cuthill_mckee_comp_func_pair(std::pair< IndexT, IndexT > const &a, std::pair< IndexT, IndexT > const &b)
Tag for the advanced Cuthill-McKee algorithm (i.e. running the 'standard' Cuthill-McKee algorithm for...
A tag class for selecting the Cuthill-McKee algorithm for reducing the bandwidth of a sparse matrix...
bool cuthill_mckee_comp_func(std::vector< int > const &a, std::vector< int > const &b)