From 6011e3869384ae48dc66e65b2ec9ec237af73fdc Mon Sep 17 00:00:00 2001 From: Lucas Czech Date: Mon, 5 Aug 2024 16:29:44 -0400 Subject: [PATCH] Refine tree postorder iterator for speed --- lib/genesis/tree/iterator/postorder.hpp | 59 +++++++++++++------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/lib/genesis/tree/iterator/postorder.hpp b/lib/genesis/tree/iterator/postorder.hpp index f029e073..2aeffb20 100644 --- a/lib/genesis/tree/iterator/postorder.hpp +++ b/lib/genesis/tree/iterator/postorder.hpp @@ -3,7 +3,7 @@ /* Genesis - A toolkit for working with phylogenetic data. - Copyright (C) 2014-2020 Lucas Czech and HITS gGmbH + Copyright (C) 2014-2024 Lucas Czech This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,9 +19,9 @@ along with this program. If not, see . Contact: - Lucas Czech - Exelixis Lab, Heidelberg Institute for Theoretical Studies - Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany + Lucas Czech + University of Copenhagen, Globe Institute, Section for GeoGenetics + Oster Voldgade 5-7, 1350 Copenhagen K, Denmark */ /** @@ -35,10 +35,11 @@ #include "genesis/tree/tree/subtree.hpp" #include "genesis/utils/containers/range.hpp" +#include #include -#include #include #include +#include namespace genesis { namespace tree { @@ -128,21 +129,21 @@ class IteratorPostorder stack_.push_back( link_ptr ); // Now start traversing in the direction of this link. - stack_.push_front( &link_ptr->outer() ); + stack_.push_back( &link_ptr->outer() ); link_ptr = &link_ptr->outer(); // Add all inner nodes along the first path, until we reach a leaf. while( is_inner_( *link_ptr )) { - push_front_children_( link_ptr ); + push_back_children_( link_ptr ); link_ptr = &link_ptr->next().outer(); } // The fist leaf that we found is the last link that was added. // Remove it from the stack again, because this is where we stop - this is the first // node being visited, and hence does not need to be on the stack any more - // (it was added to keep the push_front_children fct simple and without edge case). - assert( link_ptr == stack_.front() ); - stack_.pop_front(); + // (it was added to keep the push_back_children fct simple and without edge case). + assert( link_ptr == stack_.back() ); + stack_.pop_back(); link_ = link_ptr; } @@ -163,14 +164,14 @@ class IteratorPostorder // Find the first leaf. while( is_inner_( *link_ptr )) { - push_front_children_( link_ptr ); + push_back_children_( link_ptr ); link_ptr = &link_ptr->next().outer(); } // The leaf that was last added is the one that we visit now. // Remove it again, as we are here now. - assert( link_ptr == stack_.front() ); - stack_.pop_front(); + assert( link_ptr == stack_.back() ); + stack_.pop_back(); link_ = link_ptr; } @@ -199,24 +200,24 @@ class IteratorPostorder // There are no more links on the stack to be visited. link_ = nullptr; - } else if( &link_->outer().next() == stack_.front() ) { + } else if( &link_->outer().next() == stack_.back() ) { // This condition is active when seeing an inner node the last time, // meaning that it is its turn to be visited. - link_ = stack_.front(); - stack_.pop_front(); + link_ = stack_.back(); + stack_.pop_back(); } else { // This condition is active in all other cases: going down the tree towards the leaves. // That means, we are in a part that is not yet on the stack, so we need to add it. - link_ = stack_.front(); + link_ = stack_.back(); while( is_inner_( *link_ )) { - push_front_children_(link_); + push_back_children_(link_); link_ = &link_->next().outer(); } - assert( link_ == stack_.front() ); - stack_.pop_front(); + assert( link_ == stack_.back() ); + stack_.pop_back(); } return *this; @@ -279,20 +280,20 @@ class IteratorPostorder // Internal Helper Functions // ----------------------------------------------------- - void push_front_children_( LinkType* link ) + void push_back_children_( LinkType* link ) { - // we need to push to a tmp queue first, in order to get the order right. - // otherwise, we would still do a postorder traversal, but starting with + // We add the children, and at the end reverse their order. + // Otherwise, we would still do a postorder traversal, but starting with // the last child of each node instead of the first one. - std::deque tmp; + size_t push_cnt = 0; LinkType* c = &link->next(); while( c != link ) { - tmp.push_front( &c->outer() ); + stack_.push_back( &c->outer() ); + ++push_cnt; c = &c->next(); } - for( LinkType* l : tmp ) { - stack_.push_front(l); - } + assert( stack_.size() >= push_cnt ); + std::reverse( stack_.end() - push_cnt, stack_.end() ); } bool is_inner_( TreeLink const& link ) @@ -308,7 +309,7 @@ class IteratorPostorder LinkType* const start_; LinkType* link_; - std::deque stack_; + std::vector stack_; }; // =================================================================================================