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