SparseColEtree.h revision 7faaa9f3f0df9d23790277834d426c3d992ac3ba
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10
11/*
12
13 * NOTE: This file is the modified version of sp_coletree.c file in SuperLU
14
15 * -- SuperLU routine (version 3.1) --
16 * Univ. of California Berkeley, Xerox Palo Alto Research Center,
17 * and Lawrence Berkeley National Lab.
18 * August 1, 2008
19 *
20 * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
21 *
22 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
23 * EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
24 *
25 * Permission is hereby granted to use or copy this program for any
26 * purpose, provided the above notices are retained on all copies.
27 * Permission to modify the code and to distribute modified code is
28 * granted, provided the above notices are retained, and a notice that
29 * the code was modified is included with the above copyright notice.
30 */
31#ifndef SPARSE_COLETREE_H
32#define SPARSE_COLETREE_H
33
34namespace Eigen {
35
36namespace internal {
37
38/** Find the root of the tree/set containing the vertex i : Use Path halving */
39template<typename Index, typename IndexVector>
40Index etree_find (Index i, IndexVector& pp)
41{
42  Index p = pp(i); // Parent
43  Index gp = pp(p); // Grand parent
44  while (gp != p)
45  {
46    pp(i) = gp; // Parent pointer on find path is changed to former grand parent
47    i = gp;
48    p = pp(i);
49    gp = pp(p);
50  }
51  return p;
52}
53
54/** Compute the column elimination tree of a sparse matrix
55  * \param mat The matrix in column-major format.
56  * \param parent The elimination tree
57  * \param firstRowElt The column index of the first element in each row
58  * \param perm The permutation to apply to the column of \b mat
59  */
60template <typename MatrixType, typename IndexVector>
61int coletree(const MatrixType& mat, IndexVector& parent, IndexVector& firstRowElt, typename MatrixType::Index *perm=0)
62{
63  typedef typename MatrixType::Index Index;
64  Index nc = mat.cols(); // Number of columns
65  Index m = mat.rows();
66  Index diagSize = (std::min)(nc,m);
67  IndexVector root(nc); // root of subtree of etree
68  root.setZero();
69  IndexVector pp(nc); // disjoint sets
70  pp.setZero(); // Initialize disjoint sets
71  parent.resize(mat.cols());
72  //Compute first nonzero column in each row
73  Index row,col;
74  firstRowElt.resize(m);
75  firstRowElt.setConstant(nc);
76  firstRowElt.segment(0, diagSize).setLinSpaced(diagSize, 0, diagSize-1);
77  bool found_diag;
78  for (col = 0; col < nc; col++)
79  {
80    Index pcol = col;
81    if(perm) pcol  = perm[col];
82    for (typename MatrixType::InnerIterator it(mat, pcol); it; ++it)
83    {
84      row = it.row();
85      firstRowElt(row) = (std::min)(firstRowElt(row), col);
86    }
87  }
88  /* Compute etree by Liu's algorithm for symmetric matrices,
89          except use (firstRowElt[r],c) in place of an edge (r,c) of A.
90    Thus each row clique in A'*A is replaced by a star
91    centered at its first vertex, which has the same fill. */
92  Index rset, cset, rroot;
93  for (col = 0; col < nc; col++)
94  {
95    found_diag = col>=m;
96    pp(col) = col;
97    cset = col;
98    root(cset) = col;
99    parent(col) = nc;
100    /* The diagonal element is treated here even if it does not exist in the matrix
101     * hence the loop is executed once more */
102    Index pcol = col;
103    if(perm) pcol  = perm[col];
104    for (typename MatrixType::InnerIterator it(mat, pcol); it||!found_diag; ++it)
105    { //  A sequence of interleaved find and union is performed
106      Index i = col;
107      if(it) i = it.index();
108      if (i == col) found_diag = true;
109
110      row = firstRowElt(i);
111      if (row >= col) continue;
112      rset = internal::etree_find(row, pp); // Find the name of the set containing row
113      rroot = root(rset);
114      if (rroot != col)
115      {
116        parent(rroot) = col;
117        pp(cset) = rset;
118        cset = rset;
119        root(cset) = col;
120      }
121    }
122  }
123  return 0;
124}
125
126/**
127  * Depth-first search from vertex n.  No recursion.
128  * This routine was contributed by Cédric Doucet, CEDRAT Group, Meylan, France.
129*/
130template <typename Index, typename IndexVector>
131void nr_etdfs (Index n, IndexVector& parent, IndexVector& first_kid, IndexVector& next_kid, IndexVector& post, Index postnum)
132{
133  Index current = n, first, next;
134  while (postnum != n)
135  {
136    // No kid for the current node
137    first = first_kid(current);
138
139    // no kid for the current node
140    if (first == -1)
141    {
142      // Numbering this node because it has no kid
143      post(current) = postnum++;
144
145      // looking for the next kid
146      next = next_kid(current);
147      while (next == -1)
148      {
149        // No more kids : back to the parent node
150        current = parent(current);
151        // numbering the parent node
152        post(current) = postnum++;
153
154        // Get the next kid
155        next = next_kid(current);
156      }
157      // stopping criterion
158      if (postnum == n+1) return;
159
160      // Updating current node
161      current = next;
162    }
163    else
164    {
165      current = first;
166    }
167  }
168}
169
170
171/**
172  * \brief Post order a tree
173  * \param n the number of nodes
174  * \param parent Input tree
175  * \param post postordered tree
176  */
177template <typename Index, typename IndexVector>
178void treePostorder(Index n, IndexVector& parent, IndexVector& post)
179{
180  IndexVector first_kid, next_kid; // Linked list of children
181  Index postnum;
182  // Allocate storage for working arrays and results
183  first_kid.resize(n+1);
184  next_kid.setZero(n+1);
185  post.setZero(n+1);
186
187  // Set up structure describing children
188  Index v, dad;
189  first_kid.setConstant(-1);
190  for (v = n-1; v >= 0; v--)
191  {
192    dad = parent(v);
193    next_kid(v) = first_kid(dad);
194    first_kid(dad) = v;
195  }
196
197  // Depth-first search from dummy root vertex #n
198  postnum = 0;
199  internal::nr_etdfs(n, parent, first_kid, next_kid, post, postnum);
200}
201
202} // end namespace internal
203
204} // end namespace Eigen
205
206#endif // SPARSE_COLETREE_H
207