All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
Simplex_tree.h
1/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2 * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3 * Author(s): Clément Maria
4 *
5 * Copyright (C) 2014 Inria
6 *
7 * Modification(s):
8 * - 2023/02 Vincent Rouvreau: Add de/serialize methods for pickle feature
9 * - 2023/07 Clément Maria: Option to link all simplex tree nodes with same label in an intrusive list
10 * - 2023/05 Clément Maria: Edge insertion method for flag complexes
11 * - 2023/05 Hannah Schreiber: Factorization of expansion methods
12 * - 2023/08 Hannah Schreiber (& Clément Maria): Add possibility of stable simplex handles.
13 * - 2024/08 Hannah Schreiber: Addition of customizable copy constructor.
14 * - 2024/08 Marc Glisse: Allow storing custom data in simplices.
15 * - 2024/10 Hannah Schreiber: Const version of the Simplex_tree
16 * - YYYY/MM Author: Description of the modification
17 */
18
19#ifndef SIMPLEX_TREE_H_
20#define SIMPLEX_TREE_H_
21
22#include <gudhi/Simplex_tree/simplex_tree_options.h>
23#include <gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h>
24#include <gudhi/Simplex_tree/Simplex_tree_siblings.h>
25#include <gudhi/Simplex_tree/Simplex_tree_iterators.h>
26#include <gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h>
27#include <gudhi/Simplex_tree/serialization_utils.h> // for Gudhi::simplex_tree::de/serialize_trivial
28#include <gudhi/Simplex_tree/hooks_simplex_base.h>
29
30#include <gudhi/reader_utils.h>
32#include <gudhi/Debug_utils.h>
33
34#include <boost/container/map.hpp>
35#include <boost/container/flat_map.hpp>
36#include <boost/iterator/transform_iterator.hpp>
37#include <boost/graph/adjacency_list.hpp>
38#include <boost/range/adaptor/reversed.hpp>
39#include <boost/range/adaptor/transformed.hpp>
40#include <boost/range/size.hpp>
41#include <boost/container/static_vector.hpp>
42#include <boost/range/adaptors.hpp>
43
44#include <boost/intrusive/list.hpp>
45#include <boost/intrusive/parent_from_member.hpp>
46#include <cstddef>
47
48#ifdef GUDHI_USE_TBB
49#include <tbb/parallel_sort.h>
50#endif
51
52#include <utility> // for std::move
53#include <vector>
54#include <functional> // for greater<>
55#include <stdexcept>
56#include <limits> // Inf
57#include <initializer_list>
58#include <algorithm> // for std::max
59#include <iterator> // for std::distance
60#include <type_traits> // for std::conditional
61#include <unordered_map>
62#include <iterator> // for std::prev
63
64namespace Gudhi {
65
82enum class Extended_simplex_type {UP, DOWN, EXTRA};
83
97template<typename SimplexTreeOptions = Simplex_tree_options_default>
99 public:
101 typedef typename Options::Indexing_tag Indexing_tag;
115 typedef typename Get_simplex_data_type<Options>::type Simplex_data;
120
121 /* Type of node in the simplex tree. */
123 /* Type of dictionary Vertex_handle -> Node for traversing the simplex tree. */
124 // Note: this wastes space when Vertex_handle is 32 bits and Node is aligned on 64 bits. It would be better to use a
125 // flat_set (with our own comparator) where we can control the layout of the struct (put Vertex_handle and
126 // Simplex_key next to each other).
127 typedef typename boost::container::flat_map<Vertex_handle, Node> flat_map;
128 //Dictionary::iterator remain valid under insertions and deletions,
129 //necessary e.g. when computing oscillating rips zigzag filtrations.
130 typedef typename boost::container::map<Vertex_handle, Node> map;
131 typedef typename std::conditional<Options::stable_simplex_handles,
132 map,
133 flat_map>::type Dictionary;
134
137
138
139
140 struct Key_simplex_base_real {
141 Key_simplex_base_real() : key_(-1) {}
142 Key_simplex_base_real(Simplex_key k) : key_(k) {}
143 void assign_key(Simplex_key k) { key_ = k; }
144 Simplex_key key() const { return key_; }
145 private:
146 Simplex_key key_;
147 };
148 struct Key_simplex_base_dummy {
149 Key_simplex_base_dummy() {}
150 // Undefined so it will not link
152 Simplex_key key() const;
153 };
154
155 struct Extended_filtration_data {
156 Filtration_value minval;
157 Filtration_value maxval;
158 Extended_filtration_data(){}
159 Extended_filtration_data(Filtration_value vmin, Filtration_value vmax): minval(vmin), maxval(vmax) {}
160 };
161 typedef typename std::conditional<Options::store_key, Key_simplex_base_real, Key_simplex_base_dummy>::type
162 Key_simplex_base;
163
164 struct Filtration_simplex_base_real {
165 Filtration_simplex_base_real() : filt_(0) {}
166 Filtration_simplex_base_real(Filtration_value f) : filt_(f) {}
167 void assign_filtration(Filtration_value f) { filt_ = f; }
168 Filtration_value filtration() const { return filt_; }
169 private:
170 Filtration_value filt_;
171 };
172 struct Filtration_simplex_base_dummy {
173 Filtration_simplex_base_dummy() {}
174 Filtration_simplex_base_dummy(Filtration_value GUDHI_CHECK_code(f)) {
175 GUDHI_CHECK(f == 0, "filtration value specified for a complex that does not store them");
176 }
177 void assign_filtration(Filtration_value GUDHI_CHECK_code(f)) {
178 GUDHI_CHECK(f == 0, "filtration value specified for a complex that does not store them");
179 }
180 Filtration_value filtration() const { return 0; }
181 };
182 typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real,
183 Filtration_simplex_base_dummy>::type Filtration_simplex_base;
184
185 public:
192 typedef typename Dictionary::const_iterator Simplex_handle;
193
194 private:
195 typedef typename Dictionary::iterator Dictionary_it;
196 typedef typename Dictionary::const_iterator Dictionary_const_it;
197 typedef typename Dictionary_it::value_type Dit_value_t;
198
199 struct return_first {
200 Vertex_handle operator()(const Dit_value_t& p_sh) const {
201 return p_sh.first;
202 }
203 };
204
205 private:
212 using Optimized_star_simplex_iterator = Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
214 using Optimized_star_simplex_range = boost::iterator_range<Optimized_star_simplex_iterator>;
215
216 class Fast_cofaces_predicate {
217 Simplex_tree const* st_;
218 int codim_;
219 int dim_;
220 public:
221 Fast_cofaces_predicate(Simplex_tree const* st, int codim, int dim)
222 : st_(st), codim_(codim), dim_(codim + dim) {}
223 bool operator()( const Simplex_handle iter ) const {
224 if (codim_ == 0)
225 // Always true for a star
226 return true;
227 // Specific coface case
228 return dim_ == st_->dimension(iter);
229 }
230 };
231
232 // WARNING: this is safe only because boost::filtered_range is containing a copy of begin and end iterator.
233 // This would not be safe if it was containing a pointer to a range (maybe the case for std::views)
234 using Optimized_cofaces_simplex_filtered_range = boost::filtered_range<Fast_cofaces_predicate,
235 Optimized_star_simplex_range>;
236
237
240 static constexpr int max_dimension() { return 40; }
241 public:
253 typedef boost::transform_iterator<return_first, Dictionary_const_it> Complex_vertex_iterator;
255 typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
261 typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
263 typedef typename std::conditional<Options::link_nodes_by_label,
264 Optimized_cofaces_simplex_filtered_range, // faster implementation
265 std::vector<Simplex_handle>>::type Cofaces_simplex_range;
266
270 using Static_vertex_vector = boost::container::static_vector<Vertex_handle, max_dimension()>;
271
277 typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
283 typedef boost::iterator_range<Boundary_opposite_vertex_simplex_iterator> Boundary_opposite_vertex_simplex_range;
289 typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
297 typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
299 typedef std::vector<Simplex_handle> Filtration_simplex_range;
303 typedef typename Filtration_simplex_range::const_iterator Filtration_simplex_iterator;
304
305 /* @} */ // end name range and iterator types
312 return Complex_vertex_range(boost::make_transform_iterator(root_.members_.begin(), return_first()),
313 boost::make_transform_iterator(root_.members_.end(), return_first()));
314 }
315
324 }
325
338 }
339
360 return filtration_vect_;
361 }
362
370 GUDHI_CHECK(sh != null_simplex(), "empty simplex");
373 }
374
389 template<class SimplexHandle>
393 }
394
406 template<class SimplexHandle>
410 }
411 // end range and iterator methods
418 : null_vertex_(-1),
419 root_(nullptr, null_vertex_),
420 filtration_vect_(),
421 dimension_(-1) { }
422
438 template<typename OtherSimplexTreeOptions, typename F>
439 Simplex_tree(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
440#ifdef DEBUG_TRACES
441 std::clog << "Simplex_tree custom copy constructor" << std::endl;
442#endif // DEBUG_TRACES
443 copy_from(complex_source, translate_filtration_value);
444 }
445
449 Simplex_tree(const Simplex_tree& complex_source) {
450#ifdef DEBUG_TRACES
451 std::clog << "Simplex_tree copy constructor" << std::endl;
452#endif // DEBUG_TRACES
453 copy_from(complex_source);
454 }
455
460 Simplex_tree(Simplex_tree && complex_source) {
461#ifdef DEBUG_TRACES
462 std::clog << "Simplex_tree move constructor" << std::endl;
463#endif // DEBUG_TRACES
464 move_from(complex_source);
465
466 // just need to set dimension_ on source to make it available again
467 // (filtration_vect_ and members are already set from the move)
468 complex_source.dimension_ = -1;
469 }
470
473 root_members_recursive_deletion();
474 }
475
479 Simplex_tree& operator= (const Simplex_tree& complex_source) {
480#ifdef DEBUG_TRACES
481 std::clog << "Simplex_tree copy assignment" << std::endl;
482#endif // DEBUG_TRACES
483 // Self-assignment detection
484 if (&complex_source != this) {
485 // We start by deleting root_ if not empty
486 root_members_recursive_deletion();
487
488 copy_from(complex_source);
489 }
490 return *this;
491 }
492
498#ifdef DEBUG_TRACES
499 std::clog << "Simplex_tree move assignment" << std::endl;
500#endif // DEBUG_TRACES
501 // Self-assignment detection
502 if (&complex_source != this) {
503 // root_ deletion in case it was not empty
504 root_members_recursive_deletion();
505
506 move_from(complex_source);
507 }
508 return *this;
509 } // end constructor/destructor
511
512 private:
513 // Copy from complex_source to "this"
514 void copy_from(const Simplex_tree& complex_source) {
515 null_vertex_ = complex_source.null_vertex_;
516 filtration_vect_.clear();
517 dimension_ = complex_source.dimension_;
518 auto root_source = complex_source.root_;
519
520 // root members copy
521 root_.members() =
522 Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
523 // Needs to reassign children
524 for (auto& map_el : root_.members()) {
525 map_el.second.assign_children(&root_);
526 }
527 // Specific for optional data
528 if constexpr (!std::is_same_v<Simplex_data, No_simplex_data>) {
529 auto dst_iter = root_.members().begin();
530 auto src_iter = root_source.members().begin();
531
532 while(dst_iter != root_.members().end() || src_iter != root_source.members().end()) {
533 dst_iter->second.data() = src_iter->second.data();
534 dst_iter++;
535 src_iter++;
536 }
537 // Check in debug mode members data were copied
538 assert(dst_iter == root_.members().end());
539 assert(src_iter == root_source.members().end());
540 }
541 rec_copy<Options::store_key, true>(
542 &root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
543 }
544
545 // Copy from complex_source to "this"
546 template<typename OtherSimplexTreeOptions, typename F>
547 void copy_from(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
548 null_vertex_ = complex_source.null_vertex_;
549 filtration_vect_.clear();
550 dimension_ = complex_source.dimension_;
551 auto root_source = complex_source.root_;
552
553 // root members copy
554 if constexpr (!Options::stable_simplex_handles) root_.members().reserve(root_source.size());
555 for (auto& p : root_source.members()){
556 if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) {
557 auto it = root_.members().try_emplace(
558 root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()), p.second.key());
559 } else {
560 auto it = root_.members().try_emplace(
561 root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()));
562 }
563 }
564
565 rec_copy<OtherSimplexTreeOptions::store_key, false>(&root_, &root_source, translate_filtration_value);
566 }
567
569 template<bool store_key, bool copy_simplex_data, typename OtherSiblings, typename F>
570 void rec_copy(Siblings *sib, OtherSiblings *sib_source, F&& translate_filtration_value) {
571 auto sh_source = sib_source->members().begin();
572 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh, ++sh_source) {
573 update_simplex_tree_after_node_insertion(sh);
574 if (has_children(sh_source)) {
575 Siblings * newsib = new Siblings(sib, sh_source->first);
576 if constexpr (!Options::stable_simplex_handles) {
577 newsib->members_.reserve(sh_source->second.children()->members().size());
578 }
579 for (auto & child : sh_source->second.children()->members()){
580 Dictionary_it new_it{};
581 if constexpr (store_key && Options::store_key) {
582 new_it = newsib->members_.emplace_hint(
583 newsib->members_.end(),
584 child.first,
585 Node(newsib, translate_filtration_value(child.second.filtration()), child.second.key()));
586 } else {
587 new_it = newsib->members_.emplace_hint(newsib->members_.end(),
588 child.first,
589 Node(newsib, translate_filtration_value(child.second.filtration())));
590 }
591 // Specific for optional data
592 if constexpr (copy_simplex_data && !std::is_same_v<Simplex_data, No_simplex_data>) {
593 new_it->second.data() = child.second.data();
594 }
595 }
596 rec_copy<store_key, copy_simplex_data>(newsib, sh_source->second.children(), translate_filtration_value);
597 sh->second.assign_children(newsib);
598 }
599 }
600 }
601
602 // Move from complex_source to "this"
603 void move_from(Simplex_tree& complex_source) {
604 null_vertex_ = std::move(complex_source.null_vertex_);
605 root_ = std::move(complex_source.root_);
606 filtration_vect_ = std::move(complex_source.filtration_vect_);
607 dimension_ = complex_source.dimension_;
608 if constexpr (Options::link_nodes_by_label) {
609 nodes_label_to_list_.swap(complex_source.nodes_label_to_list_);
610 }
611 // Need to update root members (children->oncles and children need to point on the new root pointer)
612 for (auto& map_el : root_.members()) {
613 if (map_el.second.children() != &(complex_source.root_)) {
614 // reset children->oncles with the moved root_ pointer value
615 map_el.second.children()->oncles_ = &root_;
616 } else {
617 // if simplex is of dimension 0, oncles_ shall be nullptr
618 GUDHI_CHECK(map_el.second.children()->oncles_ == nullptr,
619 std::invalid_argument("Simplex_tree move constructor from an invalid Simplex_tree"));
620 // and children points on root_ - to be moved
621 map_el.second.assign_children(&root_);
622 }
623 }
624 }
625
626 // delete all root_.members() recursively
627 void root_members_recursive_deletion() {
628 for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
629 if (has_children(sh)) {
630 rec_delete(sh->second.children());
631 }
632 }
633 root_.members().clear();
634 }
635
636 // Recursive deletion
637 void rec_delete(Siblings * sib) {
638 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
639 if (has_children(sh)) {
640 rec_delete(sh->second.children());
641 }
642 }
643 delete sib;
644 }
645
646 public:
647 template<typename> friend class Simplex_tree;
648
652 template<class OtherSimplexTreeOptions>
654 if ((null_vertex_ != st2.null_vertex_) ||
655 (dimension_ != st2.dimension_ && !dimension_to_be_lowered_ && !st2.dimension_to_be_lowered_))
656 return false;
657 return rec_equal(&root_, &st2.root_);
658 }
659
661 template<class OtherSimplexTreeOptions>
663 return (!(*this == st2));
664 }
665
666 private:
668 template<class OtherSiblings>
669 bool rec_equal(Siblings const* s1, OtherSiblings const* s2) const {
670 if (s1->members().size() != s2->members().size())
671 return false;
672 auto sh2 = s2->members().begin();
673 for (auto sh1 = s1->members().begin();
674 (sh1 != s1->members().end() && sh2 != s2->members().end());
675 ++sh1, ++sh2) {
676 if (sh1->first != sh2->first || sh1->second.filtration() != sh2->second.filtration())
677 return false;
678 if (has_children(sh1) != has_children(sh2))
679 return false;
680 // Recursivity on children only if both have children
681 else if (has_children(sh1))
682 if (!rec_equal(sh1->second.children(), sh2->second.children()))
683 return false;
684 }
685 return true;
686 }
687
692 static Filtration_value filtration_(Simplex_handle sh) {
693 GUDHI_CHECK (sh != null_simplex(), "null simplex");
694 return sh->second.filtration();
695 }
696
697 // Transform a Dictionary_const_it into a Dictionary_it
698 Dictionary_it _to_node_it(Simplex_handle sh) {
699 return self_siblings(sh)->to_non_const_it(sh);
700 }
701
702 public:
709 return sh->second.key();
710 }
711
719 return filtration_vect_[idx];
720 }
721
728 if (sh != null_simplex()) {
729 return sh->second.filtration();
730 } else {
731 return std::numeric_limits<Filtration_value>::infinity();
732 }
733 }
734
739 GUDHI_CHECK(sh != null_simplex(),
740 std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex"));
741 _to_node_it(sh)->second.assign_filtration(fv);
742 }
743
749 return Dictionary_const_it();
750 }
751
754 return -1;
755 }
756
759 GUDHI_CHECK(sh != null_simplex(),
760 std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
761 return _to_node_it(sh)->second.data();
762 }
763
766 GUDHI_CHECK(sh != null_simplex(),
767 std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
768 return sh->second.data();
769 }
770
774 return null_vertex_;
775 }
776
778 size_t num_vertices() const {
779 return root_.members_.size();
780 }
781
783 bool is_empty() const {
784 return root_.members_.empty();
785 }
786
787 public:
791 size_t num_simplices() const {
792 return num_simplices(root());
793 }
794
795 private:
797 size_t num_simplices(const Siblings * sib) const {
798 auto sib_begin = sib->members().begin();
799 auto sib_end = sib->members().end();
800 size_t simplices_number = sib->members().size();
801 for (auto sh = sib_begin; sh != sib_end; ++sh) {
802 if (has_children(sh)) {
803 simplices_number += num_simplices(sh->second.children());
804 }
805 }
806 return simplices_number;
807 }
808
815 int dimension(Siblings const* curr_sib) const {
816 int dim = -1;
817 while (curr_sib != nullptr) {
818 ++dim;
819 curr_sib = curr_sib->oncles();
820 }
821 return dim;
822 }
823
824 public:
826 std::vector<size_t> num_simplices_by_dimension() const {
827 if (is_empty()) return {};
828 // std::min in case the upper bound got crazy
829 std::vector<size_t> res(std::min(upper_bound_dimension()+1, max_dimension()+1));
830 auto fun = [&res](Simplex_handle, int dim) -> void { ++res[dim]; };
831 for_each_simplex(fun);
832 if (dimension_to_be_lowered_) {
833 GUDHI_CHECK(res.front() != 0, std::logic_error("Bug in Gudhi: non-empty complex has no vertex"));
834 while (res.back() == 0) res.pop_back();
835 dimension_ = static_cast<int>(res.size()) - 1;
836 dimension_to_be_lowered_ = false;
837 } else {
838 GUDHI_CHECK(res.back() != 0,
839 std::logic_error("Bug in Gudhi: there is no simplex of dimension the dimension of the complex"));
840 }
841 return res;
842 }
843
847 int dimension(Simplex_handle sh) const {
848 return dimension(self_siblings(sh));
849 }
850
856 return dimension_;
857 }
858
865 int dimension() const {
866 if (dimension_to_be_lowered_)
867 lower_upper_bound_dimension();
868 return dimension_;
869 }
870
873 template<class SimplexHandle>
874 bool has_children(SimplexHandle sh) const {
875 // Here we rely on the root using null_vertex(), which cannot match any real vertex.
876 return (sh->second.children()->parent() == sh->first);
877 }
878
879 private:
881
885 Siblings const* children(Simplex_handle sh) const {
886 GUDHI_CHECK(has_children(sh), std::invalid_argument("Simplex_tree::children - argument has no child"));
887 return sh->second.children();
888 }
889
890 public:
898 template<class InputVertexRange = std::initializer_list<Vertex_handle>>
899 Simplex_handle find(const InputVertexRange & s) const {
900 auto first = std::begin(s);
901 auto last = std::end(s);
902
903 if (first == last)
904 return null_simplex(); // ----->>
905
906 // Copy before sorting
907 std::vector<Vertex_handle> copy(first, last);
908 std::sort(std::begin(copy), std::end(copy));
909 return find_simplex(copy);
910 }
911
912 private:
914 Simplex_handle find_simplex(const std::vector<Vertex_handle> & simplex) const {
915 Siblings const* tmp_sib = &root_;
916 Dictionary_const_it tmp_dit;
917 auto vi = simplex.begin();
918 if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
919 // Equivalent to the first iteration of the normal loop
920 GUDHI_CHECK(contiguous_vertices(), "non-contiguous vertices");
921 Vertex_handle v = *vi++;
922 if(v < 0 || v >= static_cast<Vertex_handle>(root_.members_.size()))
923 return null_simplex();
924 tmp_dit = root_.members_.begin() + v;
925 if (vi == simplex.end())
926 return tmp_dit;
927 if (!has_children(tmp_dit))
928 return null_simplex();
929 tmp_sib = tmp_dit->second.children();
930 }
931 for (;;) {
932 tmp_dit = tmp_sib->members_.find(*vi++);
933 if (tmp_dit == tmp_sib->members_.end())
934 return null_simplex();
935 if (vi == simplex.end())
936 return tmp_dit;
937 if (!has_children(tmp_dit))
938 return null_simplex();
939 tmp_sib = tmp_dit->second.children();
940 }
941 }
942
945 Simplex_handle find_vertex(Vertex_handle v) const {
946 if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
947 assert(contiguous_vertices());
948 return root_.members_.begin() + v;
949 } else {
950 return root_.members_.find(v);
951 }
952 }
953
954 public:
956 bool contiguous_vertices() const {
957 if (root_.members_.empty()) return true;
958 if (root_.members_.begin()->first != 0) return false;
959 if (std::prev(root_.members_.end())->first != static_cast<Vertex_handle>(root_.members_.size() - 1)) return false;
960 return true;
961 }
962
963 protected:
978 template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>>
979 std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex,
981 Siblings * curr_sib = &root_;
982 std::pair<Dictionary_it, bool> res_insert;
983 auto vi = simplex.begin();
984 for (; vi != std::prev(simplex.end()); ++vi) {
985 GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
986 res_insert = curr_sib->members_.emplace(*vi, Node(curr_sib, filtration));
987 if (res_insert.second) {
988 // Only required when insertion is successful
989 update_simplex_tree_after_node_insertion(res_insert.first);
990 }
991 if (!(has_children(res_insert.first))) {
992 res_insert.first->second.assign_children(new Siblings(curr_sib, *vi));
993 }
994 curr_sib = res_insert.first->second.children();
995 }
996 GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
997 res_insert = curr_sib->members_.emplace(*vi, Node(curr_sib, filtration));
998 if (!res_insert.second) {
999 // if already in the complex
1000 if (res_insert.first->second.filtration() > filtration) {
1001 // if filtration value modified
1002 res_insert.first->second.assign_filtration(filtration);
1003 return res_insert;
1004 }
1005 // if filtration value unchanged
1006 return std::pair<Simplex_handle, bool>(null_simplex(), false);
1007 } else {
1008 // Only required when insertion is successful
1009 update_simplex_tree_after_node_insertion(res_insert.first);
1010 }
1011 // otherwise the insertion has succeeded - size is a size_type
1012 int dim = static_cast<int>(boost::size(simplex)) - 1;
1013 if (dim > dimension_) {
1014 // Update dimension if needed
1015 dimension_ = dim;
1016 }
1017 return res_insert;
1018 }
1019
1020 public:
1044 template<class InputVertexRange = std::initializer_list<Vertex_handle>>
1045 std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange & simplex,
1047 auto first = std::begin(simplex);
1048 auto last = std::end(simplex);
1049
1050 if (first == last)
1051 return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
1052
1053 // Copy before sorting
1054 std::vector<Vertex_handle> copy(first, last);
1055 std::sort(std::begin(copy), std::end(copy));
1056 return insert_simplex_raw(copy, filtration);
1057 }
1058
1073 template<class InputVertexRange = std::initializer_list<Vertex_handle>>
1074 std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(const InputVertexRange& Nsimplex,
1076 auto first = std::begin(Nsimplex);
1077 auto last = std::end(Nsimplex);
1078
1079 if (first == last)
1080 return { null_simplex(), true }; // FIXME: false would make more sense to me.
1081
1082 thread_local std::vector<Vertex_handle> copy;
1083 copy.clear();
1084 copy.insert(copy.end(), first, last);
1085 std::sort(copy.begin(), copy.end());
1086 auto last_unique = std::unique(copy.begin(), copy.end());
1087 copy.erase(last_unique, copy.end());
1088 GUDHI_CHECK_code(
1089 for (Vertex_handle v : copy)
1090 GUDHI_CHECK(v != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
1091 )
1092 // Update dimension if needed. We could wait to see if the insertion succeeds, but I doubt there is much to gain.
1093 dimension_ = (std::max)(dimension_, static_cast<int>(std::distance(copy.begin(), copy.end())) - 1);
1094
1095 return rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
1096 }
1097
1098 private:
1099 // To insert {1,2,3,4}, we insert {2,3,4} twice, once at the root, and once below 1.
1100 template<class ForwardVertexIterator>
1101 std::pair<Simplex_handle, bool> rec_insert_simplex_and_subfaces_sorted(Siblings* sib,
1102 ForwardVertexIterator first,
1103 ForwardVertexIterator last,
1104 Filtration_value filt) {
1105 // An alternative strategy would be:
1106 // - try to find the complete simplex, if found (and low filtration) exit
1107 // - insert all the vertices at once in sib
1108 // - loop over those (new or not) simplices, with a recursive call(++first, last)
1109 Vertex_handle vertex_one = *first;
1110 auto&& dict = sib->members();
1111 auto insertion_result = dict.emplace(vertex_one, Node(sib, filt));
1112 std::pair<Simplex_handle, bool> result(insertion_result); // const version of insertion_result for null_simplex()
1113
1114 auto simplex_one = insertion_result.first;
1115 bool one_is_new = insertion_result.second;
1116 if (one_is_new) {
1117 // update extra data structures if the insertion is successful
1118 update_simplex_tree_after_node_insertion(insertion_result.first);
1119 } else {
1120 if (filtration(simplex_one) > filt) {
1121 assign_filtration(simplex_one, filt);
1122 } else {
1123 // FIXME: this interface makes no sense, and it doesn't seem to be tested.
1124 result.first = null_simplex();
1125 }
1126 }
1127 if (++first == last) return result;
1128 if (!has_children(simplex_one))
1129 // TODO: have special code here, we know we are building the whole subtree from scratch.
1130 simplex_one->second.assign_children(new Siblings(sib, vertex_one));
1131 auto res = rec_insert_simplex_and_subfaces_sorted(simplex_one->second.children(), first, last, filt);
1132 // No need to continue if the full simplex was already there with a low enough filtration value.
1133 if (res.first != null_simplex()) rec_insert_simplex_and_subfaces_sorted(sib, first, last, filt);
1134 return res;
1135 }
1136
1137 public:
1141 _to_node_it(sh)->second.assign_key(key);
1142 }
1143
1147 std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) const {
1148 assert(dimension(sh) == 1);
1149 return { find_vertex(sh->first), find_vertex(self_siblings(sh)->parent()) };
1150 }
1151
1153 template<class SimplexHandle>
1154 Siblings const* self_siblings(SimplexHandle sh) const {
1155 if (sh->second.children()->parent() == sh->first)
1156 return sh->second.children()->oncles();
1157 else
1158 return sh->second.children();
1159 }
1160
1162 template<class SimplexHandle>
1163 Siblings* self_siblings(SimplexHandle sh) {
1164 // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
1165 // Doing it the other way is not safe
1166 return const_cast<Siblings*>(std::as_const(*this).self_siblings(sh));
1167 }
1168
1169 public:
1171 Siblings* root() { return &root_; }
1172
1174 const Siblings * root() const {
1175 return &root_;
1176 }
1177
1184 void set_dimension(int dimension, bool exact=true) {
1185 dimension_to_be_lowered_ = !exact;
1186 dimension_ = dimension;
1187 }
1188
1189 public:
1200 void initialize_filtration(bool ignore_infinite_values = false) const {
1201 filtration_vect_.clear();
1202 filtration_vect_.reserve(num_simplices());
1204 if (ignore_infinite_values &&
1205 std::numeric_limits<Filtration_value>::has_infinity &&
1206 filtration(sh) == std::numeric_limits<Filtration_value>::infinity()) continue;
1207 filtration_vect_.push_back(sh);
1208 }
1209
1210 /* We use stable_sort here because with libstdc++ it is faster than sort.
1211 * is_before_in_filtration is now a total order, but we used to call
1212 * stable_sort for the following heuristic:
1213 * The use of a depth-first traversal of the simplex tree, provided by
1214 * complex_simplex_range(), combined with a stable sort is meant to
1215 * optimize the order of simplices with same filtration value. The
1216 * heuristic consists in inserting the cofaces of a simplex as soon as
1217 * possible.
1218 */
1219#ifdef GUDHI_USE_TBB
1220 tbb::parallel_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration(this));
1221#else
1222 std::stable_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration(this));
1223#endif
1224 }
1232 if (filtration_vect_.empty()) {
1234 }
1235 }
1243 void clear_filtration() const {
1244 filtration_vect_.clear();
1245 }
1246
1247 private:
1261 void rec_coface(std::vector<Vertex_handle> &vertices, Siblings const*curr_sib, int curr_nbVertices,
1262 std::vector<Simplex_handle>& cofaces, bool star, int nbVertices) const {
1263 if (!(star || curr_nbVertices <= nbVertices)) // dimension of actual simplex <= nbVertices
1264 return;
1265 for (Simplex_handle simplex = curr_sib->members().begin(); simplex != curr_sib->members().end(); ++simplex) {
1266 if (vertices.empty()) {
1267 // If we reached the end of the vertices, and the simplex has more vertices than the given simplex
1268 // => we found a coface
1269
1270 // Add a coface if we want the star or if the number of vertices of the current simplex matches with nbVertices
1271 bool addCoface = (star || curr_nbVertices == nbVertices);
1272 if (addCoface)
1273 cofaces.push_back(simplex);
1274 if ((!addCoface || star) && has_children(simplex)) // Rec call
1275 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1276 } else {
1277 if (simplex->first == vertices.back()) {
1278 // If curr_sib matches with the top vertex
1279 bool equalDim = (star || curr_nbVertices == nbVertices); // dimension of actual simplex == nbVertices
1280 bool addCoface = vertices.size() == 1 && equalDim;
1281 if (addCoface)
1282 cofaces.push_back(simplex);
1283 if ((!addCoface || star) && has_children(simplex)) {
1284 // Rec call
1285 Vertex_handle tmp = vertices.back();
1286 vertices.pop_back();
1287 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1288 vertices.push_back(tmp);
1289 }
1290 } else if (simplex->first > vertices.back()) {
1291 return;
1292 } else {
1293 // (simplex->first < vertices.back()
1294 if (has_children(simplex))
1295 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1296 }
1297 }
1298 }
1299 }
1300
1301 public:
1311 return cofaces_simplex_range(simplex, 0);
1312 }
1313
1325 // codimension must be positive or null integer
1326 assert(codimension >= 0);
1327
1328 if constexpr (Options::link_nodes_by_label) {
1330 Static_vertex_vector simp(rg.begin(), rg.end());
1331 // must be sorted in decreasing order
1332 assert(std::is_sorted(simp.begin(), simp.end(), std::greater<Vertex_handle>()));
1333 auto range = Optimized_star_simplex_range(Optimized_star_simplex_iterator(this, std::move(simp)),
1335 // Lazy filtered range
1336 Fast_cofaces_predicate select(this, codimension, this->dimension(simplex));
1337 return boost::adaptors::filter(range, select);
1338 } else {
1339 Cofaces_simplex_range cofaces;
1341 std::vector<Vertex_handle> copy(rg.begin(), rg.end());
1342 if (codimension + static_cast<int>(copy.size()) > dimension_ + 1 ||
1343 (codimension == 0 && static_cast<int>(copy.size()) > dimension_)) // n+codimension greater than dimension_
1344 return cofaces;
1345 // must be sorted in decreasing order
1346 assert(std::is_sorted(copy.begin(), copy.end(), std::greater<Vertex_handle>()));
1347 bool star = codimension == 0;
1348 rec_coface(copy, &root_, 1, cofaces, star, codimension + static_cast<int>(copy.size()));
1349 return cofaces;
1350 }
1351 }
1352
1353 private:
1361 bool reverse_lexicographic_order(Simplex_handle sh1, Simplex_handle sh2) const {
1364 Simplex_vertex_iterator it1 = rg1.begin();
1365 Simplex_vertex_iterator it2 = rg2.begin();
1366 while (it1 != rg1.end() && it2 != rg2.end()) {
1367 if (*it1 == *it2) {
1368 ++it1;
1369 ++it2;
1370 } else {
1371 return *it1 < *it2;
1372 }
1373 }
1374 return ((it1 == rg1.end()) && (it2 != rg2.end()));
1375 }
1376
1383 struct is_before_in_filtration {
1384 explicit is_before_in_filtration(Simplex_tree const* st)
1385 : st_(st) { }
1386
1387 bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const {
1388 // Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
1389 if (sh1->second.filtration() != sh2->second.filtration()) {
1390 return sh1->second.filtration() < sh2->second.filtration();
1391 }
1392 // is sh1 a proper subface of sh2
1393 return st_->reverse_lexicographic_order(sh1, sh2);
1394 }
1395
1396 Simplex_tree const* st_;
1397 };
1398
1399 public:
1423 template<class OneSkeletonGraph>
1424 void insert_graph(const OneSkeletonGraph& skel_graph) {
1425 // the simplex tree must be empty
1426 assert(num_simplices() == 0);
1427
1428 // is there a better way to let the compiler know that we don't mean Simplex_tree::num_vertices?
1429 using boost::num_vertices;
1430
1431 if (num_vertices(skel_graph) == 0) {
1432 return;
1433 }
1434 if (num_edges(skel_graph) == 0) {
1435 dimension_ = 0;
1436 } else {
1437 dimension_ = 1;
1438 }
1439
1440 if constexpr (!Options::stable_simplex_handles)
1441 root_.members_.reserve(num_vertices(skel_graph)); // probably useless in most cases
1442 auto verts = vertices(skel_graph) | boost::adaptors::transformed([&](auto v){
1443 return Dit_value_t(v, Node(&root_, get(vertex_filtration_t(), skel_graph, v))); });
1444 root_.members_.insert(boost::begin(verts), boost::end(verts));
1445 // This automatically sorts the vertices, the graph concept doesn't guarantee the order in which we iterate.
1446
1447 for (Dictionary_it it = boost::begin(root_.members_); it != boost::end(root_.members_); it++) {
1448 update_simplex_tree_after_node_insertion(it);
1449 }
1450
1451 std::pair<typename boost::graph_traits<OneSkeletonGraph>::edge_iterator,
1452 typename boost::graph_traits<OneSkeletonGraph>::edge_iterator> boost_edges = edges(skel_graph);
1453 // boost_edges.first is the equivalent to boost_edges.begin()
1454 // boost_edges.second is the equivalent to boost_edges.end()
1455 for (; boost_edges.first != boost_edges.second; boost_edges.first++) {
1456 auto edge = *(boost_edges.first);
1457 auto u = source(edge, skel_graph);
1458 auto v = target(edge, skel_graph);
1459 if (u == v) throw std::invalid_argument("Self-loops are not simplicial");
1460 // We cannot skip edges with the wrong orientation and expect them to
1461 // come a second time with the right orientation, that does not always
1462 // happen in practice. emplace() should be a NOP when an element with the
1463 // same key is already there, so seeing the same edge multiple times is
1464 // ok.
1465 // Should we actually forbid multiple edges? That would be consistent
1466 // with rejecting self-loops.
1467 if (v < u) std::swap(u, v);
1468 auto sh = _to_node_it(find_vertex(u));
1469 if (!has_children(sh)) {
1470 sh->second.assign_children(new Siblings(&root_, sh->first));
1471 }
1472
1473 auto insertion_res = sh->second.children()->members().emplace(
1474 v, Node(sh->second.children(), get(edge_filtration_t(), skel_graph, edge)));
1475 if (insertion_res.second) update_simplex_tree_after_node_insertion(insertion_res.first);
1476 }
1477 }
1478
1486 template <class VertexRange>
1487 void insert_batch_vertices(VertexRange const& vertices, Filtration_value filt = 0) {
1488 auto verts = vertices | boost::adaptors::transformed([&](auto v){
1489 return Dit_value_t(v, Node(&root_, filt)); });
1490 root_.members_.insert(boost::begin(verts), boost::end(verts));
1491 if (dimension_ < 0 && !root_.members_.empty()) dimension_ = 0;
1492 if constexpr (Options::link_nodes_by_label) {
1493 for (auto sh = root_.members().begin(); sh != root_.members().end(); sh++) {
1494 // update newly inserted simplex (the one that are not linked)
1495 if (!sh->second.list_max_vertex_hook_.is_linked())
1496 update_simplex_tree_after_node_insertion(sh);
1497 }
1498 }
1499 }
1500
1512 void expansion(int max_dim) {
1513 if (max_dim <= 1) return;
1514 clear_filtration(); // Drop the cache.
1515 dimension_ = max_dim;
1516 for (Dictionary_it root_it = root_.members_.begin();
1517 root_it != root_.members_.end(); ++root_it) {
1518 if (has_children(root_it)) {
1519 siblings_expansion(root_it->second.children(), max_dim - 1);
1520 }
1521 }
1522 dimension_ = max_dim - dimension_;
1523 }
1524
1560 , Vertex_handle v
1561 , Filtration_value fil
1562 , int dim_max
1563 , std::vector<Simplex_handle>& added_simplices)
1564 {
1584 static_assert(Options::link_nodes_by_label, "Options::link_nodes_by_label must be true");
1585
1586 if (u == v) { // Are we inserting a vertex?
1587 auto res_ins = root_.members().emplace(u, Node(&root_,fil));
1588 if (res_ins.second) { //if the vertex is not in the complex, insert it
1589 added_simplices.push_back(res_ins.first); //no more insert in root_.members()
1590 update_simplex_tree_after_node_insertion(res_ins.first);
1591 if (dimension_ == -1) dimension_ = 0;
1592 }
1593 return; //because the vertex is isolated, no more insertions.
1594 }
1595 // else, we are inserting an edge: ensure that u < v
1596 if (v < u) { std::swap(u,v); }
1597
1598 //Note that we copy Simplex_handle (aka map iterators) in added_simplices
1599 //while we are still modifying the Simplex_tree. Insertions in siblings may
1600 //invalidate Simplex_handles; we take care of this fact by first doing all
1601 //insertion in a Sibling, then inserting all handles in added_simplices.
1602
1603#ifdef GUDHI_DEBUG
1604 //check whether vertices u and v are in the tree. If not, return an error.
1605 auto sh_u = root_.members().find(u);
1606 GUDHI_CHECK(sh_u != root_.members().end() &&
1607 root_.members().find(v) != root_.members().end(),
1608 std::invalid_argument(
1609 "Simplex_tree::insert_edge_as_flag - inserts an edge whose vertices are not in the complex")
1610 );
1611 GUDHI_CHECK(!has_children(sh_u) ||
1612 sh_u->second.children()->members().find(v) == sh_u->second.children()->members().end(),
1613 std::invalid_argument(
1614 "Simplex_tree::insert_edge_as_flag - inserts an already existing edge")
1615 );
1616#endif
1617
1618 // to update dimension
1619 const auto tmp_dim = dimension_;
1620 auto tmp_max_dim = dimension_;
1621
1622 //for all siblings containing a Node labeled with u (including the root), run
1623 //compute_punctual_expansion
1624 //todo parallelize
1625 List_max_vertex* nodes_with_label_u = nodes_by_label(u);//all Nodes with u label
1626
1627 GUDHI_CHECK(nodes_with_label_u != nullptr,
1628 "Simplex_tree::insert_edge_as_flag - cannot find the list of Nodes with label u");
1629
1630 for (auto&& node_as_hook : *nodes_with_label_u)
1631 {
1632 Node& node_u = static_cast<Node&>(node_as_hook); //corresponding node, has label u
1633 Simplex_handle sh_u = simplex_handle_from_node(node_u);
1634 Siblings* sib_u = self_siblings(sh_u);
1635 if (sib_u->members().find(v) != sib_u->members().end()) { //v is the label of a sibling of node_u
1636 int curr_dim = dimension(sib_u);
1637 if (dim_max == -1 || curr_dim < dim_max){
1638 if (!has_children(sh_u)) {
1639 //then node_u was a leaf and now has a new child Node labeled v
1640 //the child v is created in compute_punctual_expansion
1641 node_u.assign_children(new Siblings(sib_u, u));
1642 }
1643 dimension_ = dim_max - curr_dim - 1;
1644 compute_punctual_expansion(
1645 v,
1646 node_u.children(),
1647 fil,
1648 dim_max - curr_dim - 1, //>= 0 if dim_max >= 0, <0 otherwise
1649 added_simplices );
1650 dimension_ = dim_max - dimension_;
1651 if (dimension_ > tmp_max_dim) tmp_max_dim = dimension_;
1652 }
1653 }
1654 }
1655 if (tmp_dim <= tmp_max_dim){
1656 dimension_ = tmp_max_dim;
1657 dimension_to_be_lowered_ = false;
1658 } else {
1659 dimension_ = tmp_dim;
1660 }
1661 }
1662
1663 private:
1673 void compute_punctual_expansion( Vertex_handle v
1674 , Siblings * sib
1675 , Filtration_value fil
1676 , int k //k == dim_max - dimension simplices in sib
1677 , std::vector<Simplex_handle>& added_simplices )
1678 { //insertion always succeeds because the edge {u,v} used to not be here.
1679 auto res_ins_v = sib->members().emplace(v, Node(sib,fil));
1680 added_simplices.push_back(res_ins_v.first); //no more insertion in sib
1681 update_simplex_tree_after_node_insertion(res_ins_v.first);
1682
1683 if (k == 0) { // reached the maximal dimension. if max_dim == -1, k is never equal to 0.
1684 dimension_ = 0; // to keep track of the max height of the recursion tree
1685 return;
1686 }
1687
1688 //create the subtree of new Node(v)
1689 create_local_expansion( res_ins_v.first
1690 , sib
1691 , fil
1692 , k
1693 , added_simplices );
1694
1695 //punctual expansion in nodes on the left of v, i.e. with label x < v
1696 for (auto sh = sib->members().begin(); sh != res_ins_v.first; ++sh)
1697 { //if v belongs to N^+(x), punctual expansion
1698 Simplex_handle root_sh = find_vertex(sh->first); //Node(x), x < v
1699 if (has_children(root_sh) &&
1700 root_sh->second.children()->members().find(v) != root_sh->second.children()->members().end())
1701 { //edge {x,v} is in the complex
1702 if (!has_children(sh)){
1703 sh->second.assign_children(new Siblings(sib, sh->first));
1704 }
1705 //insert v in the children of sh, and expand.
1706 compute_punctual_expansion( v
1707 , sh->second.children()
1708 , fil
1709 , k-1
1710 , added_simplices );
1711 }
1712 }
1713 }
1714
1721 void create_local_expansion(
1722 Dictionary_it sh_v //Node with label v which has just been inserted
1723 , Siblings * curr_sib //Siblings containing the node sh_v
1724 , Filtration_value fil_uv //Fil value of the edge uv in the zz filtration
1725 , int k //Stopping condition for recursion based on max dim
1726 , std::vector<Simplex_handle> &added_simplices) //range of all new simplices
1727 { //pick N^+(v)
1728 //intersect N^+(v) with labels y > v in curr_sib
1729 Dictionary_it next_it = sh_v;
1730 ++next_it;
1731
1732 if (dimension_ > k) {
1733 dimension_ = k; //to keep track of the max height of the recursion tree
1734 }
1735
1736 create_expansion<true>(curr_sib, sh_v, next_it, fil_uv, k, &added_simplices);
1737 }
1738 //TODO boost::container::ordered_unique_range_t in the creation of a Siblings
1739
1748 void siblings_expansion(
1749 Siblings * siblings // must contain elements
1750 , Filtration_value fil
1751 , int k // == max_dim expansion - dimension curr siblings
1752 , std::vector<Simplex_handle> & added_simplices )
1753 {
1754 if (dimension_ > k) {
1755 dimension_ = k; //to keep track of the max height of the recursion tree
1756 }
1757 if (k == 0) { return; } //max dimension
1758 Dictionary_it next = ++(siblings->members().begin());
1759
1760 for (Dictionary_it s_h = siblings->members().begin();
1761 next != siblings->members().end(); ++s_h, ++next)
1762 { //find N^+(s_h)
1763 create_expansion<true>(siblings, s_h, next, fil, k, &added_simplices);
1764 }
1765 }
1766
1769 void siblings_expansion(Siblings * siblings, // must contain elements
1770 int k) {
1771 if (k >= 0 && dimension_ > k) {
1772 dimension_ = k;
1773 }
1774 if (k == 0)
1775 return;
1776 Dictionary_it next = siblings->members().begin();
1777 ++next;
1778
1779 for (Dictionary_it s_h = siblings->members().begin();
1780 s_h != siblings->members().end(); ++s_h, ++next)
1781 {
1782 create_expansion<false>(siblings, s_h, next, s_h->second.filtration(), k);
1783 }
1784 }
1785
1790 template<bool force_filtration_value>
1791 void create_expansion(Siblings * siblings,
1792 Dictionary_it& s_h,
1793 Dictionary_it& next,
1794 Filtration_value fil,
1795 int k,
1796 std::vector<Simplex_handle>* added_simplices = nullptr)
1797 {
1798 Simplex_handle root_sh = find_vertex(s_h->first);
1799 thread_local std::vector<std::pair<Vertex_handle, Node> > inter;
1800
1801 if (!has_children(root_sh)) return;
1802
1803 intersection<force_filtration_value>(
1804 inter, // output intersection
1805 next, // begin
1806 siblings->members().end(), // end
1807 root_sh->second.children()->members().begin(),
1808 root_sh->second.children()->members().end(),
1809 fil);
1810 if (inter.size() != 0) {
1811 Siblings * new_sib = new Siblings(siblings, // oncles
1812 s_h->first, // parent
1813 inter); // boost::container::ordered_unique_range_t
1814 for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) {
1815 update_simplex_tree_after_node_insertion(it);
1816 if constexpr (force_filtration_value){
1817 //the way create_expansion is used, added_simplices != nullptr when force_filtration_value == true
1818 added_simplices->push_back(it);
1819 }
1820 }
1821 inter.clear();
1822 s_h->second.assign_children(new_sib);
1823 if constexpr (force_filtration_value){
1824 siblings_expansion(new_sib, fil, k - 1, *added_simplices);
1825 } else {
1826 siblings_expansion(new_sib, k - 1);
1827 }
1828 } else {
1829 // ensure the children property
1830 s_h->second.assign_children(siblings);
1831 inter.clear();
1832 }
1833 }
1834
1837 template<bool force_filtration_value = false>
1838 static void intersection(std::vector<std::pair<Vertex_handle, Node> >& intersection,
1839 Dictionary_const_it begin1, Dictionary_const_it end1,
1840 Dictionary_const_it begin2, Dictionary_const_it end2,
1841 Filtration_value filtration_) {
1842 if (begin1 == end1 || begin2 == end2)
1843 return; // ----->>
1844 while (true) {
1845 if (begin1->first == begin2->first) {
1846 if constexpr (force_filtration_value){
1847 intersection.emplace_back(begin1->first, Node(nullptr, filtration_));
1848 } else {
1849 Filtration_value filt = (std::max)({begin1->second.filtration(), begin2->second.filtration(), filtration_});
1850 intersection.emplace_back(begin1->first, Node(nullptr, filt));
1851 }
1852 if (++begin1 == end1 || ++begin2 == end2)
1853 return; // ----->>
1854 } else if (begin1->first < begin2->first) {
1855 if (++begin1 == end1)
1856 return;
1857 } else /* begin1->first > begin2->first */ {
1858 if (++begin2 == end2)
1859 return; // ----->>
1860 }
1861 }
1862 }
1863
1864 public:
1883 template< typename Blocker >
1884 void expansion_with_blockers(int max_dim, Blocker block_simplex) {
1885 // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
1886 for (auto& simplex : boost::adaptors::reverse(root_.members())) {
1887 if (has_children(&simplex)) {
1888 siblings_expansion_with_blockers(simplex.second.children(), max_dim, max_dim - 1, block_simplex);
1889 }
1890 }
1891 }
1892
1893 private:
1895 template< typename Blocker >
1896 void siblings_expansion_with_blockers(Siblings* siblings, int max_dim, int k, Blocker block_simplex) {
1897 if (dimension_ < max_dim - k) {
1898 dimension_ = max_dim - k;
1899 }
1900 if (k == 0)
1901 return;
1902 // No need to go deeper
1903 if (siblings->members().size() < 2)
1904 return;
1905 // Reverse loop starting before the last one for 'next' to be the last one
1906 for (auto simplex = std::next(siblings->members().rbegin()); simplex != siblings->members().rend(); simplex++) {
1907 std::vector<std::pair<Vertex_handle, Node> > intersection;
1908 for(auto next = siblings->members().rbegin(); next != simplex; next++) {
1909 bool to_be_inserted = true;
1910 Filtration_value filt = simplex->second.filtration();
1911 // If all the boundaries are present, 'next' needs to be inserted
1912 for (Simplex_handle border : boundary_simplex_range(simplex)) {
1913 Simplex_handle border_child = find_child(border, next->first);
1914 if (border_child == null_simplex()) {
1915 to_be_inserted=false;
1916 break;
1917 }
1918 filt = (std::max)(filt, filtration(border_child));
1919 }
1920 if (to_be_inserted) {
1921 intersection.emplace_back(next->first, Node(nullptr, filt));
1922 }
1923 }
1924 if (intersection.size() != 0) {
1925 // Reverse the order to insert
1926 Siblings * new_sib = new Siblings(
1927 siblings, // oncles
1928 simplex->first, // parent
1929 boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t
1930 simplex->second.assign_children(new_sib);
1931 std::vector<Vertex_handle> blocked_new_sib_vertex_list;
1932 // As all intersections are inserted, we can call the blocker function on all new_sib members
1933 for (auto new_sib_member = new_sib->members().begin();
1934 new_sib_member != new_sib->members().end();
1935 new_sib_member++) {
1936 update_simplex_tree_after_node_insertion(new_sib_member);
1937 bool blocker_result = block_simplex(new_sib_member);
1938 // new_sib member has been blocked by the blocker function
1939 // add it to the list to be removed - do not perform it while looping on it
1940 if (blocker_result) {
1941 blocked_new_sib_vertex_list.push_back(new_sib_member->first);
1942 // update data structures for all deleted simplices
1943 // can be done in the loop as part of another data structure
1944 update_simplex_tree_before_node_removal(new_sib_member);
1945 }
1946 }
1947 if (blocked_new_sib_vertex_list.size() == new_sib->members().size()) {
1948 // Specific case where all have to be deleted
1949 delete new_sib;
1950 // ensure the children property
1951 simplex->second.assign_children(siblings);
1952 } else {
1953 for (auto& blocked_new_sib_member : blocked_new_sib_vertex_list) {
1954 new_sib->members().erase(blocked_new_sib_member);
1955 }
1956 // ensure recursive call
1957 siblings_expansion_with_blockers(new_sib, max_dim, k - 1, block_simplex);
1958 }
1959 } else {
1960 // ensure the children property
1961 simplex->second.assign_children(siblings);
1962 }
1963 }
1964 }
1965
1970 Simplex_handle find_child(Simplex_handle sh, Vertex_handle vh) const {
1971 if (!has_children(sh))
1972 return null_simplex();
1973
1974 Simplex_handle child = sh->second.children()->find(vh);
1975 // Specific case of boost::flat_map does not find, returns boost::flat_map::end()
1976 // in simplex tree we want a null_simplex()
1977 if (child == sh->second.children()->members().end())
1978 return null_simplex();
1979
1980 return child;
1981 }
1982
1983 public:
1993 void print_hasse(std::ostream& os) const {
1994 os << num_simplices() << " " << std::endl;
1995 // TODO: should use complex_simplex_range ?
1996 for (auto sh : filtration_simplex_range()) {
1997 os << dimension(sh) << " ";
1998 for (auto b_sh : boundary_simplex_range(sh)) {
1999 os << key(b_sh) << " ";
2000 }
2001 os << filtration(sh) << " \n";
2002 }
2003 }
2004
2005 public:
2014 template<class Fun>
2015 void for_each_simplex(Fun&& fun) const {
2016 // Wrap callback so it always returns bool
2017 auto f = [&fun](Simplex_handle sh, int dim) -> bool {
2018 if constexpr (std::is_same_v<void, decltype(fun(sh, dim))>) {
2019 fun(sh, dim);
2020 return false;
2021 } else {
2022 return fun(sh, dim);
2023 }
2024 };
2025 if (!is_empty())
2026 rec_for_each_simplex(root(), 0, f);
2027 }
2028
2029 private:
2030 template<class Fun>
2031 void rec_for_each_simplex(const Siblings* sib, int dim, Fun&& fun) const {
2032 Simplex_handle sh = sib->members().end();
2033 GUDHI_CHECK(sh != sib->members().begin(), "Bug in Gudhi: only the root siblings may be empty");
2034 do {
2035 --sh;
2036 if (!fun(sh, dim) && has_children(sh)) {
2037 rec_for_each_simplex(sh->second.children(), dim+1, fun);
2038 }
2039 // We could skip checking has_children for the first element of the iteration, we know it returns false.
2040 }
2041 while(sh != sib->members().begin());
2042 }
2043
2044 public:
2052 bool modified = false;
2053 auto fun = [&modified, this](Simplex_handle sh, int dim) -> void {
2054 if (dim == 0) return;
2055 // Find the maximum filtration value in the border
2057 Boundary_simplex_iterator max_border = std::max_element(std::begin(boundary), std::end(boundary),
2058 [this](Simplex_handle sh1, Simplex_handle sh2) {
2059 return filtration(sh1) < filtration(sh2);
2060 });
2061
2062 Filtration_value max_filt_border_value = filtration(*max_border);
2063 // Replacing if(f<max) with if(!(f>=max)) would mean that if f is NaN, we replace it with the max of the children.
2064 // That seems more useful than keeping NaN.
2065 if (!(sh->second.filtration() >= max_filt_border_value)) {
2066 // Store the filtration modification information
2067 modified = true;
2068 assign_filtration(sh, max_filt_border_value);
2069 }
2070 };
2071 // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
2072 for_each_simplex(fun);
2073
2074 if(modified)
2075 clear_filtration(); // Drop the cache.
2076 return modified;
2077 }
2078
2079 public:
2081 void clear() {
2082 root_members_recursive_deletion();
2084 dimension_ = -1;
2085 dimension_to_be_lowered_ = false;
2086 if constexpr (Options::link_nodes_by_label)
2087 nodes_label_to_list_.clear();
2088 }
2089
2098 if (std::numeric_limits<Filtration_value>::has_infinity && filtration == std::numeric_limits<Filtration_value>::infinity())
2099 return false; // ---->>
2100 bool modified = rec_prune_above_filtration(root(), filtration);
2101 if(modified)
2102 clear_filtration(); // Drop the cache.
2103 return modified;
2104 }
2105
2106 private:
2107 bool rec_prune_above_filtration(Siblings* sib, Filtration_value filt) {
2108 auto&& list = sib->members();
2109 bool modified = false;
2110 bool emptied = false;
2111 Simplex_handle last;
2112
2113 auto to_remove = [this, filt](Dit_value_t& simplex) {
2114 if (simplex.second.filtration() <= filt) return false;
2115 if (has_children(&simplex)) rec_delete(simplex.second.children());
2116 // dimension may need to be lowered
2117 dimension_to_be_lowered_ = true;
2118 return true;
2119 };
2120
2121 //TODO: `if constexpr` replaceable by `std::erase_if` in C++20? Has a risk of additional runtime,
2122 //so to benchmark first.
2123 if constexpr (Options::stable_simplex_handles) {
2124 modified = false;
2125 for (auto sh = list.begin(); sh != list.end();) {
2126 if (to_remove(*sh)) {
2127 sh = list.erase(sh);
2128 modified = true;
2129 } else {
2130 ++sh;
2131 }
2132 }
2133 emptied = (list.empty() && sib != root());
2134 } else {
2135 last = std::remove_if(list.begin(), list.end(), to_remove);
2136 modified = (last != list.end());
2137 emptied = (last == list.begin() && sib != root());
2138 }
2139
2140 if (emptied) {
2141 // Removing the whole siblings, parent becomes a leaf.
2142 sib->oncles()->members()[sib->parent()].assign_children(sib->oncles());
2143 delete sib;
2144 // dimension may need to be lowered
2145 dimension_to_be_lowered_ = true;
2146 return true;
2147 } else {
2148 // Keeping some elements of siblings. Remove the others, and recurse in the remaining ones.
2149 if constexpr (!Options::stable_simplex_handles) list.erase(last, list.end());
2150 for (auto&& simplex : list)
2151 if (has_children(&simplex)) modified |= rec_prune_above_filtration(simplex.second.children(), filt);
2152 }
2153
2154 return modified;
2155 }
2156
2157 public:
2163 if (dimension >= dimension_)
2164 return false;
2165
2166 bool modified = false;
2167 if (dimension < 0) {
2168 if (num_vertices() > 0) {
2169 root_members_recursive_deletion();
2170 modified = true;
2171 }
2172 // Force dimension to -1, in case user calls `prune_above_dimension(-10)`
2173 dimension = -1;
2174 } else {
2175 modified = rec_prune_above_dimension(root(), dimension, 0);
2176 }
2177 if(modified) {
2178 // Thanks to `if (dimension >= dimension_)` and dimension forced to -1 `if (dimension < 0)`, we know the new dimension
2179 dimension_ = dimension;
2180 clear_filtration(); // Drop the cache.
2181 }
2182 return modified;
2183 }
2184
2185 private:
2186 bool rec_prune_above_dimension(Siblings* sib, int dim, int actual_dim) {
2187 bool modified = false;
2188 auto&& list = sib->members();
2189
2190 for (auto&& simplex : list)
2191 if (has_children(&simplex)) {
2192 if (actual_dim >= dim) {
2193 rec_delete(simplex.second.children());
2194 simplex.second.assign_children(sib);
2195 modified = true;
2196 } else {
2197 modified |= rec_prune_above_dimension(simplex.second.children(), dim, actual_dim + 1);
2198 }
2199 }
2200
2201 return modified;
2202 }
2203
2204 private:
2210 bool lower_upper_bound_dimension() const {
2211 // reset automatic detection to recompute
2212 dimension_to_be_lowered_ = false;
2213 int new_dimension = -1;
2214 // Browse the tree from the left to the right as higher dimension cells are more likely on the left part of the tree
2215 for (Simplex_handle sh : complex_simplex_range()) {
2216#ifdef DEBUG_TRACES
2217 for (auto vertex : simplex_vertex_range(sh)) {
2218 std::clog << " " << vertex;
2219 }
2220 std::clog << std::endl;
2221#endif // DEBUG_TRACES
2222
2223 int sh_dimension = dimension(sh);
2224 if (sh_dimension >= dimension_)
2225 // Stop browsing as soon as the dimension is reached, no need to go further
2226 return false;
2227 new_dimension = (std::max)(new_dimension, sh_dimension);
2228 }
2229 dimension_ = new_dimension;
2230 return true;
2231 }
2232
2233
2234 public:
2244 // Guarantee the simplex has no children
2245 GUDHI_CHECK(!has_children(sh),
2246 std::invalid_argument("Simplex_tree::remove_maximal_simplex - argument has children"));
2247
2248 update_simplex_tree_before_node_removal(sh);
2249
2250 // Simplex is a leaf, it means the child is the Siblings owning the leaf
2251 Dictionary_it nodeIt = _to_node_it(sh);
2252 Siblings* child = nodeIt->second.children();
2253
2254 if ((child->size() > 1) || (child == root())) {
2255 // Not alone, just remove it from members
2256 // Special case when child is the root of the simplex tree, just remove it from members
2257 child->erase(nodeIt);
2258 } else {
2259 // Sibling is emptied : must be deleted, and its parent must point on his own Sibling
2260 child->oncles()->members().at(child->parent()).assign_children(child->oncles());
2261 delete child;
2262 // dimension may need to be lowered
2263 dimension_to_be_lowered_ = true;
2264 }
2265 }
2266
2283 std::pair<Filtration_value, Extended_simplex_type> decode_extended_filtration(Filtration_value f, const Extended_filtration_data& efd) const{
2284 std::pair<Filtration_value, Extended_simplex_type> p;
2285 Filtration_value minval = efd.minval;
2286 Filtration_value maxval = efd.maxval;
2287 if (f >= -2 && f <= -1) {
2288 p.first = minval + (maxval-minval)*(f + 2); p.second = Extended_simplex_type::UP;
2289 } else if (f >= 1 && f <= 2) {
2290 p.first = minval - (maxval-minval)*(f - 2); p.second = Extended_simplex_type::DOWN;
2291 } else {
2292 p.first = std::numeric_limits<Filtration_value>::quiet_NaN(); p.second = Extended_simplex_type::EXTRA;
2293 }
2294 return p;
2295 };
2296
2310 Extended_filtration_data extend_filtration() {
2311 clear_filtration(); // Drop the cache.
2312
2313 // Compute maximum and minimum of filtration values
2314 Vertex_handle maxvert = std::numeric_limits<Vertex_handle>::min();
2315 Filtration_value minval = std::numeric_limits<Filtration_value>::infinity();
2316 Filtration_value maxval = -std::numeric_limits<Filtration_value>::infinity();
2317 for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
2318 Filtration_value f = this->filtration(sh);
2319 minval = std::min(minval, f);
2320 maxval = std::max(maxval, f);
2321 maxvert = std::max(sh->first, maxvert);
2322 }
2323
2324 GUDHI_CHECK(maxvert < std::numeric_limits<Vertex_handle>::max(), std::invalid_argument("Simplex_tree contains a vertex with the largest Vertex_handle"));
2325 maxvert++;
2326
2327 Simplex_tree st_copy = *this;
2328
2329 // Add point for coning the simplicial complex
2330 this->insert_simplex_raw({maxvert}, -3);
2331
2332 Filtration_value scale = maxval-minval;
2333 if (scale != 0)
2334 scale = 1 / scale;
2335
2336 // For each simplex
2337 std::vector<Vertex_handle> vr;
2338 for (auto sh_copy : st_copy.complex_simplex_range()) {
2339 auto&& simplex_range = st_copy.simplex_vertex_range(sh_copy);
2340 vr.assign(simplex_range.begin(), simplex_range.end());
2341 auto sh = this->find(vr);
2342
2343 // Create cone on simplex
2344 vr.push_back(maxvert);
2345 if (this->dimension(sh) == 0) {
2346 Filtration_value v = this->filtration(sh);
2347 Filtration_value scaled_v = (v - minval) * scale;
2348 // Assign ascending value between -2 and -1 to vertex
2349 this->assign_filtration(sh, -2 + scaled_v);
2350 // Assign descending value between 1 and 2 to cone on vertex
2351 this->insert_simplex(vr, 2 - scaled_v);
2352 } else {
2353 // Assign value -3 to simplex and cone on simplex
2354 this->assign_filtration(sh, -3);
2355 this->insert_simplex(vr, -3);
2356 }
2357 }
2358
2359 // Automatically assign good values for simplices
2361
2362 // Return the filtration data
2363 return Extended_filtration_data(minval, maxval);
2364 }
2365
2371 auto filt = filtration_(sh);
2372 for(auto v : simplex_vertex_range(sh))
2373 if(filtration_(find_vertex(v)) == filt)
2374 return v;
2375 return null_vertex();
2376 }
2377
2385 // See issue #251 for potential speed improvements.
2386 auto&& vertices = simplex_vertex_range(sh); // vertices in decreasing order
2387 auto end = std::end(vertices);
2388 auto vi = std::begin(vertices);
2389 GUDHI_CHECK(vi != end, "empty simplex");
2390 auto v0 = *vi;
2391 ++vi;
2392 GUDHI_CHECK(vi != end, "simplex of dimension 0");
2393 if(std::next(vi) == end) return sh; // shortcut for dimension 1
2394 Static_vertex_vector suffix;
2395 suffix.push_back(v0);
2396 auto filt = filtration_(sh);
2397 do
2398 {
2399 Vertex_handle v = *vi;
2400 auto&& children1 = find_vertex(v)->second.children()->members_;
2401 for(auto w : suffix){
2402 // Can we take advantage of the fact that suffix is ordered?
2403 Simplex_handle s = children1.find(w);
2404 if(filtration_(s) == filt)
2405 return s;
2406 }
2407 suffix.push_back(v);
2408 }
2409 while(++vi != end);
2410 return null_simplex();
2411 }
2412
2418 auto filt = filtration_(sh);
2419 // Naive implementation, it can be sped up.
2420 for(auto b : boundary_simplex_range(sh))
2421 if(filtration_(b) == filt)
2423 return sh; // None of its faces has the same filtration.
2424 }
2425
2426 public:
2427 // intrusive list of Nodes with same label using the hooks
2428 typedef boost::intrusive::member_hook<Hooks_simplex_base_link_nodes, typename Hooks_simplex_base_link_nodes::Member_hook_t,
2429 &Hooks_simplex_base_link_nodes::list_max_vertex_hook_>
2430 List_member_hook_t;
2431 // auto_unlink in Member_hook_t is incompatible with constant time size
2432 typedef boost::intrusive::list<Hooks_simplex_base_link_nodes, List_member_hook_t,
2433 boost::intrusive::constant_time_size<false>> List_max_vertex;
2434 // type of hooks stored in each Node, Node inherits from Hooks_simplex_base
2435 typedef typename std::conditional<Options::link_nodes_by_label, Hooks_simplex_base_link_nodes,
2436 Hooks_simplex_base_dummy>::type Hooks_simplex_base;
2437
2440 private:
2441 // if Options::link_nodes_by_label is true, store the lists of Nodes with same label, empty otherwise.
2442 // unordered_map Vertex_handle v -> list of all Nodes with label v.
2443 std::unordered_map<Vertex_handle, List_max_vertex> nodes_label_to_list_;
2444
2445 List_max_vertex* nodes_by_label(Vertex_handle v) {
2446 // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
2447 // Doing it the other way is not safe
2448 return const_cast<List_max_vertex*>(std::as_const(*this).nodes_by_label(v));
2449 }
2450
2451 List_max_vertex const* nodes_by_label(Vertex_handle v) const {
2452 if constexpr (Options::link_nodes_by_label) {
2453 auto it_v = nodes_label_to_list_.find(v);
2454 if (it_v != nodes_label_to_list_.end()) {
2455 return &(it_v->second);
2456 } else {
2457 return nullptr;
2458 }
2459 }
2460 return nullptr;
2461 }
2462
2465 static Simplex_handle simplex_handle_from_node(const Node& node) {
2466 if constexpr (Options::stable_simplex_handles){
2467 //Relies on the Dictionary type to be boost::container::map<Vertex_handle, Node>.
2468 //If the type changes or boost fundamentally changes something on the structure of their map,
2469 //a safer/more general but much slower version is:
2470 // if (node.children()->parent() == label) { // verifies if node is a leaf
2471 // return children->oncles()->find(label);
2472 // } else {
2473 // return children->members().find(label);
2474 // }
2475 //Requires an additional parameter "Vertex_handle label" which is the label of the node.
2476
2477 Dictionary_const_it testIt = node.children()->members().begin();
2478 const Node* testNode = &testIt->second;
2479 auto testIIt = testIt.get();
2480 auto testPtr = testIIt.pointed_node();
2481 //distance between node and pointer to map pair in memory
2482 auto shift = (const char*)(testNode) - (const char*)(testPtr);
2483
2484 //decltype(testPtr) = boost::intrusive::compact_rbtree_node<void*>*
2485 decltype(testPtr) sh_ptr = decltype(testPtr)((const char*)(&node) - shift); //shifts from node to pointer
2486 //decltype(testIIt) =
2487 //boost::intrusive::tree_iterator<
2488 // boost::intrusive::bhtraits<
2489 // boost::container::base_node<
2490 // std::pair<const int, Simplex_tree_node_explicit_storage<Simplex_tree>>,
2491 // boost::container::dtl::intrusive_tree_hook<void*, boost::container::red_black_tree, true>, true>,
2492 // boost::intrusive::rbtree_node_traits<void*, true>,
2493 // boost::intrusive::normal_link,
2494 // boost::intrusive::dft_tag,
2495 // 3>,
2496 // false>
2497 decltype(testIIt) sh_ii;
2498 sh_ii = sh_ptr; //creates ``subiterator'' from pointer
2499 Dictionary_const_it sh(sh_ii); //creates iterator from subiterator
2500
2501 return sh;
2502 } else {
2503 //node needs to be casted as non const, because a const pair* cannot be casted into a Simplex_handle
2504 return (Simplex_handle)(boost::intrusive::get_parent_from_member<Dit_value_t>(const_cast<Node*>(&node),
2505 &Dit_value_t::second));
2506 }
2507 }
2508
2509 // Give access to Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator and keep nodes_by_label and
2510 // simplex_handle_from_node private
2511 friend class Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator<Simplex_tree>;
2512
2513 private:
2514 // update all extra data structures in the Simplex_tree. Must be called after all
2515 // simplex insertions.
2516 void update_simplex_tree_after_node_insertion(Simplex_handle sh) {
2517#ifdef DEBUG_TRACES
2518 std::clog << "update_simplex_tree_after_node_insertion" << std::endl;
2519#endif // DEBUG_TRACES
2520 if constexpr (Options::link_nodes_by_label) {
2521 // Creates an entry with sh->first if not already in the map and insert sh->second at the end of the list
2522 nodes_label_to_list_[sh->first].push_back(_to_node_it(sh)->second);
2523 }
2524 }
2525
2526 // update all extra data structures in the Simplex_tree. Must be called before
2527 // all simplex removals
2528 void update_simplex_tree_before_node_removal(Simplex_handle sh) {
2529#ifdef DEBUG_TRACES
2530 std::clog << "update_simplex_tree_before_node_removal" << std::endl;
2531#endif // DEBUG_TRACES
2532 if constexpr (Options::link_nodes_by_label) {
2533 _to_node_it(sh)->second.unlink_hooks(); // remove from lists of same label Nodes
2534 if (nodes_label_to_list_[sh->first].empty())
2535 nodes_label_to_list_.erase(sh->first);
2536 }
2537 }
2538
2539 public:
2548 void reset_filtration(Filtration_value filt_value, int min_dim = 0) {
2549 rec_reset_filtration(&root_, filt_value, min_dim);
2550 clear_filtration(); // Drop the cache.
2551 }
2552
2553 private:
2559 void rec_reset_filtration(Siblings * sib, Filtration_value filt_value, int min_depth) {
2560 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
2561 if (min_depth <= 0) {
2562 sh->second.assign_filtration(filt_value);
2563 }
2564 if (has_children(sh)) {
2565 rec_reset_filtration(sh->second.children(), filt_value, min_depth - 1);
2566 }
2567 }
2568 }
2569
2570 public:
2578 std::size_t get_serialization_size() const {
2579 const std::size_t vh_byte_size = sizeof(Vertex_handle);
2580 const std::size_t fv_byte_size = SimplexTreeOptions::store_filtration ? sizeof(Filtration_value) : 0;
2581 const std::size_t buffer_byte_size = vh_byte_size + num_simplices() * (fv_byte_size + 2 * vh_byte_size);
2582#ifdef DEBUG_TRACES
2583 std::clog << "Gudhi::simplex_tree::get_serialization_size - buffer size = " << buffer_byte_size << std::endl;
2584#endif // DEBUG_TRACES
2585 return buffer_byte_size;
2586 }
2587
2600 /* Let's take the following simplicial complex as example: */
2601 /* (vertices are represented as letters to ease the understanding) */
2602 /* o---o---o */
2603 /* a b\X/c */
2604 /* o */
2605 /* d */
2606 /* The simplex tree is: */
2607 /* a o b o c o d o */
2608 /* | |\ | */
2609 /* b o c o o d o d */
2610 /* | */
2611 /* d o */
2612 /* The serialization is (without filtration values that comes right after vertex handle value): */
2613 /* 04(number of vertices)0a 0b 0c 0d(list of vertices)01(number of [a] children)0b([a,b] simplex) */
2614 /* 00(number of [a,b] children)02(number of [b] children)0c 0d(list of [b] children)01(number of [b,c] children) */
2615 /* 0d(list of [b,c] children)00(number of [b,c,d] children)00(number of [b,d] children)01(number of [c] children) */
2616 /* 0d(list of [c] children)00(number of [b,d] children)00(number of [d] children) */
2617 /* Without explanation and with filtration values: */
2618 /* 04 0a F(a) 0b F(b) 0c F(c) 0d F(d) 01 0b F(a,b) 00 02 0c F(b,c) 0d F(b,d) 01 0d F(b,c,d) 00 00 01 0d F(c,d) 00 00 */
2619 void serialize(char* buffer, const std::size_t buffer_size) const {
2620 char* buffer_end = rec_serialize(&root_, buffer);
2621 if (static_cast<std::size_t>(buffer_end - buffer) != buffer_size)
2622 throw std::invalid_argument("Serialization does not match end of buffer");
2623 }
2624
2625 private:
2627 char* rec_serialize(Siblings const *sib, char* buffer) const {
2628 char* ptr = buffer;
2629 ptr = Gudhi::simplex_tree::serialize_trivial(static_cast<Vertex_handle>(sib->members().size()), ptr);
2630#ifdef DEBUG_TRACES
2631 std::clog << "\n" << sib->members().size() << " : ";
2632#endif // DEBUG_TRACES
2633 for (auto& map_el : sib->members()) {
2634 ptr = Gudhi::simplex_tree::serialize_trivial(map_el.first, ptr); // Vertex
2635 if (Options::store_filtration)
2636 ptr = Gudhi::simplex_tree::serialize_trivial(map_el.second.filtration(), ptr); // Filtration
2637#ifdef DEBUG_TRACES
2638 std::clog << " [ " << map_el.first << " | " << map_el.second.filtration() << " ] ";
2639#endif // DEBUG_TRACES
2640 }
2641 for (auto& map_el : sib->members()) {
2642 if (has_children(&map_el)) {
2643 ptr = rec_serialize(map_el.second.children(), ptr);
2644 } else {
2645 ptr = Gudhi::simplex_tree::serialize_trivial(static_cast<Vertex_handle>(0), ptr);
2646#ifdef DEBUG_TRACES
2647 std::cout << "\n0 : ";
2648#endif // DEBUG_TRACES
2649 }
2650 }
2651 return ptr;
2652 }
2653
2654 public:
2669 void deserialize(const char* buffer, const std::size_t buffer_size) {
2670 GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty"));
2671 const char* ptr = buffer;
2672 // Needs to read size before recursivity to manage new siblings for children
2673 Vertex_handle members_size;
2674 ptr = Gudhi::simplex_tree::deserialize_trivial(members_size, ptr);
2675 ptr = rec_deserialize(&root_, members_size, ptr, 0);
2676 if (static_cast<std::size_t>(ptr - buffer) != buffer_size) {
2677 throw std::invalid_argument("Deserialization does not match end of buffer");
2678 }
2679 }
2680
2681 private:
2683 const char* rec_deserialize(Siblings *sib, Vertex_handle members_size, const char* ptr, int dim) {
2684 // In case buffer is just a 0 char
2685 if (members_size > 0) {
2686 if constexpr (!Options::stable_simplex_handles) sib->members_.reserve(members_size);
2687 Vertex_handle vertex;
2689 for (Vertex_handle idx = 0; idx < members_size; idx++) {
2690 ptr = Gudhi::simplex_tree::deserialize_trivial(vertex, ptr);
2691 if (Options::store_filtration) {
2692 ptr = Gudhi::simplex_tree::deserialize_trivial(filtration, ptr);
2693 // Default is no children
2694 sib->members_.emplace_hint(sib->members_.end(), vertex, Node(sib, filtration));
2695 } else {
2696 // Default is no children
2697 sib->members_.emplace_hint(sib->members_.end(), vertex, Node(sib));
2698 }
2699 }
2700 Vertex_handle child_size;
2701 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
2702 update_simplex_tree_after_node_insertion(sh);
2703 ptr = Gudhi::simplex_tree::deserialize_trivial(child_size, ptr);
2704 if (child_size > 0) {
2705 Siblings* child = new Siblings(sib, sh->first);
2706 sh->second.assign_children(child);
2707 ptr = rec_deserialize(child, child_size, ptr, dim + 1);
2708 }
2709 }
2710 if (dim > dimension_) {
2711 // Update dimension if needed
2712 dimension_ = dim;
2713 }
2714 }
2715 return ptr;
2716 }
2717
2718 private:
2719 Vertex_handle null_vertex_;
2722 Siblings root_;
2723
2724 // all mutable as their content has no impact on the content of the simplex tree itself
2725 // they correspond to some kind of cache or helper attributes.
2727 mutable std::vector<Simplex_handle> filtration_vect_;
2729 mutable int dimension_;
2730 mutable bool dimension_to_be_lowered_ = false;
2731};
2732
2733// Print a Simplex_tree in os.
2734template<typename...T>
2735std::ostream& operator<<(std::ostream & os, const Simplex_tree<T...> & st) {
2736 for (auto sh : st.filtration_simplex_range()) {
2737 os << st.dimension(sh) << " ";
2738 for (auto v : st.simplex_vertex_range(sh)) {
2739 os << v << " ";
2740 }
2741 os << st.filtration(sh) << "\n"; // TODO(VR): why adding the key ?? not read ?? << " " << st.key(sh) << " \n";
2742 }
2743 return os;
2744}
2745
2746template<typename...T>
2747std::istream& operator>>(std::istream & is, Simplex_tree<T...> & st) {
2748 typedef Simplex_tree<T...> ST;
2749 std::vector<typename ST::Vertex_handle> simplex;
2750 typename ST::Filtration_value fil;
2751 int max_dim = -1;
2752 while (read_simplex(is, simplex, fil)) {
2753 // read all simplices in the file as a list of vertices
2754 // Warning : simplex_size needs to be casted in int - Can be 0
2755 int dim = static_cast<int> (simplex.size() - 1);
2756 if (max_dim < dim) {
2757 max_dim = dim;
2758 }
2759 // insert every simplex in the simplex tree
2760 st.insert_simplex(simplex, fil);
2761 simplex.clear();
2762 }
2763 st.set_dimension(max_dim);
2764
2765 return is;
2766}
2767 // end addtogroup simplex_tree
2769
2770} // namespace Gudhi
2771
2772#endif // SIMPLEX_TREE_H_
Extended simplex type data structure for representing the type of simplices in an extended filtration...
Iterator over the simplices of the boundary of a simplex and their opposite vertices.
Definition: Simplex_tree_iterators.h:193
Iterator over the simplices of the boundary of a simplex.
Definition: Simplex_tree_iterators.h:81
Iterator over the simplices of a simplicial complex.
Definition: Simplex_tree_iterators.h:308
Iterator over the simplices of the star of a simplex.
Definition: Simplex_tree_star_simplex_iterators.h:162
Iterator over the vertices of a simplex in a SimplexTree.
Definition: Simplex_tree_iterators.h:36
Iterator over the simplices of the skeleton of a given dimension of the simplicial complex.
Definition: Simplex_tree_iterators.h:382
Simplex Tree data structure for representing simplicial complexes.
Definition: Simplex_tree.h:98
Simplex_tree(Simplex_tree &&complex_source)
User-defined move constructor relocates the whole tree structure including extra data (Simplex_data) ...
Definition: Simplex_tree.h:460
std::vector< size_t > num_simplices_by_dimension() const
Returns the number of simplices of each dimension in the simplex tree.
Definition: Simplex_tree.h:826
void for_each_simplex(Fun &&fun) const
Definition: Simplex_tree.h:2015
void insert_edge_as_flag(Vertex_handle u, Vertex_handle v, Filtration_value fil, int dim_max, std::vector< Simplex_handle > &added_simplices)
Adds a new vertex or a new edge in a flag complex, as well as all simplices of its star,...
Definition: Simplex_tree.h:1559
Options::Filtration_value Filtration_value
Type for the value of the filtration function.
Definition: Simplex_tree.h:105
Simplex_tree_boundary_opposite_vertex_simplex_iterator< Simplex_tree > Boundary_opposite_vertex_simplex_iterator
Iterator over the simplices of the boundary of a simplex and their opposite vertices.
Definition: Simplex_tree.h:281
void reset_filtration(Filtration_value filt_value, int min_dim=0)
This function resets the filtration value of all the simplices of dimension at least min_dim....
Definition: Simplex_tree.h:2548
int dimension(Simplex_handle sh) const
Returns the dimension of a simplex.
Definition: Simplex_tree.h:847
void assign_filtration(Simplex_handle sh, Filtration_value fv)
Sets the filtration value of a simplex.
Definition: Simplex_tree.h:738
Siblings const * self_siblings(SimplexHandle sh) const
Definition: Simplex_tree.h:1154
std::conditional< Options::link_nodes_by_label, Optimized_cofaces_simplex_filtered_range, std::vector< Simplex_handle > >::type Cofaces_simplex_range
Range over the cofaces of a simplex.
Definition: Simplex_tree.h:265
void initialize_filtration(bool ignore_infinite_values=false) const
Initializes the filtration cache, i.e. sorts the simplices according to their order in the filtration...
Definition: Simplex_tree.h:1200
Complex_simplex_range complex_simplex_range() const
Returns a range over the simplices of the simplicial complex.
Definition: Simplex_tree.h:321
bool make_filtration_non_decreasing()
This function ensures that each simplex has a higher filtration value than its faces by increasing th...
Definition: Simplex_tree.h:2051
bool operator==(const Simplex_tree< OtherSimplexTreeOptions > &st2) const
Checks if two simplex trees are equal. Any extra data (Simplex_data) stored in the simplices are igno...
Definition: Simplex_tree.h:653
Vertex_handle vertex_with_same_filtration(Simplex_handle sh) const
Returns a vertex of sh that has the same filtration value as sh if it exists, and null_vertex() other...
Definition: Simplex_tree.h:2370
bool operator!=(const Simplex_tree< OtherSimplexTreeOptions > &st2) const
Checks if two simplex trees are different.
Definition: Simplex_tree.h:662
bool prune_above_dimension(int dimension)
Remove all simplices of dimension greater than a given value.
Definition: Simplex_tree.h:2162
std::pair< Simplex_handle, bool > insert_simplex(const InputVertexRange &simplex, Filtration_value filtration=0)
Insert a simplex, represented by a range of Vertex_handles, in the simplicial complex.
Definition: Simplex_tree.h:1045
Vertex_handle null_vertex() const
Returns a Vertex_handle different from all Vertex_handles associated to the vertices of the simplicia...
Definition: Simplex_tree.h:773
Simplex_handle edge_with_same_filtration(Simplex_handle sh) const
Returns an edge of sh that has the same filtration value as sh if it exists, and null_simplex() other...
Definition: Simplex_tree.h:2384
boost::iterator_range< Boundary_simplex_iterator > Boundary_simplex_range
Range over the simplices of the boundary of a simplex.
Definition: Simplex_tree.h:277
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const
Returns a range over the vertices of a simplex.
Definition: Simplex_tree.h:369
Boundary_opposite_vertex_simplex_range boundary_opposite_vertex_simplex_range(SimplexHandle sh) const
Given a simplex, returns a range over the simplices of its boundary and their opposite vertices.
Definition: Simplex_tree.h:407
Simplex_tree_simplex_vertex_iterator< Simplex_tree > Simplex_vertex_iterator
Iterator over the vertices of a simplex.
Definition: Simplex_tree.h:259
void clear()
Remove all the simplices, leaving an empty complex.
Definition: Simplex_tree.h:2081
static Simplex_key key(Simplex_handle sh)
Returns the key associated to a simplex.
Definition: Simplex_tree.h:708
static Filtration_value filtration(Simplex_handle sh)
Returns the filtration value of a simplex.
Definition: Simplex_tree.h:727
Simplex_tree(const Simplex_tree< OtherSimplexTreeOptions > &complex_source, F &&translate_filtration_value)
Construct the simplex tree as the copy of a given simplex tree with eventually different template par...
Definition: Simplex_tree.h:439
Cofaces_simplex_range star_simplex_range(const Simplex_handle simplex) const
Compute the star of a n simplex.
Definition: Simplex_tree.h:1310
boost::transform_iterator< return_first, Dictionary_const_it > Complex_vertex_iterator
Iterator over the vertices of the simplicial complex.
Definition: Simplex_tree.h:253
bool has_children(SimplexHandle sh) const
Returns true if the node in the simplex tree pointed by the given simplex handle has children.
Definition: Simplex_tree.h:874
void expansion_with_blockers(int max_dim, Blocker block_simplex)
Expands a simplex tree containing only a graph. Simplices corresponding to cliques in the graph are a...
Definition: Simplex_tree.h:1884
boost::iterator_range< Simplex_vertex_iterator > Simplex_vertex_range
Range over the vertices of a simplex.
Definition: Simplex_tree.h:261
void remove_maximal_simplex(Simplex_handle sh)
Remove a maximal simplex.
Definition: Simplex_tree.h:2243
void assign_key(Simplex_handle sh, Simplex_key key)
Assign a value 'key' to the key of the simplex represented by the Simplex_handle 'sh'.
Definition: Simplex_tree.h:1140
Options::Simplex_key Simplex_key
Key associated to each simplex.
Definition: Simplex_tree.h:109
Simplex_tree()
Constructs an empty simplex tree.
Definition: Simplex_tree.h:417
boost::iterator_range< Complex_simplex_iterator > Complex_simplex_range
Range over the simplices of the simplicial complex.
Definition: Simplex_tree.h:289
Dictionary::const_iterator Simplex_handle
Handle type to a simplex contained in the simplicial complex represented by the simplex tree.
Definition: Simplex_tree.h:192
Simplex_tree_boundary_simplex_iterator< Simplex_tree > Boundary_simplex_iterator
Iterator over the simplices of the boundary of a simplex.
Definition: Simplex_tree.h:275
const Simplex_data & simplex_data(Simplex_handle sh) const
Returns the extra data stored in a simplex.
Definition: Simplex_tree.h:765
Simplex_tree_siblings< Simplex_tree, Dictionary > Siblings
Set of nodes sharing a same parent in the simplex tree.
Definition: Simplex_tree.h:136
Siblings * self_siblings(SimplexHandle sh)
Definition: Simplex_tree.h:1163
std::pair< Simplex_handle, Simplex_handle > endpoints(Simplex_handle sh) const
Definition: Simplex_tree.h:1147
bool is_empty() const
Returns whether the complex is empty.
Definition: Simplex_tree.h:783
Simplex_tree_complex_simplex_iterator< Simplex_tree > Complex_simplex_iterator
Iterator over the simplices of the simplicial complex.
Definition: Simplex_tree.h:287
Simplex_handle simplex(Simplex_key idx) const
Returns the simplex that has index idx in the filtration.
Definition: Simplex_tree.h:718
void print_hasse(std::ostream &os) const
Write the hasse diagram of the simplicial complex in os.
Definition: Simplex_tree.h:1993
Simplex_data & simplex_data(Simplex_handle sh)
Returns the extra data stored in a simplex.
Definition: Simplex_tree.h:758
void insert_batch_vertices(VertexRange const &vertices, Filtration_value filt=0)
Inserts several vertices.
Definition: Simplex_tree.h:1487
std::vector< Simplex_handle > Filtration_simplex_range
Range over the simplices of the simplicial complex, ordered by the filtration.
Definition: Simplex_tree.h:299
Filtration_simplex_range::const_iterator Filtration_simplex_iterator
Iterator over the simplices of the simplicial complex, ordered by the filtration.
Definition: Simplex_tree.h:303
int dimension() const
Returns the dimension of the simplicial complex.
Definition: Simplex_tree.h:865
Extended_filtration_data extend_filtration()
Extend filtration for computing extended persistence. This function only uses the filtration values a...
Definition: Simplex_tree.h:2310
std::pair< Filtration_value, Extended_simplex_type > decode_extended_filtration(Filtration_value f, const Extended_filtration_data &efd) const
Retrieve the original filtration value for a given simplex in the Simplex_tree. Since the computation...
Definition: Simplex_tree.h:2283
void maybe_initialize_filtration() const
Initializes the filtration cache if it isn't initialized yet.
Definition: Simplex_tree.h:1231
Options::Vertex_handle Vertex_handle
Type for the vertex handle.
Definition: Simplex_tree.h:119
Skeleton_simplex_range skeleton_simplex_range(int dim) const
Returns a range over the simplices of the dim-skeleton of the simplicial complex.
Definition: Simplex_tree.h:335
Filtration_simplex_range const & filtration_simplex_range(Indexing_tag=Indexing_tag()) const
Returns a range over the simplices of the simplicial complex, in the order of the filtration.
Definition: Simplex_tree.h:358
size_t num_vertices() const
Returns the number of vertices in the complex.
Definition: Simplex_tree.h:778
Cofaces_simplex_range cofaces_simplex_range(const Simplex_handle simplex, int codimension) const
Compute the cofaces of a n simplex.
Definition: Simplex_tree.h:1324
void clear_filtration() const
Clears the filtration cache produced by initialize_filtration().
Definition: Simplex_tree.h:1243
void expansion(int max_dim)
Expands the Simplex_tree containing only its one skeleton until dimension max_dim.
Definition: Simplex_tree.h:1512
Simplex_tree(const Simplex_tree &complex_source)
User-defined copy constructor reproduces the whole tree structure including extra data (Simplex_data)...
Definition: Simplex_tree.h:449
size_t num_simplices() const
Returns the number of simplices in the simplex_tree.
Definition: Simplex_tree.h:791
boost::iterator_range< Complex_vertex_iterator > Complex_vertex_range
Range over the vertices of the simplicial complex.
Definition: Simplex_tree.h:255
static Simplex_key null_key()
Returns a fixed number not in the interval [0, num_simplices()).
Definition: Simplex_tree.h:753
Simplex_handle minimal_simplex_with_same_filtration(Simplex_handle sh) const
Returns a minimal face of sh that has the same filtration value as sh.
Definition: Simplex_tree.h:2417
bool prune_above_filtration(Filtration_value filtration)
Prune above filtration value given as parameter.
Definition: Simplex_tree.h:2097
int upper_bound_dimension() const
Returns an upper bound on the dimension of the simplicial complex.
Definition: Simplex_tree.h:855
Siblings * root()
Definition: Simplex_tree.h:1171
std::pair< Simplex_handle, bool > insert_simplex_and_subfaces(const InputVertexRange &Nsimplex, Filtration_value filtration=0)
Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of Vertex_handles,...
Definition: Simplex_tree.h:1074
Simplex_tree_skeleton_simplex_iterator< Simplex_tree > Skeleton_simplex_iterator
Iterator over the simplices of the skeleton of the simplicial complex, for a given dimension.
Definition: Simplex_tree.h:294
Simplex_tree & operator=(Simplex_tree &&complex_source)
User-defined move assignment relocates the whole tree structure including extra data (Simplex_data) s...
Definition: Simplex_tree.h:497
const Siblings * root() const
Definition: Simplex_tree.h:1174
boost::iterator_range< Boundary_opposite_vertex_simplex_iterator > Boundary_opposite_vertex_simplex_range
Range over the simplices of the boundary of a simplex and their opposite vertices.
Definition: Simplex_tree.h:283
Simplex_tree & operator=(const Simplex_tree &complex_source)
User-defined copy assignment reproduces the whole tree structure including extra data (Simplex_data) ...
Definition: Simplex_tree.h:479
Complex_vertex_range complex_vertex_range() const
Returns a range over the vertices of the simplicial complex. The order is increasing according to < o...
Definition: Simplex_tree.h:311
void insert_graph(const OneSkeletonGraph &skel_graph)
Inserts a 1-skeleton in an empty Simplex_tree.
Definition: Simplex_tree.h:1424
Get_simplex_data_type< Options >::type Simplex_data
Extra data stored in each simplex.
Definition: Simplex_tree.h:115
Simplex_handle find(const InputVertexRange &s) const
Given a range of Vertex_handles, returns the Simplex_handle of the simplex in the simplicial complex ...
Definition: Simplex_tree.h:899
boost::iterator_range< Skeleton_simplex_iterator > Skeleton_simplex_range
Range over the simplices of the skeleton of the simplicial complex, for a given dimension.
Definition: Simplex_tree.h:297
~Simplex_tree()
Destructor; deallocates the whole tree structure.
Definition: Simplex_tree.h:472
static Simplex_handle null_simplex()
Returns a Simplex_handle different from all Simplex_handles associated to the simplices in the simpli...
Definition: Simplex_tree.h:748
Boundary_simplex_range boundary_simplex_range(SimplexHandle sh) const
Returns a range over the simplices of the boundary of a simplex.
Definition: Simplex_tree.h:390
void set_dimension(int dimension, bool exact=true)
Set a dimension for the simplicial complex.
Definition: Simplex_tree.h:1184
Graph simplicial complex methods.
std::ostream & operator<<(std::ostream &os, const Permutahedral_representation< Vertex, OrderedSetPartition > &simplex)
Print a permutahedral representation to a stream.
Definition: Permutahedral_representation.h:173
constexpr auto & get(Gudhi::persistence_matrix::Persistence_interval< Dimension, Event_value > &i) noexcept
Partial specialization of get for Gudhi::persistence_matrix::Persistence_interval.
Definition: persistence_interval.h:199
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14
bool read_simplex(std::istream &in_, std::vector< Vertex_handle > &simplex, Filtration_value &fil)
Read a face from a file.
Definition: reader_utils.h:158
This file includes common file reader for GUDHI.
Value type for a filtration function on a cell complex.
Definition: FiltrationValue.h:20
No hook when SimplexTreeOptions::link_nodes_by_label is false.
Definition: hooks_simplex_base.h:20
Node of a simplex tree with filtration value and simplex key.
Definition: Simplex_tree_node_explicit_storage.h:40
Concept describing an indexing scheme (see FilteredComplex) for applying continuous maps to a cell co...
Definition: IndexingTag.h:18
Key type used as simplex identifier.
Definition: SimplexKey.h:15
Concept of the template parameter for the class Gudhi::Simplex_tree<SimplexTreeOptions>.
Definition: SimplexTreeOptions.h:17
static const bool store_filtration
If true, each simplex has extra storage for one Filtration_value, and this value is propagated by ope...
Definition: SimplexTreeOptions.h:34
Handle type for the vertices of a cell complex.
Definition: VertexHandle.h:15