| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr> |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| /* |
| |
| * NOTE: This file is the modified version of sp_coletree.c file in SuperLU |
| |
| * -- SuperLU routine (version 3.1) -- |
| * Univ. of California Berkeley, Xerox Palo Alto Research Center, |
| * and Lawrence Berkeley National Lab. |
| * August 1, 2008 |
| * |
| * Copyright (c) 1994 by Xerox Corporation. All rights reserved. |
| * |
| * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY |
| * EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
| * |
| * Permission is hereby granted to use or copy this program for any |
| * purpose, provided the above notices are retained on all copies. |
| * Permission to modify the code and to distribute modified code is |
| * granted, provided the above notices are retained, and a notice that |
| * the code was modified is included with the above copyright notice. |
| */ |
| #ifndef SPARSE_COLETREE_H |
| #define SPARSE_COLETREE_H |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| |
| namespace internal { |
| |
| /** Find the root of the tree/set containing the vertex i : Use Path halving */ |
| template <typename Index, typename IndexVector> |
| Index etree_find(Index i, IndexVector& pp) { |
| Index p = pp(i); // Parent |
| Index gp = pp(p); // Grand parent |
| while (gp != p) { |
| pp(i) = gp; // Parent pointer on find path is changed to former grand parent |
| i = gp; |
| p = pp(i); |
| gp = pp(p); |
| } |
| return p; |
| } |
| |
| /** Compute the column elimination tree of a sparse matrix |
| * \param mat The matrix in column-major format. |
| * \param parent The elimination tree |
| * \param firstRowElt The column index of the first element in each row |
| * \param perm The permutation to apply to the column of \b mat |
| */ |
| template <typename MatrixType, typename IndexVector> |
| int coletree(const MatrixType& mat, IndexVector& parent, IndexVector& firstRowElt, |
| typename MatrixType::StorageIndex* perm = 0) { |
| typedef typename MatrixType::StorageIndex StorageIndex; |
| StorageIndex nc = convert_index<StorageIndex>(mat.cols()); // Number of columns |
| StorageIndex m = convert_index<StorageIndex>(mat.rows()); |
| StorageIndex diagSize = (std::min)(nc, m); |
| IndexVector root(nc); // root of subtree of etree |
| root.setZero(); |
| IndexVector pp(nc); // disjoint sets |
| pp.setZero(); // Initialize disjoint sets |
| parent.resize(mat.cols()); |
| // Compute first nonzero column in each row |
| firstRowElt.resize(m); |
| firstRowElt.setConstant(nc); |
| firstRowElt.segment(0, diagSize).setLinSpaced(diagSize, 0, diagSize - 1); |
| bool found_diag; |
| for (StorageIndex col = 0; col < nc; col++) { |
| StorageIndex pcol = col; |
| if (perm) pcol = perm[col]; |
| for (typename MatrixType::InnerIterator it(mat, pcol); it; ++it) { |
| Index row = it.row(); |
| firstRowElt(row) = (std::min)(firstRowElt(row), col); |
| } |
| } |
| /* Compute etree by Liu's algorithm for symmetric matrices, |
| except use (firstRowElt[r],c) in place of an edge (r,c) of A. |
| Thus each row clique in A'*A is replaced by a star |
| centered at its first vertex, which has the same fill. */ |
| StorageIndex rset, cset, rroot; |
| for (StorageIndex col = 0; col < nc; col++) { |
| found_diag = col >= m; |
| pp(col) = col; |
| cset = col; |
| root(cset) = col; |
| parent(col) = nc; |
| /* The diagonal element is treated here even if it does not exist in the matrix |
| * hence the loop is executed once more */ |
| StorageIndex pcol = col; |
| if (perm) pcol = perm[col]; |
| for (typename MatrixType::InnerIterator it(mat, pcol); it || !found_diag; |
| ++it) { // A sequence of interleaved find and union is performed |
| Index i = col; |
| if (it) i = it.index(); |
| if (i == col) found_diag = true; |
| |
| StorageIndex row = firstRowElt(i); |
| if (row >= col) continue; |
| rset = internal::etree_find(row, pp); // Find the name of the set containing row |
| rroot = root(rset); |
| if (rroot != col) { |
| parent(rroot) = col; |
| pp(cset) = rset; |
| cset = rset; |
| root(cset) = col; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * Depth-first search from vertex n. No recursion. |
| * This routine was contributed by Cédric Doucet, CEDRAT Group, Meylan, France. |
| */ |
| template <typename IndexVector> |
| void nr_etdfs(typename IndexVector::Scalar n, IndexVector& parent, IndexVector& first_kid, IndexVector& next_kid, |
| IndexVector& post, typename IndexVector::Scalar postnum) { |
| typedef typename IndexVector::Scalar StorageIndex; |
| StorageIndex current = n, first, next; |
| while (postnum != n) { |
| // No kid for the current node |
| first = first_kid(current); |
| |
| // no kid for the current node |
| if (first == -1) { |
| // Numbering this node because it has no kid |
| post(current) = postnum++; |
| |
| // looking for the next kid |
| next = next_kid(current); |
| while (next == -1) { |
| // No more kids : back to the parent node |
| current = parent(current); |
| // numbering the parent node |
| post(current) = postnum++; |
| |
| // Get the next kid |
| next = next_kid(current); |
| } |
| // stopping criterion |
| if (postnum == n + 1) return; |
| |
| // Updating current node |
| current = next; |
| } else { |
| current = first; |
| } |
| } |
| } |
| |
| /** |
| * \brief Post order a tree |
| * \param n the number of nodes |
| * \param parent Input tree |
| * \param post postordered tree |
| */ |
| template <typename IndexVector> |
| void treePostorder(typename IndexVector::Scalar n, IndexVector& parent, IndexVector& post) { |
| typedef typename IndexVector::Scalar StorageIndex; |
| IndexVector first_kid, next_kid; // Linked list of children |
| StorageIndex postnum; |
| // Allocate storage for working arrays and results |
| first_kid.resize(n + 1); |
| next_kid.setZero(n + 1); |
| post.setZero(n + 1); |
| |
| // Set up structure describing children |
| first_kid.setConstant(-1); |
| for (StorageIndex v = n - 1; v >= 0; v--) { |
| StorageIndex dad = parent(v); |
| next_kid(v) = first_kid(dad); |
| first_kid(dad) = v; |
| } |
| |
| // Depth-first search from dummy root vertex #n |
| postnum = 0; |
| internal::nr_etdfs(n, parent, first_kid, next_kid, post, postnum); |
| } |
| |
| } // end namespace internal |
| |
| } // end namespace Eigen |
| |
| #endif // SPARSE_COLETREE_H |