diff options
Diffstat (limited to 'src/Simplex_tree/include/gudhi/Simplex_tree.h')
-rw-r--r-- | src/Simplex_tree/include/gudhi/Simplex_tree.h | 213 |
1 files changed, 183 insertions, 30 deletions
diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 317bce23..cb6ab309 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -289,7 +289,6 @@ class Simplex_tree { /** \brief Constructs an empty simplex tree. */ Simplex_tree() : null_vertex_(-1), - threshold_(0), root_(nullptr, null_vertex_), filtration_vect_(), dimension_(-1) { } @@ -297,7 +296,6 @@ class Simplex_tree { /** \brief User-defined copy constructor reproduces the whole tree structure. */ Simplex_tree(const Simplex_tree& simplex_source) : null_vertex_(simplex_source.null_vertex_), - threshold_(simplex_source.threshold_), root_(nullptr, null_vertex_ , simplex_source.root_.members_), filtration_vect_(), dimension_(simplex_source.dimension_) { @@ -323,12 +321,10 @@ class Simplex_tree { /** \brief User-defined move constructor moves the whole tree structure. */ Simplex_tree(Simplex_tree && old) : null_vertex_(std::move(old.null_vertex_)), - threshold_(std::move(old.threshold_)), root_(std::move(old.root_)), filtration_vect_(std::move(old.filtration_vect_)), dimension_(std::move(old.dimension_)) { old.dimension_ = -1; - old.threshold_ = 0; old.root_ = Siblings(nullptr, null_vertex_); } @@ -356,7 +352,6 @@ class Simplex_tree { /** \brief Checks if two simplex trees are equal. */ bool operator==(Simplex_tree& st2) { if ((null_vertex_ != st2.null_vertex_) || - (threshold_ != st2.threshold_) || (dimension_ != st2.dimension_)) return false; return rec_equal(&root_, &st2.root_); @@ -396,25 +391,25 @@ class Simplex_tree { return sh->second.key(); } - /** \brief Returns the simplex associated to a key. + /** \brief Returns the simplex that has index idx in the filtration. * * The filtration must be initialized. * \pre SimplexTreeOptions::store_key */ - Simplex_handle simplex(Simplex_key key) const { - return filtration_vect_[key]; + Simplex_handle simplex(Simplex_key idx) const { + return filtration_vect_[idx]; } /** \brief Returns the filtration value of a simplex. * - * Called on the null_simplex, returns INFINITY. + * Called on the null_simplex, it returns infinity. * If SimplexTreeOptions::store_filtration is false, returns 0. */ static Filtration_value filtration(Simplex_handle sh) { if (sh != null_simplex()) { return sh->second.filtration(); } else { - return INFINITY; + return std::numeric_limits<Filtration_value>::infinity(); } } @@ -427,11 +422,6 @@ class Simplex_tree { sh->second.assign_filtration(fv); } - /** \brief Returns an upper bound of the filtration values of the simplices. */ - Filtration_value filtration() const { - return threshold_; - } - /** \brief Returns a Simplex_handle different from all Simplex_handles * associated to the simplices in the simplicial complex. * @@ -492,7 +482,17 @@ class Simplex_tree { } /** \brief Returns an upper bound on the dimension of the simplicial complex. */ - int dimension() const { + int upper_bound_dimension() const { + return dimension_; + } + + /** \brief Returns the dimension of the simplicial complex. + \details This function is not constant time because it can recompute dimension if required (can be triggered by + `remove_maximal_simplex()` or `prune_above_filtration()`). + */ + int dimension() { + if (dimension_to_be_lowered_) + lower_upper_bound_dimension(); return dimension_; } @@ -601,7 +601,11 @@ class Simplex_tree { // if filtration value unchanged return std::pair<Simplex_handle, bool>(null_simplex(), false); } - // otherwise the insertion has succeeded + // otherwise the insertion has succeeded - size is a size_type + if (static_cast<int>(simplex.size()) - 1 > dimension_) { + // Update dimension if needed + dimension_ = static_cast<int>(simplex.size()) - 1; + } return res_insert; } @@ -757,13 +761,12 @@ class Simplex_tree { return &root_; } - /** Set an upper bound for the filtration values. */ - void set_filtration(Filtration_value fil) { - threshold_ = fil; - } - - /** Set a dimension for the simplicial complex. */ + /** \brief Set a dimension for the simplicial complex. + * \details This function must be used with caution because it disables dimension recomputation when required + * (this recomputation can be triggered by `remove_maximal_simplex()` or `prune_above_filtration()`). + */ void set_dimension(int dimension) { + dimension_to_be_lowered_ = false; dimension_ = dimension; } @@ -1082,6 +1085,120 @@ class Simplex_tree { } public: + /** \brief Expands a simplex tree containing only a graph. Simplices corresponding to cliques in the graph are added + * incrementally, faces before cofaces, unless the simplex has dimension larger than `max_dim` or `block_simplex` + * returns true for this simplex. + * + * @param[in] max_dim Expansion maximal dimension value. + * @param[in] block_simplex Blocker oracle. Its concept is <CODE>bool block_simplex(Simplex_handle sh)</CODE> + * + * The function identifies a candidate simplex whose faces are all already in the complex, inserts + * it with a filtration value corresponding to the maximum of the filtration values of the faces, then calls + * `block_simplex` on a `Simplex_handle` for this new simplex. If `block_simplex` returns true, the simplex is + * removed, otherwise it is kept. Note that the evaluation of `block_simplex` is a good time to update the + * filtration value of the simplex if you want a customized value. The algorithm then proceeds with the next + * candidate. + * + * @warning several candidates of the same dimension may be inserted simultaneously before calling `block_simplex`, + * so if you examine the complex in `block_simplex`, you may hit a few simplices of the same dimension that have not + * been vetted by `block_simplex` yet, or have already been rejected but not yet removed. + */ + template< typename Blocker > + void expansion_with_blockers(int max_dim, Blocker block_simplex) { + // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree + for (auto& simplex : boost::adaptors::reverse(root_.members())) { + if (has_children(&simplex)) { + siblings_expansion_with_blockers(simplex.second.children(), max_dim, max_dim - 1, block_simplex); + } + } + } + + private: + /** \brief Recursive expansion with blockers of the simplex tree.*/ + template< typename Blocker > + void siblings_expansion_with_blockers(Siblings* siblings, int max_dim, int k, Blocker block_simplex) { + if (dimension_ < max_dim - k) { + dimension_ = max_dim - k; + } + if (k == 0) + return; + // No need to go deeper + if (siblings->members().size() < 2) + return; + // Reverse loop starting before the last one for 'next' to be the last one + for (auto simplex = siblings->members().rbegin() + 1; simplex != siblings->members().rend(); simplex++) { + std::vector<std::pair<Vertex_handle, Node> > intersection; + for(auto next = siblings->members().rbegin(); next != simplex; next++) { + bool to_be_inserted = true; + Filtration_value filt = simplex->second.filtration(); + // If all the boundaries are present, 'next' needs to be inserted + for (Simplex_handle border : boundary_simplex_range(simplex)) { + Simplex_handle border_child = find_child(border, next->first); + if (border_child == null_simplex()) { + to_be_inserted=false; + break; + } + filt = std::max(filt, filtration(border_child)); + } + if (to_be_inserted) { + intersection.emplace_back(next->first, Node(nullptr, filt)); + } + } + if (intersection.size() != 0) { + // Reverse the order to insert + Siblings * new_sib = new Siblings(siblings, // oncles + simplex->first, // parent + boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t + std::vector<Vertex_handle> blocked_new_sib_vertex_list; + // As all intersections are inserted, we can call the blocker function on all new_sib members + for (auto new_sib_member = new_sib->members().begin(); + new_sib_member != new_sib->members().end(); + new_sib_member++) { + bool blocker_result = block_simplex(new_sib_member); + // new_sib member has been blocked by the blocker function + // add it to the list to be removed - do not perform it while looping on it + if (blocker_result) { + blocked_new_sib_vertex_list.push_back(new_sib_member->first); + } + } + if (blocked_new_sib_vertex_list.size() == new_sib->members().size()) { + // Specific case where all have to be deleted + delete new_sib; + // ensure the children property + simplex->second.assign_children(siblings); + } else { + for (auto& blocked_new_sib_member : blocked_new_sib_vertex_list) { + new_sib->members().erase(blocked_new_sib_member); + } + // ensure recursive call + simplex->second.assign_children(new_sib); + siblings_expansion_with_blockers(new_sib, max_dim, k - 1, block_simplex); + } + } else { + // ensure the children property + simplex->second.assign_children(siblings); + } + } + } + + /* \private Returns the Simplex_handle composed of the vertex list (from the Simplex_handle), plus the given + * Vertex_handle if the Vertex_handle is found in the Simplex_handle children list. + * Returns null_simplex() if it does not exist + */ + Simplex_handle find_child(Simplex_handle sh, Vertex_handle vh) const { + if (!has_children(sh)) + return null_simplex(); + + Simplex_handle child = sh->second.children()->find(vh); + // Specific case of boost::flat_map does not find, returns boost::flat_map::end() + // in simplex tree we want a null_simplex() + if (child == sh->second.children()->members().end()) + return null_simplex(); + + return child; + } + + public: /** \brief Write the hasse diagram of the simplicial complex in os. * * Each row in the file correspond to a simplex. A line is written: @@ -1157,6 +1274,9 @@ class Simplex_tree { * \post Some simplex tree functions require the filtration to be valid. `prune_above_filtration()` * function is not launching `initialize_filtration()` but returns the filtration modification information. If the * complex has changed , please call `initialize_filtration()` to recompute it. + * \post Note that the dimension of the simplicial complex may be lower after calling `prune_above_filtration()` + * than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper + * bound. If you care, you can call `dimension()` to recompute the exact dimension. */ bool prune_above_filtration(Filtration_value filtration) { return rec_prune_above_filtration(root(), filtration); @@ -1168,6 +1288,8 @@ class Simplex_tree { auto last = std::remove_if(list.begin(), list.end(), [=](Dit_value_t& simplex) { if (simplex.second.filtration() <= filt) return false; if (has_children(&simplex)) rec_delete(simplex.second.children()); + // dimension may need to be lowered + dimension_to_be_lowered_ = true; return true; }); @@ -1176,6 +1298,8 @@ class Simplex_tree { // Removing the whole siblings, parent becomes a leaf. sib->oncles()->members()[sib->parent()].assign_children(sib->oncles()); delete sib; + // dimension may need to be lowered + dimension_to_be_lowered_ = true; return true; } else { // Keeping some elements of siblings. Remove the others, and recurse in the remaining ones. @@ -1187,12 +1311,45 @@ class Simplex_tree { return modified; } + private: + /** \brief Deep search simplex tree dimension recompute. + * @return True if the dimension was modified, false otherwise. + * \pre Be sure the simplex tree has not a too low dimension value as the deep search stops when the former dimension + * has been reached (cf. `upper_bound_dimension()` and `set_dimension()` methods). + */ + bool lower_upper_bound_dimension() { + // reset automatic detection to recompute + dimension_to_be_lowered_ = false; + int new_dimension = -1; + // Browse the tree from the left to the right as higher dimension cells are more likely on the left part of the tree + for (Simplex_handle sh : complex_simplex_range()) { +#ifdef DEBUG_TRACES + for (auto vertex : simplex_vertex_range(sh)) { + std::cout << " " << vertex; + } + std::cout << std::endl; +#endif // DEBUG_TRACES + + int sh_dimension = dimension(sh); + if (sh_dimension >= dimension_) + // Stop browsing as soon as the dimension is reached, no need to go furter + return false; + new_dimension = std::max(new_dimension, sh_dimension); + } + dimension_ = new_dimension; + return true; + } + + public: /** \brief Remove a maximal simplex. * @param[in] sh Simplex handle on the maximal simplex to remove. * \pre Please check the simplex has no coface before removing it. * \exception std::invalid_argument In debug mode, if sh has children. * \post Be aware that removing is shifting data in a flat_map (initialize_filtration to be done). + * \post Note that the dimension of the simplicial complex may be lower after calling `remove_maximal_simplex()` + * than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper + * bound. If you care, you can call `dimension()` to recompute the exact dimension. */ void remove_maximal_simplex(Simplex_handle sh) { // Guarantee the simplex has no children @@ -1210,13 +1367,13 @@ class Simplex_tree { // Sibling is emptied : must be deleted, and its parent must point on his own Sibling child->oncles()->members().at(child->parent()).assign_children(child->oncles()); delete child; + // dimension may need to be lowered + dimension_to_be_lowered_ = true; } } private: Vertex_handle null_vertex_; - /** \brief Upper bound on the filtration values of the simplices.*/ - Filtration_value threshold_; /** \brief Total number of simplices in the complex, without the empty simplex.*/ /** \brief Set of simplex tree Nodes representing the vertices.*/ Siblings root_; @@ -1224,6 +1381,7 @@ class Simplex_tree { std::vector<Simplex_handle> filtration_vect_; /** \brief Upper bound on the dimension of the simplicial complex.*/ int dimension_; + bool dimension_to_be_lowered_ = false; }; // Print a Simplex_tree in os. @@ -1244,7 +1402,6 @@ std::istream& operator>>(std::istream & is, Simplex_tree<T...> & st) { typedef Simplex_tree<T...> ST; std::vector<typename ST::Vertex_handle> simplex; typename ST::Filtration_value fil; - typename ST::Filtration_value max_fil = 0; int max_dim = -1; while (read_simplex(is, simplex, fil)) { // read all simplices in the file as a list of vertices @@ -1253,15 +1410,11 @@ std::istream& operator>>(std::istream & is, Simplex_tree<T...> & st) { if (max_dim < dim) { max_dim = dim; } - if (max_fil < fil) { - max_fil = fil; - } // insert every simplex in the simplex tree st.insert_simplex(simplex, fil); simplex.clear(); } st.set_dimension(max_dim); - st.set_filtration(max_fil); return is; } |