Loading...
Searching...
No Matches
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: Generalization of the notion of filtration values.
14 * - 2024/08 Hannah Schreiber: Addition of customizable copy constructor.
15 * - 2024/08 Marc Glisse: Allow storing custom data in simplices.
16 * - 2024/10 Hannah Schreiber: Const version of the Simplex_tree
17 * - 2025/02 Hannah Schreiber (& David Loiseaux): Insertion strategies for `insert_simplex_and_subfaces`
18 * - 2025/03 Hannah Schreiber (& David Loiseaux): Add number_of_parameters_ member
19 * - YYYY/MM Author: Description of the modification
20 */
21
22#ifndef SIMPLEX_TREE_H_
23#define SIMPLEX_TREE_H_
24
25#include <gudhi/Simplex_tree/simplex_tree_options.h>
26#include <gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h>
27#include <gudhi/Simplex_tree/Simplex_tree_siblings.h>
28#include <gudhi/Simplex_tree/Simplex_tree_iterators.h>
29#include <gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h>
30#include <gudhi/Simplex_tree/filtration_value_utils.h> // for de/serialize + empty_filtration_value
31#include <gudhi/Simplex_tree/hooks_simplex_base.h>
32
33#include <gudhi/reader_utils.h>
35#include <gudhi/Debug_utils.h>
36
37#include <boost/container/map.hpp>
38#include <boost/container/flat_map.hpp>
39#include <boost/iterator/transform_iterator.hpp>
40#include <boost/graph/adjacency_list.hpp>
41#include <boost/range/adaptor/reversed.hpp>
42#include <boost/range/adaptor/transformed.hpp>
43#include <boost/range/size.hpp>
44#include <boost/container/static_vector.hpp>
45#include <boost/range/adaptors.hpp>
46
47#include <boost/intrusive/list.hpp>
48#include <boost/intrusive/parent_from_member.hpp>
49
50#ifdef GUDHI_USE_TBB
51#include <tbb/parallel_sort.h>
52#endif
53
54#include <cstddef>
55#include <cstdint> // std::uint8_t
56#include <utility> // for std::move
57#include <vector>
58#include <functional> // for greater<>
59#include <stdexcept>
60#include <limits> // Inf
61#include <initializer_list>
62#include <algorithm> // for std::max
63#include <iterator> // for std::distance
64#include <type_traits> // for std::conditional
65#include <unordered_map>
66#include <iterator> // for std::prev
67
68namespace Gudhi {
69
73
86enum class Extended_simplex_type {UP, DOWN, EXTRA};
87
100
101template<typename SimplexTreeOptions = Simplex_tree_options_default>
102class Simplex_tree {
103 public:
104 typedef SimplexTreeOptions Options;
105 typedef typename Options::Indexing_tag Indexing_tag;
109 typedef typename Options::Filtration_value Filtration_value;
113 typedef typename Options::Simplex_key Simplex_key;
119 typedef typename Get_simplex_data_type<Options>::type Simplex_data;
123 typedef typename Options::Vertex_handle Vertex_handle;
124
125 /* Type of node in the simplex tree. */
127 /* Type of dictionary Vertex_handle -> Node for traversing the simplex tree. */
128 // Note: this wastes space when Vertex_handle is 32 bits and Node is aligned on 64 bits. It would be better to use a
129 // flat_set (with our own comparator) where we can control the layout of the struct (put Vertex_handle and
130 // Simplex_key next to each other).
131 typedef typename boost::container::flat_map<Vertex_handle, Node> flat_map;
132 //Dictionary::iterator remain valid under insertions and deletions,
133 //necessary e.g. when computing oscillating rips zigzag filtrations.
134 typedef typename boost::container::map<Vertex_handle, Node> map;
135 typedef typename std::conditional<Options::stable_simplex_handles,
136 map,
137 flat_map>::type Dictionary;
138
141
142 struct Key_simplex_base_real {
143 Key_simplex_base_real() : key_(-1) {}
144 Key_simplex_base_real(Simplex_key k) : key_(k) {}
145 void assign_key(Simplex_key k) { key_ = k; }
146 Simplex_key key() const { return key_; }
147 private:
148 Simplex_key key_;
149 };
150 struct Key_simplex_base_dummy {
151 Key_simplex_base_dummy() {}
152 // Undefined so it will not link
153 void assign_key(Simplex_key);
154 Simplex_key key() const;
155 };
156
157 struct Extended_filtration_data {
158 Filtration_value minval;
159 Filtration_value maxval;
160 Extended_filtration_data() {}
161
162 Extended_filtration_data(const Filtration_value& vmin, const Filtration_value& vmax)
163 : minval(vmin), maxval(vmax)
164 {}
165 };
166 typedef typename std::conditional<Options::store_key, Key_simplex_base_real, Key_simplex_base_dummy>::type
167 Key_simplex_base;
168
169 struct Filtration_simplex_base_real {
170 Filtration_simplex_base_real() : filt_() {}
171 Filtration_simplex_base_real(Filtration_value f) : filt_(f) {}
172 void assign_filtration(const Filtration_value& f) { filt_ = f; }
173 const Filtration_value& filtration() const { return filt_; }
174 Filtration_value& filtration() { return filt_; }
175
176 static const Filtration_value& get_infinity() { return inf_; }
177 static const Filtration_value& get_minus_infinity() { return minus_inf_; }
178
179 private:
180 Filtration_value filt_;
181
182 inline static const Filtration_value inf_ = std::numeric_limits<Filtration_value>::has_infinity
183 ? std::numeric_limits<Filtration_value>::infinity()
184 : std::numeric_limits<Filtration_value>::max();
185 inline static const Filtration_value minus_inf_ = std::numeric_limits<Filtration_value>::has_infinity
186 ? -std::numeric_limits<Filtration_value>::infinity()
187 : std::numeric_limits<Filtration_value>::lowest();
188 };
189
190 struct Filtration_simplex_base_dummy {
191 Filtration_simplex_base_dummy() {}
192 Filtration_simplex_base_dummy(Filtration_value GUDHI_CHECK_code(f)) {
193 GUDHI_CHECK(f == null_, "filtration value specified in the constructor for a complex that does not store them");
194 }
195 void assign_filtration(const Filtration_value& GUDHI_CHECK_code(f)) {
196 GUDHI_CHECK(f == null_, "filtration value assigned for a complex that does not store them");
197 }
198 const Filtration_value& filtration() const { return null_; }
199
200 private:
201 inline static const Filtration_value null_{Gudhi::simplex_tree::empty_filtration_value};
202 };
203 typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real,
204 Filtration_simplex_base_dummy>::type Filtration_simplex_base;
205
206 public:
213 typedef typename Dictionary::const_iterator Simplex_handle;
214
215 private:
216 typedef typename Dictionary::iterator Dictionary_it;
217 typedef typename Dictionary::const_iterator Dictionary_const_it;
218 typedef typename Dictionary_it::value_type Dit_value_t;
219
220 struct return_first {
221 Vertex_handle operator()(const Dit_value_t& p_sh) const {
222 return p_sh.first;
223 }
224 };
225
226 private:
233 using Optimized_star_simplex_iterator = Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
235 using Optimized_star_simplex_range = boost::iterator_range<Optimized_star_simplex_iterator>;
236
237 class Fast_cofaces_predicate {
238 Simplex_tree const* st_;
239 int codim_;
240 int dim_;
241 public:
242 Fast_cofaces_predicate(Simplex_tree const* st, int codim, int dim)
243 : st_(st), codim_(codim), dim_(codim + dim) {}
244 bool operator()( const Simplex_handle iter ) const {
245 if (codim_ == 0)
246 // Always true for a star
247 return true;
248 // Specific coface case
249 return dim_ == st_->dimension(iter);
250 }
251 };
252
253 // WARNING: this is safe only because boost::filtered_range is containing a copy of begin and end iterator.
254 // This would not be safe if it was containing a pointer to a range (maybe the case for std::views)
255 using Optimized_cofaces_simplex_filtered_range = boost::filtered_range<Fast_cofaces_predicate,
256 Optimized_star_simplex_range>;
257
258
261 static constexpr int max_dimension() { return 40; }
262 public:
270
274 typedef boost::transform_iterator<return_first, Dictionary_const_it> Complex_vertex_iterator;
276 typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
282 typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
284 typedef typename std::conditional<Options::link_nodes_by_label,
285 Optimized_cofaces_simplex_filtered_range, // faster implementation
286 std::vector<Simplex_handle>>::type Cofaces_simplex_range;
287
291 using Static_vertex_vector = boost::container::static_vector<Vertex_handle, max_dimension()>;
292
298 typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
304 typedef boost::iterator_range<Boundary_opposite_vertex_simplex_iterator> Boundary_opposite_vertex_simplex_range;
310 typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
318 typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
324 typedef boost::iterator_range<Dimension_simplex_iterator> Dimension_simplex_range;
326 typedef std::vector<Simplex_handle> Filtration_simplex_range;
330 typedef typename Filtration_simplex_range::const_iterator Filtration_simplex_iterator;
331
332 /* @} */ // end name range and iterator types
335
339 return Complex_vertex_range(boost::make_transform_iterator(root_.members_.begin(), return_first()),
340 boost::make_transform_iterator(root_.members_.end(), return_first()));
341 }
342
352
366
378
407 Filtration_simplex_range const& filtration_simplex_range(Indexing_tag = Indexing_tag()) const {
409 return filtration_vect_;
410 }
411
419 GUDHI_CHECK(sh != null_simplex(), "empty simplex");
422 }
423
438 template<class SimplexHandle>
443
455 template<class SimplexHandle>
460 // end range and iterator methods
464
467 : null_vertex_(-1),
468 root_(nullptr, null_vertex_),
469 number_of_parameters_(1),
470 filtration_vect_(),
471 dimension_(-1),
472 dimension_to_be_lowered_(false) {}
473
489 template<typename OtherSimplexTreeOptions, typename F>
490 Simplex_tree(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
491#ifdef DEBUG_TRACES
492 std::clog << "Simplex_tree custom copy constructor" << std::endl;
493#endif // DEBUG_TRACES
494 copy_from(complex_source, translate_filtration_value);
495 }
496
500 Simplex_tree(const Simplex_tree& complex_source) {
501#ifdef DEBUG_TRACES
502 std::clog << "Simplex_tree copy constructor" << std::endl;
503#endif // DEBUG_TRACES
504 copy_from(complex_source);
505 }
506
511 Simplex_tree(Simplex_tree && complex_source) {
512#ifdef DEBUG_TRACES
513 std::clog << "Simplex_tree move constructor" << std::endl;
514#endif // DEBUG_TRACES
515 move_from(complex_source);
516 }
517
520 root_members_recursive_deletion();
521 }
522
526 Simplex_tree& operator= (const Simplex_tree& complex_source) {
527#ifdef DEBUG_TRACES
528 std::clog << "Simplex_tree copy assignment" << std::endl;
529#endif // DEBUG_TRACES
530 // Self-assignment detection
531 if (&complex_source != this) {
532 // We start by deleting root_ if not empty
533 root_members_recursive_deletion();
534
535 copy_from(complex_source);
536 }
537 return *this;
538 }
539
544 Simplex_tree& operator=(Simplex_tree&& complex_source) {
545#ifdef DEBUG_TRACES
546 std::clog << "Simplex_tree move assignment" << std::endl;
547#endif // DEBUG_TRACES
548 // Self-assignment detection
549 if (&complex_source != this) {
550 // root_ deletion in case it was not empty
551 root_members_recursive_deletion();
552
553 move_from(complex_source);
554 }
555 return *this;
556 }
557 // end constructor/destructor
558
559 private:
560 // Copy from complex_source to "this"
561 void copy_from(const Simplex_tree& complex_source) {
562 null_vertex_ = complex_source.null_vertex_;
563 filtration_vect_.clear();
564 number_of_parameters_ = complex_source.number_of_parameters_;
565 dimension_ = complex_source.dimension_;
566 dimension_to_be_lowered_ = complex_source.dimension_to_be_lowered_;
567 auto root_source = complex_source.root_;
568
569 // root members copy
570 root_.members() =
571 Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
572 // Needs to reassign children
573 for (auto& map_el : root_.members()) {
574 map_el.second.assign_children(&root_);
575 }
576 // Specific for optional data
577 if constexpr (!std::is_same_v<Simplex_data, No_simplex_data>) {
578 auto dst_iter = root_.members().begin();
579 auto src_iter = root_source.members().begin();
580
581 while(dst_iter != root_.members().end() || src_iter != root_source.members().end()) {
582 dst_iter->second.data() = src_iter->second.data();
583 dst_iter++;
584 src_iter++;
585 }
586 // Check in debug mode members data were copied
587 assert(dst_iter == root_.members().end());
588 assert(src_iter == root_source.members().end());
589 }
590 rec_copy<Options::store_key, true>(
591 &root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
592 }
593
594 // Copy from complex_source to "this"
595 template<typename OtherSimplexTreeOptions, typename F>
596 void copy_from(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
597 null_vertex_ = complex_source.null_vertex_;
598 filtration_vect_.clear();
599 number_of_parameters_ = complex_source.number_of_parameters_;
600 dimension_ = complex_source.dimension_;
601 dimension_to_be_lowered_ = complex_source.dimension_to_be_lowered_;
602 auto root_source = complex_source.root_;
603
604 // root members copy
605 if constexpr (!Options::stable_simplex_handles) root_.members().reserve(root_source.size());
606 for (auto& p : root_source.members()){
607 if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) {
608 root_.members().try_emplace(
609 root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()), p.second.key());
610 } else {
611 root_.members().try_emplace(
612 root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()));
613 }
614 }
615
616 rec_copy<OtherSimplexTreeOptions::store_key, false>(&root_, &root_source, translate_filtration_value);
617 }
618
620 template<bool store_key, bool copy_simplex_data, typename OtherSiblings, typename F>
621 void rec_copy(Siblings *sib, OtherSiblings *sib_source, F&& translate_filtration_value) {
622 auto sh_source = sib_source->members().begin();
623 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh, ++sh_source) {
624 update_simplex_tree_after_node_insertion(sh);
625 if (has_children(sh_source)) {
626 Siblings * newsib = new Siblings(sib, sh_source->first);
627 if constexpr (!Options::stable_simplex_handles) {
628 newsib->members_.reserve(sh_source->second.children()->members().size());
629 }
630 for (auto & child : sh_source->second.children()->members()){
631 Dictionary_it new_it{};
632 if constexpr (store_key && Options::store_key) {
633 new_it = newsib->members_.emplace_hint(
634 newsib->members_.end(),
635 child.first,
636 Node(newsib, translate_filtration_value(child.second.filtration()), child.second.key()));
637 } else {
638 new_it = newsib->members_.emplace_hint(newsib->members_.end(),
639 child.first,
640 Node(newsib, translate_filtration_value(child.second.filtration())));
641 }
642 // Specific for optional data
643 if constexpr (copy_simplex_data && !std::is_same_v<Simplex_data, No_simplex_data>) {
644 new_it->second.data() = child.second.data();
645 }
646 }
647 rec_copy<store_key, copy_simplex_data>(newsib, sh_source->second.children(), translate_filtration_value);
648 sh->second.assign_children(newsib);
649 }
650 }
651 }
652
653 // Move from complex_source to "this"
654 void move_from(Simplex_tree& complex_source) {
655 null_vertex_ = std::move(complex_source.null_vertex_);
656 root_ = std::move(complex_source.root_);
657 filtration_vect_ = std::move(complex_source.filtration_vect_);
658 number_of_parameters_ = std::exchange(complex_source.number_of_parameters_, 1);
659 dimension_ = std::exchange(complex_source.dimension_, -1);
660 dimension_to_be_lowered_ = std::exchange(complex_source.dimension_to_be_lowered_, false);
661 if constexpr (Options::link_nodes_by_label) {
662 nodes_label_to_list_.swap(complex_source.nodes_label_to_list_);
663 }
664 // Need to update root members (children->oncles and children need to point on the new root pointer)
665 for (auto& map_el : root_.members()) {
666 if (map_el.second.children() != &(complex_source.root_)) {
667 // reset children->oncles with the moved root_ pointer value
668 map_el.second.children()->oncles_ = &root_;
669 } else {
670 // if simplex is of dimension 0, oncles_ shall be nullptr
671 GUDHI_CHECK(map_el.second.children()->oncles_ == nullptr,
672 std::invalid_argument("Simplex_tree move constructor from an invalid Simplex_tree"));
673 // and children points on root_ - to be moved
674 map_el.second.assign_children(&root_);
675 }
676 }
677 }
678
679 // delete all root_.members() recursively
680 void root_members_recursive_deletion() {
681 for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
682 if (has_children(sh)) {
683 rec_delete(sh->second.children());
684 }
685 }
686 root_.members().clear();
687 }
688
689 // Recursive deletion
690 void rec_delete(Siblings * sib) {
691 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
692 if (has_children(sh)) {
693 rec_delete(sh->second.children());
694 }
695 }
696 delete sib;
697 }
698
699 public:
700 template<typename> friend class Simplex_tree;
701
705 template <class OtherSimplexTreeOptions>
706 bool operator==(const Simplex_tree<OtherSimplexTreeOptions>& st2) const {
707 if ((null_vertex_ != st2.null_vertex_) ||
708 (dimension_ != st2.dimension_ && !dimension_to_be_lowered_ && !st2.dimension_to_be_lowered_) ||
709 (number_of_parameters_ != st2.number_of_parameters_))
710 return false;
711 return rec_equal(&root_, &st2.root_);
712 }
713
715 template<class OtherSimplexTreeOptions>
716 bool operator!=(const Simplex_tree<OtherSimplexTreeOptions>& st2) const {
717 return (!(*this == st2));
718 }
719
720 private:
722 template<class OtherSiblings>
723 bool rec_equal(Siblings const* s1, OtherSiblings const* s2) const {
724 if (s1->members().size() != s2->members().size())
725 return false;
726 auto sh2 = s2->members().begin();
727 for (auto sh1 = s1->members().begin();
728 (sh1 != s1->members().end() && sh2 != s2->members().end());
729 ++sh1, ++sh2) {
730 if (sh1->first != sh2->first || !(sh1->second.filtration() == sh2->second.filtration()))
731 return false;
732 if (has_children(sh1) != has_children(sh2))
733 return false;
734 // Recursivity on children only if both have children
735 else if (has_children(sh1))
736 if (!rec_equal(sh1->second.children(), sh2->second.children()))
737 return false;
738 }
739 return true;
740 }
741
746 static const Filtration_value& filtration_(Simplex_handle sh) {
747 GUDHI_CHECK (sh != null_simplex(), "null simplex");
748 return sh->second.filtration();
749 }
750
751 // Transform a Dictionary_const_it into a Dictionary_it
752 Dictionary_it _to_node_it(Simplex_handle sh) {
753 return self_siblings(sh)->to_non_const_it(sh);
754 }
755
756 public:
763 return sh->second.key();
764 }
765
773 return filtration_vect_[idx];
774 }
775
782 if (sh != null_simplex()) {
783 return sh->second.filtration();
784 } else {
785 return Filtration_simplex_base_real::get_infinity();
786 }
787 }
788
793 GUDHI_CHECK(sh != null_simplex(),
794 std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex"));
795 _to_node_it(sh)->second.assign_filtration(fv);
796 }
797
803 return Dictionary_const_it();
804 }
805
808 return -1;
809 }
810
813 GUDHI_CHECK(sh != null_simplex(),
814 std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
815 return _to_node_it(sh)->second.data();
816 }
817
820 GUDHI_CHECK(sh != null_simplex(),
821 std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
822 return sh->second.data();
823 }
824
828 return null_vertex_;
829 }
830
832 size_t num_vertices() const {
833 return root_.members_.size();
834 }
835
837 bool is_empty() const {
838 return root_.members_.empty();
839 }
840
841 public:
845 size_t num_simplices() const {
846 return num_simplices(root());
847 }
848
849 private:
851 size_t num_simplices(const Siblings * sib) const {
852 auto sib_begin = sib->members().begin();
853 auto sib_end = sib->members().end();
854 size_t simplices_number = sib->members().size();
855 for (auto sh = sib_begin; sh != sib_end; ++sh) {
856 if (has_children(sh)) {
857 simplices_number += num_simplices(sh->second.children());
858 }
859 }
860 return simplices_number;
861 }
862
869 int dimension(Siblings const* curr_sib) const {
870 int dim = -1;
871 while (curr_sib != nullptr) {
872 ++dim;
873 curr_sib = curr_sib->oncles();
874 }
875 return dim;
876 }
877
878 public:
880 std::vector<std::size_t> num_simplices_by_dimension() const {
881 if (is_empty()) return {};
882 // std::min in case the upper bound got crazy
883 std::vector<std::size_t> res(std::min(upper_bound_dimension()+1, max_dimension()+1));
884 auto fun = [&res](Simplex_handle, int dim) -> void { ++res[dim]; };
885 for_each_simplex(fun);
886 if (dimension_to_be_lowered_) {
887 GUDHI_CHECK(res.front() != 0, std::logic_error("Bug in Gudhi: non-empty complex has no vertex"));
888 while (res.back() == 0) res.pop_back();
889 dimension_ = static_cast<int>(res.size()) - 1;
890 dimension_to_be_lowered_ = false;
891 } else {
892 GUDHI_CHECK(res.back() != 0,
893 std::logic_error("Bug in Gudhi: there is no simplex of dimension the dimension of the complex"));
894 }
895 return res;
896 }
897
901 int dimension(Simplex_handle sh) const {
902 return dimension(self_siblings(sh));
903 }
904
910 return dimension_;
911 }
912
919 int dimension() const {
920 if (dimension_to_be_lowered_)
921 lower_upper_bound_dimension();
922 return dimension_;
923 }
924
930 int num_parameters() const {
931 return number_of_parameters_;
932 }
933
937 void set_num_parameters(int new_number) {
938 number_of_parameters_ = new_number;
939 }
940
945 auto euler_characteristic() const {
946 using ssize_t = std::make_signed_t<std::size_t>;
947
948 auto dimension_count = num_simplices_by_dimension();
949 ssize_t euler = 0;
950 ssize_t sign = 1;
951 for (const ssize_t count : dimension_count) {
952 euler += sign * count;
953 sign = -sign;
954 }
955 return euler;
956 }
957
960 template<class SimplexHandle>
961 bool has_children(SimplexHandle sh) const {
962 // Here we rely on the root using null_vertex(), which cannot match any real vertex.
963 return (sh->second.children()->parent() == sh->first);
964 }
965
966 private:
968
972 Siblings const* children(Simplex_handle sh) const {
973 GUDHI_CHECK(has_children(sh), std::invalid_argument("Simplex_tree::children - argument has no child"));
974 return sh->second.children();
975 }
976
977 public:
985 template<class InputVertexRange = std::initializer_list<Vertex_handle>>
986 Simplex_handle find(const InputVertexRange & s) const {
987 auto first = std::begin(s);
988 auto last = std::end(s);
989
990 if (first == last)
991 return null_simplex(); // ----->>
992
993 // Copy before sorting
994 std::vector<Vertex_handle> copy(first, last);
995 std::sort(std::begin(copy), std::end(copy));
996 return find_simplex(copy);
997 }
998
999 private:
1001 Simplex_handle find_simplex(const std::vector<Vertex_handle> & simplex) const {
1002 Siblings const* tmp_sib = &root_;
1003 Dictionary_const_it tmp_dit;
1004 auto vi = simplex.begin();
1006 // Equivalent to the first iteration of the normal loop
1007 GUDHI_CHECK(contiguous_vertices(), "non-contiguous vertices");
1008 Vertex_handle v = *vi++;
1009 if(v < 0 || v >= static_cast<Vertex_handle>(root_.members_.size()))
1010 return null_simplex();
1011 tmp_dit = root_.members_.begin() + v;
1012 if (vi == simplex.end())
1013 return tmp_dit;
1014 if (!has_children(tmp_dit))
1015 return null_simplex();
1016 tmp_sib = tmp_dit->second.children();
1017 }
1018 for (;;) {
1019 tmp_dit = tmp_sib->members_.find(*vi++);
1020 if (tmp_dit == tmp_sib->members_.end())
1021 return null_simplex();
1022 if (vi == simplex.end())
1023 return tmp_dit;
1024 if (!has_children(tmp_dit))
1025 return null_simplex();
1026 tmp_sib = tmp_dit->second.children();
1027 }
1028 }
1029
1032 Simplex_handle find_vertex(Vertex_handle v) const {
1033 if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
1034 assert(contiguous_vertices());
1035 return root_.members_.begin() + v;
1036 } else {
1037 return root_.members_.find(v);
1038 }
1039 }
1040
1041 public:
1043 bool contiguous_vertices() const {
1044 if (root_.members_.empty()) return true;
1045 if (root_.members_.begin()->first != 0) return false;
1046 if (std::prev(root_.members_.end())->first != static_cast<Vertex_handle>(root_.members_.size() - 1)) return false;
1047 return true;
1048 }
1049
1050 private:
1066 template<bool update_fil, bool update_children, bool set_to_null, class Filt>
1067 std::pair<Dictionary_it, bool> insert_node_(Siblings *sib, Vertex_handle v, Filt&& filtration_value) {
1068 std::pair<Dictionary_it, bool> ins = sib->members_.try_emplace(v, sib, std::forward<Filt>(filtration_value));
1069
1070 if constexpr (update_children){
1071 if (!(has_children(ins.first))) {
1072 ins.first->second.assign_children(new Siblings(sib, v));
1073 }
1074 }
1075
1076 if (ins.second){
1077 // Only required when insertion is successful
1078 update_simplex_tree_after_node_insertion(ins.first);
1079 return ins;
1080 }
1081
1082 if constexpr (Options::store_filtration && update_fil){
1083 if (unify_lifetimes(ins.first->second.filtration(), filtration_value)) return ins;
1084 }
1085
1086 if constexpr (set_to_null){
1087 ins.first = Dictionary_it();
1088 }
1089
1090 return ins;
1091 }
1092
1093 protected:
1108 template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>>
1109 std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex,
1111 Siblings * curr_sib = &root_;
1112 std::pair<Dictionary_it, bool> res_insert;
1113 auto vi = simplex.begin();
1114
1115 for (; vi != std::prev(simplex.end()); ++vi) {
1116 GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
1117 res_insert = insert_node_<false, true, false>(curr_sib, *vi, filtration);
1118 curr_sib = res_insert.first->second.children();
1119 }
1120
1121 GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
1122 res_insert = insert_node_<true, false, true>(curr_sib, *vi, filtration);
1123 if (res_insert.second){
1124 int dim = static_cast<int>(boost::size(simplex)) - 1;
1125 if (dim > dimension_) {
1126 // Update dimension if needed
1127 dimension_ = dim;
1128 }
1129 }
1130 return res_insert;
1131 }
1132
1133 public:
1157 template<class InputVertexRange = std::initializer_list<Vertex_handle>>
1158 std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange & simplex,
1160 auto first = std::begin(simplex);
1161 auto last = std::end(simplex);
1162
1163 if (first == last)
1164 return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
1165
1166 // Copy before sorting
1167 std::vector<Vertex_handle> copy(first, last);
1168 std::sort(std::begin(copy), std::end(copy));
1169 return insert_simplex_raw(copy, filtration);
1170 }
1171
1218
1219 // Retro-compatibility
1239 template <class InputVertexRange = std::initializer_list<Vertex_handle> >
1240 std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(const InputVertexRange& n_simplex,
1242 {
1244 }
1245
1246 // possibility of different default values depending on chosen strategy
1271 template <class InputVertexRange = std::initializer_list<Vertex_handle>>
1272 std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(Filtration_maintenance insertion_strategy,
1273 const InputVertexRange& n_simplex)
1274 {
1275 auto get_default_value = [](Filtration_maintenance strategy) -> Filtration_value {
1276 switch (strategy) {
1278 return Filtration_simplex_base_real::get_infinity();
1280 return Filtration_simplex_base_real::get_minus_infinity();
1282 return Filtration_value();
1283 default:
1284 throw std::invalid_argument("Given insertion strategy is not available.");
1285 }
1286 };
1287
1288 return insert_simplex_and_subfaces(insertion_strategy, n_simplex, get_default_value(insertion_strategy));
1289 }
1290
1291 // actual insertion method
1312 template <class InputVertexRange = std::initializer_list<Vertex_handle>>
1313 std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(
1314 [[maybe_unused]] Filtration_maintenance insertion_strategy,
1315 const InputVertexRange& n_simplex,
1317 {
1318 auto first = std::begin(n_simplex);
1319 auto last = std::end(n_simplex);
1320
1321 if (first == last) return {null_simplex(), true}; // FIXME: false would make more sense to me.
1322
1323 thread_local std::vector<Vertex_handle> copy;
1324 copy.clear();
1325 copy.insert(copy.end(), first, last);
1326 std::sort(copy.begin(), copy.end());
1327 auto last_unique = std::unique(copy.begin(), copy.end());
1328 copy.erase(last_unique, copy.end());
1329 GUDHI_CHECK_code(for (Vertex_handle v : copy)
1330 GUDHI_CHECK(v != null_vertex(), "cannot use the dummy null_vertex() as a real vertex"););
1331 // Update dimension if needed. We could wait to see if the insertion succeeds, but I doubt there is much to gain.
1332 dimension_ = (std::max)(dimension_, static_cast<int>(std::distance(copy.begin(), copy.end())) - 1);
1333
1334 if constexpr (Options::store_filtration){
1335 switch (insertion_strategy) {
1337 return _rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
1339 return _insert_simplex_and_subfaces_at_highest(root(), copy.begin(), copy.end(), filtration);
1341 return _insert_simplex_and_subfaces_forcing_filtration_value(root(), copy.begin(), copy.end(), filtration);
1342 default:
1343 throw std::invalid_argument("Given insertion strategy is not available.");
1344 }
1345 } else {
1346 // filtration values not stored, so no differences between the strategies
1347 return _rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
1348 }
1349 }
1350
1351 private:
1352 // To insert {1,2,3,4}, we insert {2,3,4} twice, once at the root, and once below 1.
1353 template <class ForwardVertexIterator, bool update_fil = true>
1354 std::pair<Simplex_handle, bool> _rec_insert_simplex_and_subfaces_sorted(Siblings* sib,
1355 ForwardVertexIterator first,
1356 ForwardVertexIterator last,
1357 const Filtration_value& filt) {
1358 // An alternative strategy would be:
1359 // - try to find the complete simplex, if found (and low filtration) exit
1360 // - insert all the vertices at once in sib
1361 // - loop over those (new or not) simplices, with a recursive call(++first, last)
1362 Vertex_handle vertex_one = *first;
1363
1364 // insert_node_<bool update_fil, bool update_children, bool set_to_null>(sib, ...)
1365 // update_fil: if true, calls `unify_lifetimes` on the new and old filtration value of the node
1366 // update_children: if true, assign a child to the node if it did not have one
1367 // set_to_null: if true, sets returned iterator to null simplex
1368
1369 if (++first == last) return insert_node_<update_fil, false, update_fil>(sib, vertex_one, filt);
1370
1371 // TODO: have special code here, we know we are building the whole subtree from scratch.
1372 auto insertion_result = insert_node_<update_fil, true, false>(sib, vertex_one, filt);
1373
1374 auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, update_fil>(
1375 insertion_result.first->second.children(), first, last, filt);
1376 // No need to continue if the full simplex was already there with a low enough filtration value.
1377 if (res.first != null_simplex())
1378 _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, update_fil>(sib, first, last, filt);
1379 return res;
1380 }
1381
1382 bool _make_subfiltration_non_decreasing(Simplex_handle sh, const Filtration_value& filt) {
1383 Filtration_value& f = _to_node_it(sh)->second.filtration();
1384 bool changed = false;
1385 for (auto sh_b : boundary_simplex_range(sh)) {
1386 bool b_changed = true;
1387 // In this particular loop, only newly inserted faces and eventually (old) top faces can have the same value
1388 // than filt. This avoids going too much down the tree.
1389 if (filt == filtration(sh_b)) b_changed = _make_subfiltration_non_decreasing(sh_b, filt);
1390 // If the face did not change value after calling the recursion, than the intersection won't change f's value.
1391 if (b_changed) changed |= intersect_lifetimes(f, filtration(sh_b));
1392 }
1393 return changed;
1394 }
1395
1396 template <class ForwardVertexIterator>
1397 std::pair<Simplex_handle, bool> _insert_simplex_and_subfaces_at_highest(Siblings* sib,
1398 ForwardVertexIterator first,
1399 ForwardVertexIterator last,
1400 const Filtration_value& filt) {
1401 auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, false>(sib, first, last, filt);
1402 if (res.second) {
1403 _make_subfiltration_non_decreasing(res.first, filt);
1404 } else {
1405 res.first = null_simplex();
1406 }
1407 return res;
1408 }
1409
1410 template <class ForwardVertexIterator>
1411 std::pair<Simplex_handle, bool> _insert_simplex_and_subfaces_forcing_filtration_value(Siblings* sib,
1412 ForwardVertexIterator first,
1413 ForwardVertexIterator last,
1414 const Filtration_value& filt) {
1415 auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, false>(sib, first, last, filt);
1416 if (!res.second) {
1417 res.first = null_simplex();
1418 }
1419 return res;
1420 }
1421
1422 public:
1426 _to_node_it(sh)->second.assign_key(key);
1427 }
1428
1432 std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) const {
1433 assert(dimension(sh) == 1);
1434 return { find_vertex(sh->first), find_vertex(self_siblings(sh)->parent()) };
1435 }
1436
1438 template<class SimplexHandle>
1439 Siblings const* self_siblings(SimplexHandle sh) const {
1440 if (sh->second.children()->parent() == sh->first)
1441 return sh->second.children()->oncles();
1442 else
1443 return sh->second.children();
1444 }
1445
1447 template<class SimplexHandle>
1448 Siblings* self_siblings(SimplexHandle sh) {
1449 // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
1450 // Doing it the other way is not safe
1451 return const_cast<Siblings*>(std::as_const(*this).self_siblings(sh));
1452 }
1453
1454 public:
1456 Siblings* root() { return &root_; }
1457
1459 const Siblings * root() const {
1460 return &root_;
1461 }
1462
1469 void set_dimension(int dimension, bool exact=true) {
1470 dimension_to_be_lowered_ = !exact;
1471 dimension_ = dimension;
1472 }
1473
1474 private:
1482 bool reverse_lexicographic_order(Simplex_handle sh1, Simplex_handle sh2) const {
1485 Simplex_vertex_iterator it1 = rg1.begin();
1486 Simplex_vertex_iterator it2 = rg2.begin();
1487 while (it1 != rg1.end() && it2 != rg2.end()) {
1488 if (*it1 == *it2) {
1489 ++it1;
1490 ++it2;
1491 } else {
1492 return *it1 < *it2;
1493 }
1494 }
1495 return ((it1 == rg1.end()) && (it2 != rg2.end()));
1496 }
1497
1504 struct is_before_in_totally_ordered_filtration {
1505 explicit is_before_in_totally_ordered_filtration(Simplex_tree const* st)
1506 : st_(st) { }
1507
1508 bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const {
1509 // Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
1510 if (!(sh1->second.filtration() == sh2->second.filtration())) {
1511 return sh1->second.filtration() < sh2->second.filtration();
1512 }
1513 // is sh1 a proper subface of sh2
1514 return st_->reverse_lexicographic_order(sh1, sh2);
1515 }
1516
1517 Simplex_tree const* st_;
1518 };
1519
1520 public:
1534 void initialize_filtration(bool ignore_infinite_values = false) const {
1535 if (ignore_infinite_values){
1536 initialize_filtration(is_before_in_totally_ordered_filtration(this), [&](Simplex_handle sh) -> bool {
1537 return is_positive_infinity(filtration(sh));
1538 });
1539 } else {
1540 initialize_filtration(is_before_in_totally_ordered_filtration(this), [](Simplex_handle) -> bool {
1541 return false;
1542 });
1543 }
1544 }
1545
1579 template<typename Comparator, typename Ignorer>
1580 void initialize_filtration(Comparator&& is_before_in_filtration, Ignorer&& ignore_simplex) const {
1581 filtration_vect_.clear();
1582 filtration_vect_.reserve(num_simplices());
1584 if (ignore_simplex(sh)) continue;
1585 filtration_vect_.push_back(sh);
1586 }
1587
1588#ifdef GUDHI_USE_TBB
1589 tbb::parallel_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
1590#else
1591 std::stable_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
1592#endif
1593 }
1594
1602 if (filtration_vect_.empty()) {
1604 }
1605 }
1606
1613 void clear_filtration() const {
1614 filtration_vect_.clear();
1615 }
1616
1617 private:
1631 void rec_coface(std::vector<Vertex_handle> &vertices, Siblings const*curr_sib, int curr_nbVertices,
1632 std::vector<Simplex_handle>& cofaces, bool star, int nbVertices) const {
1633 if (!(star || curr_nbVertices <= nbVertices)) // dimension of actual simplex <= nbVertices
1634 return;
1635 for (Simplex_handle simplex = curr_sib->members().begin(); simplex != curr_sib->members().end(); ++simplex) {
1636 if (vertices.empty()) {
1637 // If we reached the end of the vertices, and the simplex has more vertices than the given simplex
1638 // => we found a coface
1639
1640 // Add a coface if we want the star or if the number of vertices of the current simplex matches with nbVertices
1641 bool addCoface = (star || curr_nbVertices == nbVertices);
1642 if (addCoface)
1643 cofaces.push_back(simplex);
1644 if ((!addCoface || star) && has_children(simplex)) // Rec call
1645 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1646 } else {
1647 if (simplex->first == vertices.back()) {
1648 // If curr_sib matches with the top vertex
1649 bool equalDim = (star || curr_nbVertices == nbVertices); // dimension of actual simplex == nbVertices
1650 bool addCoface = vertices.size() == 1 && equalDim;
1651 if (addCoface)
1652 cofaces.push_back(simplex);
1653 if ((!addCoface || star) && has_children(simplex)) {
1654 // Rec call
1655 Vertex_handle tmp = vertices.back();
1656 vertices.pop_back();
1657 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1658 vertices.push_back(tmp);
1659 }
1660 } else if (simplex->first > vertices.back()) {
1661 return;
1662 } else {
1663 // (simplex->first < vertices.back()
1664 if (has_children(simplex))
1665 rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1666 }
1667 }
1668 }
1669 }
1670
1671 public:
1683
1695 // codimension must be positive or null integer
1696 assert(codimension >= 0);
1697
1698 if constexpr (Options::link_nodes_by_label) {
1700 Static_vertex_vector simp(rg.begin(), rg.end());
1701 // must be sorted in decreasing order
1702 assert(std::is_sorted(simp.begin(), simp.end(), std::greater<Vertex_handle>()));
1703 auto range = Optimized_star_simplex_range(Optimized_star_simplex_iterator(this, std::move(simp)),
1704 Optimized_star_simplex_iterator());
1705 // Lazy filtered range
1706 Fast_cofaces_predicate select(this, codimension, this->dimension(simplex));
1707 return boost::adaptors::filter(range, select);
1708 } else {
1709 Cofaces_simplex_range cofaces;
1711 std::vector<Vertex_handle> copy(rg.begin(), rg.end());
1712 if (codimension + static_cast<int>(copy.size()) > dimension_ + 1 ||
1713 (codimension == 0 && static_cast<int>(copy.size()) > dimension_)) // n+codimension greater than dimension_
1714 return cofaces;
1715 // must be sorted in decreasing order
1716 assert(std::is_sorted(copy.begin(), copy.end(), std::greater<Vertex_handle>()));
1717 bool star = codimension == 0;
1718 rec_coface(copy, &root_, 1, cofaces, star, codimension + static_cast<int>(copy.size()));
1719 return cofaces;
1720 }
1721 }
1722
1723 public:
1747 template<class OneSkeletonGraph>
1748 void insert_graph(const OneSkeletonGraph& skel_graph) {
1749 // the simplex tree must be empty
1750 assert(num_simplices() == 0);
1751
1752 // is there a better way to let the compiler know that we don't mean Simplex_tree::num_vertices?
1753 using boost::num_vertices;
1754
1755 if (num_vertices(skel_graph) == 0) {
1756 return;
1757 }
1758 if (num_edges(skel_graph) == 0) {
1759 dimension_ = 0;
1760 } else {
1761 dimension_ = 1;
1762 }
1763
1764 if constexpr (!Options::stable_simplex_handles)
1765 root_.members_.reserve(num_vertices(skel_graph)); // probably useless in most cases
1766 auto verts = vertices(skel_graph) | boost::adaptors::transformed([&](auto v){
1767 return Dit_value_t(v, Node(&root_, get(vertex_filtration_t(), skel_graph, v))); });
1768 root_.members_.insert(boost::begin(verts), boost::end(verts));
1769 // This automatically sorts the vertices, the graph concept doesn't guarantee the order in which we iterate.
1770
1771 for (Dictionary_it it = boost::begin(root_.members_); it != boost::end(root_.members_); it++) {
1772 update_simplex_tree_after_node_insertion(it);
1773 }
1774
1775 std::pair<typename boost::graph_traits<OneSkeletonGraph>::edge_iterator,
1776 typename boost::graph_traits<OneSkeletonGraph>::edge_iterator> boost_edges = edges(skel_graph);
1777 // boost_edges.first is the equivalent to boost_edges.begin()
1778 // boost_edges.second is the equivalent to boost_edges.end()
1779 for (; boost_edges.first != boost_edges.second; boost_edges.first++) {
1780 auto edge = *(boost_edges.first);
1781 auto u = source(edge, skel_graph);
1782 auto v = target(edge, skel_graph);
1783 if (u == v) throw std::invalid_argument("Self-loops are not simplicial");
1784 // We cannot skip edges with the wrong orientation and expect them to
1785 // come a second time with the right orientation, that does not always
1786 // happen in practice. emplace() should be a NOP when an element with the
1787 // same key is already there, so seeing the same edge multiple times is
1788 // ok.
1789 // Should we actually forbid multiple edges? That would be consistent
1790 // with rejecting self-loops.
1791 if (v < u) std::swap(u, v);
1792 auto sh = _to_node_it(find_vertex(u));
1793 if (!has_children(sh)) {
1794 sh->second.assign_children(new Siblings(&root_, sh->first));
1795 }
1796
1797 insert_node_<false, false, false>(sh->second.children(), v, get(edge_filtration_t(), skel_graph, edge));
1798 }
1799 }
1800
1808 template <class VertexRange = std::initializer_list<Vertex_handle> >
1809 void insert_batch_vertices(VertexRange const& vertices, const Filtration_value& filt = Filtration_value()) {
1810 auto verts = vertices | boost::adaptors::transformed([&](auto v){
1811 return Dit_value_t(v, Node(&root_, filt)); });
1812 root_.members_.insert(boost::begin(verts), boost::end(verts));
1813 if (dimension_ < 0 && !root_.members_.empty()) dimension_ = 0;
1814 if constexpr (Options::link_nodes_by_label) {
1815 for (auto sh = root_.members().begin(); sh != root_.members().end(); sh++) {
1816 // update newly inserted simplex (the one that are not linked)
1817 if (!sh->second.list_max_vertex_hook_.is_linked())
1818 update_simplex_tree_after_node_insertion(sh);
1819 }
1820 }
1821 }
1822
1834 void expansion(int max_dimension) {
1835 if (max_dimension <= 1) return;
1836 clear_filtration(); // Drop the cache.
1837 dimension_ = max_dimension;
1838 for (Dictionary_it root_it = root_.members_.begin();
1839 root_it != root_.members_.end(); ++root_it) {
1840 if (has_children(root_it)) {
1841 siblings_expansion(root_it->second.children(), max_dimension - 1);
1842 }
1843 }
1844 dimension_ = max_dimension - dimension_;
1845 }
1846
1882 , Vertex_handle v
1883 , const Filtration_value& fil
1884 , int dim_max
1885 , std::vector<Simplex_handle>& added_simplices)
1886 {
1905
1906 static_assert(Options::link_nodes_by_label, "Options::link_nodes_by_label must be true");
1907
1908 if (u == v) { // Are we inserting a vertex?
1909 auto res_ins = insert_node_<false, false, false>(&root_, u, fil);
1910 if (res_ins.second) { //if the vertex is not in the complex, insert it
1911 added_simplices.push_back(res_ins.first); //no more insert in root_.members()
1912 if (dimension_ == -1) dimension_ = 0;
1913 }
1914 return; //because the vertex is isolated, no more insertions.
1915 }
1916 // else, we are inserting an edge: ensure that u < v
1917 if (v < u) { std::swap(u,v); }
1918
1919 //Note that we copy Simplex_handle (aka map iterators) in added_simplices
1920 //while we are still modifying the Simplex_tree. Insertions in siblings may
1921 //invalidate Simplex_handles; we take care of this fact by first doing all
1922 //insertion in a Sibling, then inserting all handles in added_simplices.
1923
1924#ifdef GUDHI_DEBUG
1925 //check whether vertices u and v are in the tree. If not, return an error.
1926 auto sh_u = root_.members().find(u);
1927 GUDHI_CHECK(sh_u != root_.members().end() &&
1928 root_.members().find(v) != root_.members().end(),
1929 std::invalid_argument(
1930 "Simplex_tree::insert_edge_as_flag - inserts an edge whose vertices are not in the complex")
1931 );
1932 GUDHI_CHECK(!has_children(sh_u) ||
1933 sh_u->second.children()->members().find(v) == sh_u->second.children()->members().end(),
1934 std::invalid_argument(
1935 "Simplex_tree::insert_edge_as_flag - inserts an already existing edge")
1936 );
1937#endif
1938
1939 // to update dimension
1940 const auto tmp_dim = dimension_;
1941 auto tmp_max_dim = dimension_;
1942
1943 //for all siblings containing a Node labeled with u (including the root), run
1944 //compute_punctual_expansion
1945 //todo parallelize
1946 List_max_vertex* nodes_with_label_u = nodes_by_label(u);//all Nodes with u label
1947
1948 GUDHI_CHECK(nodes_with_label_u != nullptr,
1949 "Simplex_tree::insert_edge_as_flag - cannot find the list of Nodes with label u");
1950
1951 for (auto&& node_as_hook : *nodes_with_label_u)
1952 {
1953 Node& node_u = static_cast<Node&>(node_as_hook); //corresponding node, has label u
1954 Simplex_handle sh_u = simplex_handle_from_node(node_u);
1955 Siblings* sib_u = self_siblings(sh_u);
1956 if (sib_u->members().find(v) != sib_u->members().end()) { //v is the label of a sibling of node_u
1957 int curr_dim = dimension(sib_u);
1958 if (dim_max == -1 || curr_dim < dim_max){
1959 if (!has_children(sh_u)) {
1960 //then node_u was a leaf and now has a new child Node labeled v
1961 //the child v is created in compute_punctual_expansion
1962 node_u.assign_children(new Siblings(sib_u, u));
1963 }
1964 dimension_ = dim_max - curr_dim - 1;
1965 compute_punctual_expansion(
1966 v,
1967 node_u.children(),
1968 fil,
1969 dim_max - curr_dim - 1, //>= 0 if dim_max >= 0, <0 otherwise
1970 added_simplices );
1971 dimension_ = dim_max - dimension_;
1972 if (dimension_ > tmp_max_dim) tmp_max_dim = dimension_;
1973 }
1974 }
1975 }
1976 if (tmp_dim <= tmp_max_dim){
1977 dimension_ = tmp_max_dim;
1978 dimension_to_be_lowered_ = false;
1979 } else {
1980 dimension_ = tmp_dim;
1981 }
1982 }
1983
1984 private:
1994 void compute_punctual_expansion( Vertex_handle v
1995 , Siblings * sib
1996 , const Filtration_value& fil
1997 , int k //k == dim_max - dimension simplices in sib
1998 , std::vector<Simplex_handle>& added_simplices )
1999 { //insertion always succeeds because the edge {u,v} used to not be here.
2000 auto res_ins_v = sib->members().emplace(v, Node(sib,fil));
2001 added_simplices.push_back(res_ins_v.first); //no more insertion in sib
2002 update_simplex_tree_after_node_insertion(res_ins_v.first);
2003
2004 if (k == 0) { // reached the maximal dimension. if max_dim == -1, k is never equal to 0.
2005 dimension_ = 0; // to keep track of the max height of the recursion tree
2006 return;
2007 }
2008
2009 //create the subtree of new Node(v)
2010 create_local_expansion( res_ins_v.first
2011 , sib
2012 , fil
2013 , k
2014 , added_simplices );
2015
2016 //punctual expansion in nodes on the left of v, i.e. with label x < v
2017 for (auto sh = sib->members().begin(); sh != res_ins_v.first; ++sh)
2018 { //if v belongs to N^+(x), punctual expansion
2019 Simplex_handle root_sh = find_vertex(sh->first); //Node(x), x < v
2020 if (has_children(root_sh) &&
2021 root_sh->second.children()->members().find(v) != root_sh->second.children()->members().end())
2022 { //edge {x,v} is in the complex
2023 if (!has_children(sh)){
2024 sh->second.assign_children(new Siblings(sib, sh->first));
2025 }
2026 //insert v in the children of sh, and expand.
2027 compute_punctual_expansion( v
2028 , sh->second.children()
2029 , fil
2030 , k-1
2031 , added_simplices );
2032 }
2033 }
2034 }
2035
2042 void create_local_expansion(
2043 Dictionary_it sh_v //Node with label v which has just been inserted
2044 , Siblings *curr_sib //Siblings containing the node sh_v
2045 , const Filtration_value& fil_uv //Fil value of the edge uv in the zz filtration
2046 , int k //Stopping condition for recursion based on max dim
2047 , std::vector<Simplex_handle> &added_simplices) //range of all new simplices
2048 { //pick N^+(v)
2049 //intersect N^+(v) with labels y > v in curr_sib
2050 Dictionary_it next_it = sh_v;
2051 ++next_it;
2052
2053 if (dimension_ > k) {
2054 dimension_ = k; //to keep track of the max height of the recursion tree
2055 }
2056
2057 create_expansion<true>(curr_sib, sh_v, next_it, fil_uv, k, &added_simplices);
2058 }
2059 //TODO boost::container::ordered_unique_range_t in the creation of a Siblings
2060
2069 void siblings_expansion(
2070 Siblings * siblings // must contain elements
2071 , const Filtration_value& fil
2072 , int k // == max_dim expansion - dimension curr siblings
2073 , std::vector<Simplex_handle> & added_simplices )
2074 {
2075 if (dimension_ > k) {
2076 dimension_ = k; //to keep track of the max height of the recursion tree
2077 }
2078 if (k == 0) { return; } //max dimension
2079 Dictionary_it next = ++(siblings->members().begin());
2080
2081 for (Dictionary_it s_h = siblings->members().begin();
2082 next != siblings->members().end(); ++s_h, ++next)
2083 { //find N^+(s_h)
2084 create_expansion<true>(siblings, s_h, next, fil, k, &added_simplices);
2085 }
2086 }
2087
2090 void siblings_expansion(Siblings * siblings, // must contain elements
2091 int k) {
2092 if (k >= 0 && dimension_ > k) {
2093 dimension_ = k;
2094 }
2095 if (k == 0)
2096 return;
2097 Dictionary_it next = siblings->members().begin();
2098 ++next;
2099
2100 for (Dictionary_it s_h = siblings->members().begin();
2101 s_h != siblings->members().end(); ++s_h, ++next)
2102 {
2103 create_expansion<false>(siblings, s_h, next, s_h->second.filtration(), k);
2104 }
2105 }
2106
2111 template<bool force_filtration_value>
2112 void create_expansion(Siblings * siblings,
2113 Dictionary_it& s_h,
2114 Dictionary_it& next,
2115 const Filtration_value& fil,
2116 int k,
2117 std::vector<Simplex_handle>* added_simplices = nullptr)
2118 {
2119 Simplex_handle root_sh = find_vertex(s_h->first);
2120 thread_local std::vector<std::pair<Vertex_handle, Node> > inter;
2121
2122 if (!has_children(root_sh)) return;
2123
2124 intersection<force_filtration_value>(
2125 inter, // output intersection
2126 next, // begin
2127 siblings->members().end(), // end
2128 root_sh->second.children()->members().begin(),
2129 root_sh->second.children()->members().end(),
2130 fil);
2131 if (inter.size() != 0) {
2132 Siblings * new_sib = new Siblings(siblings, // oncles
2133 s_h->first, // parent
2134 inter); // boost::container::ordered_unique_range_t
2135 for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) {
2136 update_simplex_tree_after_node_insertion(it);
2137 if constexpr (force_filtration_value){
2138 //the way create_expansion is used, added_simplices != nullptr when force_filtration_value == true
2139 added_simplices->push_back(it);
2140 }
2141 }
2142 inter.clear();
2143 s_h->second.assign_children(new_sib);
2144 if constexpr (force_filtration_value){
2145 siblings_expansion(new_sib, fil, k - 1, *added_simplices);
2146 } else {
2147 siblings_expansion(new_sib, k - 1);
2148 }
2149 } else {
2150 // ensure the children property
2151 s_h->second.assign_children(siblings);
2152 inter.clear();
2153 }
2154 }
2155
2158 template<bool force_filtration_value = false>
2159 static void intersection(std::vector<std::pair<Vertex_handle, Node> >& intersection,
2160 Dictionary_const_it begin1, Dictionary_const_it end1,
2161 Dictionary_const_it begin2, Dictionary_const_it end2,
2162 const Filtration_value& filtration_) {
2163 if (begin1 == end1 || begin2 == end2)
2164 return; // ----->>
2165 while (true) {
2166 if (begin1->first == begin2->first) {
2167 if constexpr (force_filtration_value){
2168 intersection.emplace_back(begin1->first, Node(nullptr, filtration_));
2169 } else {
2170 Filtration_value filt = begin1->second.filtration();
2171 intersect_lifetimes(filt, begin2->second.filtration());
2172 intersect_lifetimes(filt, filtration_);
2173 intersection.emplace_back(begin1->first, Node(nullptr, filt));
2174 }
2175 if (++begin1 == end1 || ++begin2 == end2)
2176 return; // ----->>
2177 } else if (begin1->first < begin2->first) {
2178 if (++begin1 == end1)
2179 return;
2180 } else /* begin1->first > begin2->first */ {
2181 if (++begin2 == end2)
2182 return; // ----->>
2183 }
2184 }
2185 }
2186
2187 public:
2206 template< typename Blocker >
2207 void expansion_with_blockers(int max_dim, Blocker block_simplex) {
2208 // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
2209 for (auto& simplex : boost::adaptors::reverse(root_.members())) {
2210 if (has_children(&simplex)) {
2211 siblings_expansion_with_blockers(simplex.second.children(), max_dim, max_dim - 1, block_simplex);
2212 }
2213 }
2214 }
2215
2216 private:
2218 template< typename Blocker >
2219 void siblings_expansion_with_blockers(Siblings* siblings, int max_dim, int k, Blocker block_simplex) {
2220 if (dimension_ < max_dim - k) {
2221 dimension_ = max_dim - k;
2222 }
2223 if (k == 0)
2224 return;
2225 // No need to go deeper
2226 if (siblings->members().size() < 2)
2227 return;
2228 // Reverse loop starting before the last one for 'next' to be the last one
2229 for (auto simplex = std::next(siblings->members().rbegin()); simplex != siblings->members().rend(); simplex++) {
2230 std::vector<std::pair<Vertex_handle, Node> > intersection;
2231 for(auto next = siblings->members().rbegin(); next != simplex; next++) {
2232 bool to_be_inserted = true;
2233 Filtration_value filt = simplex->second.filtration();
2234 // If all the boundaries are present, 'next' needs to be inserted
2235 for (Simplex_handle border : boundary_simplex_range(simplex)) {
2236 Simplex_handle border_child = find_child(border, next->first);
2237 if (border_child == null_simplex()) {
2238 to_be_inserted=false;
2239 break;
2240 }
2241 intersect_lifetimes(filt, filtration(border_child));
2242 }
2243 if (to_be_inserted) {
2244 intersection.emplace_back(next->first, Node(nullptr, filt));
2245 }
2246 }
2247 if (intersection.size() != 0) {
2248 // Reverse the order to insert
2249 Siblings * new_sib = new Siblings(
2250 siblings, // oncles
2251 simplex->first, // parent
2252 boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t
2253 simplex->second.assign_children(new_sib);
2254 std::vector<Vertex_handle> blocked_new_sib_vertex_list;
2255 // As all intersections are inserted, we can call the blocker function on all new_sib members
2256 for (auto new_sib_member = new_sib->members().begin();
2257 new_sib_member != new_sib->members().end();
2258 new_sib_member++) {
2259 update_simplex_tree_after_node_insertion(new_sib_member);
2260 bool blocker_result = block_simplex(new_sib_member);
2261 // new_sib member has been blocked by the blocker function
2262 // add it to the list to be removed - do not perform it while looping on it
2263 if (blocker_result) {
2264 blocked_new_sib_vertex_list.push_back(new_sib_member->first);
2265 // update data structures for all deleted simplices
2266 // can be done in the loop as part of another data structure
2267 update_simplex_tree_before_node_removal(new_sib_member);
2268 }
2269 }
2270 if (blocked_new_sib_vertex_list.size() == new_sib->members().size()) {
2271 // Specific case where all have to be deleted
2272 delete new_sib;
2273 // ensure the children property
2274 simplex->second.assign_children(siblings);
2275 } else {
2276 for (auto& blocked_new_sib_member : blocked_new_sib_vertex_list) {
2277 new_sib->members().erase(blocked_new_sib_member);
2278 }
2279 // ensure recursive call
2280 siblings_expansion_with_blockers(new_sib, max_dim, k - 1, block_simplex);
2281 }
2282 } else {
2283 // ensure the children property
2284 simplex->second.assign_children(siblings);
2285 }
2286 }
2287 }
2288
2293 Simplex_handle find_child(Simplex_handle sh, Vertex_handle vh) const {
2294 if (!has_children(sh))
2295 return null_simplex();
2296
2297 Simplex_handle child = sh->second.children()->find(vh);
2298 // Specific case of boost::flat_map does not find, returns boost::flat_map::end()
2299 // in simplex tree we want a null_simplex()
2300 if (child == sh->second.children()->members().end())
2301 return null_simplex();
2302
2303 return child;
2304 }
2305
2306 public:
2316 void print_hasse(std::ostream& os) const {
2317 os << num_simplices() << " " << std::endl;
2318 // TODO: should use complex_simplex_range ?
2319 for (auto sh : filtration_simplex_range()) {
2320 os << dimension(sh) << " ";
2321 for (auto b_sh : boundary_simplex_range(sh)) {
2322 os << key(b_sh) << " ";
2323 }
2324 os << filtration(sh) << " \n";
2325 }
2326 }
2327
2328 public:
2337 template<class Fun>
2338 void for_each_simplex(Fun&& fun) const {
2339 // Wrap callback so it always returns bool
2340 auto f = [&fun](Simplex_handle sh, int dim) -> bool {
2341 if constexpr (std::is_same_v<void, decltype(fun(sh, dim))>) {
2342 fun(sh, dim);
2343 return false;
2344 } else {
2345 return fun(sh, dim);
2346 }
2347 };
2348 if (!is_empty())
2349 rec_for_each_simplex(root(), 0, f);
2350 }
2351
2352 private:
2353 template<class Fun>
2354 void rec_for_each_simplex(const Siblings* sib, int dim, Fun&& fun) const {
2355 Simplex_handle sh = sib->members().end();
2356 GUDHI_CHECK(sh != sib->members().begin(), "Bug in Gudhi: only the root siblings may be empty");
2357 do {
2358 --sh;
2359 if (!fun(sh, dim) && has_children(sh)) {
2360 rec_for_each_simplex(sh->second.children(), dim+1, fun);
2361 }
2362 // We could skip checking has_children for the first element of the iteration, we know it returns false.
2363 }
2364 while(sh != sib->members().begin());
2365 }
2366
2367 public:
2375 bool modified = false;
2376 auto fun = [&modified, this](Simplex_handle sh, int dim) -> void {
2377 if (dim == 0) return;
2378
2379 Filtration_value& current_filt = _to_node_it(sh)->second.filtration();
2380
2381 // Find the maximum filtration value in the border and assigns it if it is greater than the current
2383 // considers NaN as the lowest possible value.
2384 // stores the filtration modification information
2385 modified |= intersect_lifetimes(current_filt, b->second.filtration());
2386 }
2387 };
2388 // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
2389 for_each_simplex(fun);
2390
2391 if (modified)
2392 clear_filtration(); // Drop the cache.
2393 return modified;
2394 }
2395
2396 public:
2398 void clear() {
2399 root_members_recursive_deletion();
2401 dimension_ = -1;
2402 dimension_to_be_lowered_ = false;
2403 if constexpr (Options::link_nodes_by_label)
2404 nodes_label_to_list_.clear();
2405 }
2406
2420 return false; // ---->>
2421 bool modified = rec_prune_above_filtration(root(), filtration);
2422 if(modified)
2423 clear_filtration(); // Drop the cache.
2424 return modified;
2425 }
2426
2427 private:
2428 bool rec_prune_above_filtration(Siblings* sib, const Filtration_value& filt) {
2429 auto&& list = sib->members();
2430 bool modified = false;
2431 bool emptied = false;
2432 Simplex_handle last;
2433
2434 auto to_remove = [this, filt](Dit_value_t& simplex) {
2435 //if filt and simplex.second.filtration() are non comparable, should return false.
2436 //if simplex.second.filtration() is NaN, should return true.
2437 if (filt < simplex.second.filtration() || !(simplex.second.filtration() == simplex.second.filtration())) {
2438 if (has_children(&simplex)) rec_delete(simplex.second.children());
2439 // dimension may need to be lowered
2440 dimension_to_be_lowered_ = true;
2441 return true;
2442 }
2443 return false;
2444 };
2445
2446 //TODO: `if constexpr` replaceable by `std::erase_if` in C++20? Has a risk of additional runtime,
2447 //so to benchmark first.
2448 if constexpr (Options::stable_simplex_handles) {
2449 modified = false;
2450 for (auto sh = list.begin(); sh != list.end();) {
2451 if (to_remove(*sh)) {
2452 sh = list.erase(sh);
2453 modified = true;
2454 } else {
2455 ++sh;
2456 }
2457 }
2458 emptied = (list.empty() && sib != root());
2459 } else {
2460 last = std::remove_if(list.begin(), list.end(), to_remove);
2461 modified = (last != list.end());
2462 emptied = (last == list.begin() && sib != root());
2463 }
2464
2465 if (emptied) {
2466 // Removing the whole siblings, parent becomes a leaf.
2467 sib->oncles()->members()[sib->parent()].assign_children(sib->oncles());
2468 delete sib;
2469 // dimension may need to be lowered
2470 dimension_to_be_lowered_ = true;
2471 return true;
2472 } else {
2473 // Keeping some elements of siblings. Remove the others, and recurse in the remaining ones.
2474 if constexpr (!Options::stable_simplex_handles) list.erase(last, list.end());
2475 for (auto&& simplex : list)
2476 if (has_children(&simplex)) modified |= rec_prune_above_filtration(simplex.second.children(), filt);
2477 }
2478
2479 return modified;
2480 }
2481
2482 public:
2487 bool prune_above_dimension(int dimension) {
2488 if (dimension >= dimension_)
2489 return false;
2490
2491 bool modified = false;
2492 if (dimension < 0) {
2493 if (num_vertices() > 0) {
2494 root_members_recursive_deletion();
2495 modified = true;
2496 }
2497 // Force dimension to -1, in case user calls `prune_above_dimension(-10)`
2498 dimension = -1;
2499 } else {
2500 modified = rec_prune_above_dimension(root(), dimension, 0);
2501 }
2502 if(modified) {
2503 // Thanks to `if (dimension >= dimension_)` and dimension forced to -1 `if (dimension < 0)`, we know the new dimension
2504 dimension_ = dimension;
2505 clear_filtration(); // Drop the cache.
2506 }
2507 return modified;
2508 }
2509
2510 private:
2511 bool rec_prune_above_dimension(Siblings* sib, int dim, int actual_dim) {
2512 bool modified = false;
2513 auto&& list = sib->members();
2514
2515 for (auto&& simplex : list)
2516 if (has_children(&simplex)) {
2517 if (actual_dim >= dim) {
2518 rec_delete(simplex.second.children());
2519 simplex.second.assign_children(sib);
2520 modified = true;
2521 } else {
2522 modified |= rec_prune_above_dimension(simplex.second.children(), dim, actual_dim + 1);
2523 }
2524 }
2525
2526 return modified;
2527 }
2528
2529 private:
2535 bool lower_upper_bound_dimension() const {
2536 // reset automatic detection to recompute
2537 dimension_to_be_lowered_ = false;
2538 int new_dimension = -1;
2539 // Browse the tree from the left to the right as higher dimension cells are more likely on the left part of the tree
2541#ifdef DEBUG_TRACES
2542 for (auto vertex : simplex_vertex_range(sh)) {
2543 std::clog << " " << vertex;
2544 }
2545 std::clog << std::endl;
2546#endif // DEBUG_TRACES
2547
2548 int sh_dimension = dimension(sh);
2549 if (sh_dimension >= dimension_)
2550 // Stop browsing as soon as the dimension is reached, no need to go further
2551 return false;
2552 new_dimension = (std::max)(new_dimension, sh_dimension);
2553 }
2554 dimension_ = new_dimension;
2555 return true;
2556 }
2557
2558
2559 public:
2569 // Guarantee the simplex has no children
2570 GUDHI_CHECK(!has_children(sh),
2571 std::invalid_argument("Simplex_tree::remove_maximal_simplex - argument has children"));
2572
2573 update_simplex_tree_before_node_removal(sh);
2574
2575 // Simplex is a leaf, it means the child is the Siblings owning the leaf
2576 Dictionary_it nodeIt = _to_node_it(sh);
2577 Siblings* child = nodeIt->second.children();
2578
2579 if ((child->size() > 1) || (child == root())) {
2580 // Not alone, just remove it from members
2581 // Special case when child is the root of the simplex tree, just remove it from members
2582 child->erase(nodeIt);
2583 } else {
2584 // Sibling is emptied : must be deleted, and its parent must point on his own Sibling
2585 child->oncles()->members().at(child->parent()).assign_children(child->oncles());
2586 delete child;
2587 // dimension may need to be lowered
2588 dimension_to_be_lowered_ = true;
2589 }
2590 }
2591
2608 std::pair<Filtration_value, Extended_simplex_type> decode_extended_filtration(
2609 const Filtration_value& f,
2610 const Extended_filtration_data& efd) const
2611 {
2612 std::pair<Filtration_value, Extended_simplex_type> p;
2613 const Filtration_value& minval = efd.minval;
2614 const Filtration_value& maxval = efd.maxval;
2615 if (f >= -2 && f <= -1) {
2616 p.first = minval + (maxval - minval) * (f + 2);
2617 p.second = Extended_simplex_type::UP;
2618 } else if (f >= 1 && f <= 2) {
2619 p.first = minval - (maxval - minval) * (f - 2);
2620 p.second = Extended_simplex_type::DOWN;
2621 } else {
2622 p.first = std::numeric_limits<Filtration_value>::quiet_NaN();
2623 p.second = Extended_simplex_type::EXTRA;
2624 }
2625 return p;
2626 };
2627
2628 //TODO: externalize this method and `decode_extended_filtration`
2646 Extended_filtration_data extend_filtration() {
2647 clear_filtration(); // Drop the cache.
2648
2649 // Compute maximum and minimum of filtration values
2650 Vertex_handle maxvert = std::numeric_limits<Vertex_handle>::min();
2651 Filtration_value minval = Filtration_simplex_base_real::get_infinity();
2652 Filtration_value maxval = Filtration_simplex_base_real::get_minus_infinity();
2653 for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
2654 const Filtration_value& f = this->filtration(sh);
2655 minval = std::min(minval, f);
2656 maxval = std::max(maxval, f);
2657 maxvert = std::max(sh->first, maxvert);
2658 }
2659
2660 GUDHI_CHECK(maxvert < std::numeric_limits<Vertex_handle>::max(), std::invalid_argument("Simplex_tree contains a vertex with the largest Vertex_handle"));
2661 maxvert++;
2662
2663 Simplex_tree st_copy = *this;
2664
2665 // Add point for coning the simplicial complex
2666 this->insert_simplex_raw({maxvert}, -3);
2667
2668 Filtration_value scale = maxval-minval;
2669 if (scale != 0)
2670 scale = 1 / scale;
2671
2672 // For each simplex
2673 std::vector<Vertex_handle> vr;
2674 for (auto sh_copy : st_copy.complex_simplex_range()) {
2675 auto&& simplex_range = st_copy.simplex_vertex_range(sh_copy);
2676 vr.assign(simplex_range.begin(), simplex_range.end());
2677 auto sh = this->find(vr);
2678
2679 // Create cone on simplex
2680 vr.push_back(maxvert);
2681 if (this->dimension(sh) == 0) {
2682 const Filtration_value& v = this->filtration(sh);
2683 Filtration_value scaled_v = (v - minval) * scale;
2684 // Assign ascending value between -2 and -1 to vertex
2685 this->assign_filtration(sh, -2 + scaled_v);
2686 // Assign descending value between 1 and 2 to cone on vertex
2687 this->insert_simplex(vr, 2 - scaled_v);
2688 } else {
2689 // Assign value -3 to simplex and cone on simplex
2690 this->assign_filtration(sh, -3);
2691 this->insert_simplex(vr, -3);
2692 }
2693 }
2694
2695 // Automatically assign good values for simplices
2697
2698 // Return the filtration data
2699 return Extended_filtration_data(minval, maxval);
2700 }
2701
2707 auto filt = filtration_(sh);
2708 for(auto v : simplex_vertex_range(sh))
2709 if(filtration_(find_vertex(v)) == filt)
2710 return v;
2711 return null_vertex();
2712 }
2713
2721 // See issue #251 for potential speed improvements.
2722 auto&& vertices = simplex_vertex_range(sh); // vertices in decreasing order
2723 auto end = std::end(vertices);
2724 auto vi = std::begin(vertices);
2725 GUDHI_CHECK(vi != end, "empty simplex");
2726 auto v0 = *vi;
2727 ++vi;
2728 GUDHI_CHECK(vi != end, "simplex of dimension 0");
2729 if(std::next(vi) == end) return sh; // shortcut for dimension 1
2730 Static_vertex_vector suffix;
2731 suffix.push_back(v0);
2732 auto filt = filtration_(sh);
2733 do
2734 {
2735 Vertex_handle v = *vi;
2736 auto&& children1 = find_vertex(v)->second.children()->members_;
2737 for(auto w : suffix){
2738 // Can we take advantage of the fact that suffix is ordered?
2739 Simplex_handle s = children1.find(w);
2740 if(filtration_(s) == filt)
2741 return s;
2742 }
2743 suffix.push_back(v);
2744 }
2745 while(++vi != end);
2746 return null_simplex();
2747 }
2748
2754 auto filt = filtration_(sh);
2755 // Naive implementation, it can be sped up.
2756 for(auto b : boundary_simplex_range(sh))
2757 if(filtration_(b) == filt)
2759 return sh; // None of its faces has the same filtration.
2760 }
2761
2762 public:
2763 // intrusive list of Nodes with same label using the hooks
2764 typedef boost::intrusive::member_hook<Hooks_simplex_base_link_nodes, typename Hooks_simplex_base_link_nodes::Member_hook_t,
2765 &Hooks_simplex_base_link_nodes::list_max_vertex_hook_>
2766 List_member_hook_t;
2767 // auto_unlink in Member_hook_t is incompatible with constant time size
2768 typedef boost::intrusive::list<Hooks_simplex_base_link_nodes, List_member_hook_t,
2769 boost::intrusive::constant_time_size<false>> List_max_vertex;
2770 // type of hooks stored in each Node, Node inherits from Hooks_simplex_base
2771 typedef typename std::conditional<Options::link_nodes_by_label, Hooks_simplex_base_link_nodes,
2772 Hooks_simplex_base_dummy>::type Hooks_simplex_base;
2773
2776 private:
2777 // if Options::link_nodes_by_label is true, store the lists of Nodes with same label, empty otherwise.
2778 // unordered_map Vertex_handle v -> list of all Nodes with label v.
2779 std::unordered_map<Vertex_handle, List_max_vertex> nodes_label_to_list_;
2780
2781 List_max_vertex* nodes_by_label(Vertex_handle v) {
2782 // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
2783 // Doing it the other way is not safe
2784 return const_cast<List_max_vertex*>(std::as_const(*this).nodes_by_label(v));
2785 }
2786
2787 List_max_vertex const* nodes_by_label(Vertex_handle v) const {
2788 if constexpr (Options::link_nodes_by_label) {
2789 auto it_v = nodes_label_to_list_.find(v);
2790 if (it_v != nodes_label_to_list_.end()) {
2791 return &(it_v->second);
2792 } else {
2793 return nullptr;
2794 }
2795 }
2796 return nullptr;
2797 }
2798
2801 static Simplex_handle simplex_handle_from_node(const Node& node) {
2802 if constexpr (Options::stable_simplex_handles){
2803 //Relies on the Dictionary type to be boost::container::map<Vertex_handle, Node>.
2804 //If the type changes or boost fundamentally changes something on the structure of their map,
2805 //a safer/more general but much slower version is:
2806 // if (node.children()->parent() == label) { // verifies if node is a leaf
2807 // return children->oncles()->find(label);
2808 // } else {
2809 // return children->members().find(label);
2810 // }
2811 //Requires an additional parameter "Vertex_handle label" which is the label of the node.
2812
2813 Dictionary_const_it testIt = node.children()->members().begin();
2814 const Node* testNode = &testIt->second;
2815 auto testIIt = testIt.get();
2816 auto testPtr = testIIt.pointed_node();
2817 //distance between node and pointer to map pair in memory
2818 auto shift = (const char*)(testNode) - (const char*)(testPtr);
2819
2820 //decltype(testPtr) = boost::intrusive::compact_rbtree_node<void*>*
2821 decltype(testPtr) sh_ptr = decltype(testPtr)((const char*)(&node) - shift); //shifts from node to pointer
2822 //decltype(testIIt) =
2823 //boost::intrusive::tree_iterator<
2824 // boost::intrusive::bhtraits<
2825 // boost::container::base_node<
2826 // std::pair<const int, Simplex_tree_node_explicit_storage<Simplex_tree>>,
2827 // boost::container::dtl::intrusive_tree_hook<void*, boost::container::red_black_tree, true>, true>,
2828 // boost::intrusive::rbtree_node_traits<void*, true>,
2829 // boost::intrusive::normal_link,
2830 // boost::intrusive::dft_tag,
2831 // 3>,
2832 // false>
2833 decltype(testIIt) sh_ii;
2834 sh_ii = sh_ptr; //creates ``subiterator'' from pointer
2835 Dictionary_const_it sh(sh_ii); //creates iterator from subiterator
2836
2837 return sh;
2838 } else {
2839 //node needs to be casted as non const, because a const pair* cannot be casted into a Simplex_handle
2840 return (Simplex_handle)(boost::intrusive::get_parent_from_member<Dit_value_t>(const_cast<Node*>(&node),
2841 &Dit_value_t::second));
2842 }
2843 }
2844
2845 // Give access to Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator and keep nodes_by_label and
2846 // simplex_handle_from_node private
2847 friend class Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator<Simplex_tree>;
2848
2849 private:
2850 // update all extra data structures in the Simplex_tree. Must be called after all
2851 // simplex insertions.
2852 void update_simplex_tree_after_node_insertion(Simplex_handle sh) {
2853#ifdef DEBUG_TRACES
2854 std::clog << "update_simplex_tree_after_node_insertion" << std::endl;
2855#endif // DEBUG_TRACES
2856 if constexpr (Options::link_nodes_by_label) {
2857 // Creates an entry with sh->first if not already in the map and insert sh->second at the end of the list
2858 nodes_label_to_list_[sh->first].push_back(_to_node_it(sh)->second);
2859 }
2860 }
2861
2862 // update all extra data structures in the Simplex_tree. Must be called before
2863 // all simplex removals
2864 void update_simplex_tree_before_node_removal(Simplex_handle sh) {
2865#ifdef DEBUG_TRACES
2866 std::clog << "update_simplex_tree_before_node_removal" << std::endl;
2867#endif // DEBUG_TRACES
2868 if constexpr (Options::link_nodes_by_label) {
2869 _to_node_it(sh)->second.unlink_hooks(); // remove from lists of same label Nodes
2870 if (nodes_label_to_list_[sh->first].empty())
2871 nodes_label_to_list_.erase(sh->first);
2872 }
2873 }
2874
2875 public:
2884 void reset_filtration(const Filtration_value& filtration, int min_dim = 0) {
2885 rec_reset_filtration(&root_, filtration, min_dim);
2886 clear_filtration(); // Drop the cache.
2887 }
2888
2889 private:
2895 void rec_reset_filtration(Siblings * sib, const Filtration_value& filt_value, int min_depth) {
2896 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
2897 if (min_depth <= 0) {
2898 sh->second.assign_filtration(filt_value);
2899 }
2900 if (has_children(sh)) {
2901 rec_reset_filtration(sh->second.children(), filt_value, min_depth - 1);
2902 }
2903 }
2904 }
2905
2906 std::size_t num_simplices_and_filtration_serialization_size(Siblings const* sib, std::size_t& fv_byte_size) const {
2907 using namespace Gudhi::simplex_tree;
2908
2909 auto sib_begin = sib->members().begin();
2910 auto sib_end = sib->members().end();
2911 size_t simplices_number = sib->members().size();
2912 for (auto sh = sib_begin; sh != sib_end; ++sh) {
2914 fv_byte_size += get_serialization_size_of(sh->second.filtration());
2915 if (has_children(sh)) {
2916 simplices_number += num_simplices_and_filtration_serialization_size(sh->second.children(), fv_byte_size);
2917 }
2918 }
2919 return simplices_number;
2920 }
2921
2922 public:
2932 std::size_t get_serialization_size() const {
2933 const std::size_t version_byte_size = sizeof(std::int16_t);
2934 const std::size_t np_byte_size = sizeof(decltype(number_of_parameters_));
2935 const std::size_t vh_byte_size = sizeof(Vertex_handle);
2936 std::size_t fv_byte_size = 0;
2937 const std::size_t tree_size = num_simplices_and_filtration_serialization_size(&root_, fv_byte_size);
2938 const std::size_t buffer_byte_size =
2939 version_byte_size + np_byte_size + vh_byte_size + fv_byte_size + tree_size * 2 * vh_byte_size;
2940#ifdef DEBUG_TRACES
2941 std::clog << "Gudhi::simplex_tree::get_serialization_size - buffer size = " << buffer_byte_size << std::endl;
2942#endif // DEBUG_TRACES
2943 return buffer_byte_size;
2944 }
2945
2960 /* Let's take the following simplicial complex as example: */
2961 /* (vertices are represented as letters to ease the understanding) */
2962 /* o---o---o */
2963 /* a b\X/c */
2964 /* o */
2965 /* d */
2966 /* The simplex tree is: */
2967 /* a o b o c o d o */
2968 /* | |\ | */
2969 /* b o c o o d o d */
2970 /* | */
2971 /* d o */
2972 /* The serialization is (without filtration values that comes right after vertex handle value): */
2973 /* 04(number of vertices)0a 0b 0c 0d(list of vertices)01(number of [a] children)0b([a,b] simplex) */
2974 /* 00(number of [a,b] children)02(number of [b] children)0c 0d(list of [b] children)01(number of [b,c] children) */
2975 /* 0d(list of [b,c] children)00(number of [b,c,d] children)00(number of [b,d] children)01(number of [c] children) */
2976 /* 0d(list of [c] children)00(number of [b,d] children)00(number of [d] children) */
2977 /* Without explanation and with filtration values: */
2978 /* 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 */
2979 void serialize(char* buffer, const std::size_t buffer_size) const {
2980 char* buffer_end = serialize_value_to_char_buffer(SERIALIZATION_VERSION, buffer);
2981 buffer_end = serialize_value_to_char_buffer(number_of_parameters_, buffer_end);
2982 buffer_end = rec_serialize(&root_, buffer_end);
2983 if (static_cast<std::size_t>(buffer_end - buffer) != buffer_size)
2984 throw std::invalid_argument("Serialization does not match end of buffer");
2985 }
2986
2987 private:
2989 char* rec_serialize(Siblings const *sib, char* buffer) const {
2990 char* ptr = buffer;
2991 ptr = serialize_value_to_char_buffer(static_cast<Vertex_handle>(sib->members().size()), ptr);
2992#ifdef DEBUG_TRACES
2993 std::clog << "\n" << sib->members().size() << " : ";
2994#endif // DEBUG_TRACES
2995 for (auto& map_el : sib->members()) {
2996 ptr = serialize_value_to_char_buffer(map_el.first, ptr); // Vertex
2998 ptr = serialize_value_to_char_buffer(map_el.second.filtration(), ptr); // Filtration
2999#ifdef DEBUG_TRACES
3000 std::clog << " [ " << map_el.first << " | " << map_el.second.filtration() << " ] ";
3001#endif // DEBUG_TRACES
3002 }
3003 for (auto& map_el : sib->members()) {
3004 if (has_children(&map_el)) {
3005 ptr = rec_serialize(map_el.second.children(), ptr);
3006 } else {
3007 ptr = serialize_value_to_char_buffer(static_cast<Vertex_handle>(0), ptr);
3008#ifdef DEBUG_TRACES
3009 std::cout << "\n0 : ";
3010#endif // DEBUG_TRACES
3011 }
3012 }
3013 return ptr;
3014 }
3015
3016 public:
3035 void deserialize(const char* buffer, const std::size_t buffer_size) {
3036 deserialize(buffer, buffer_size, [](Filtration_value& filtration, const char* ptr) {
3037 return deserialize_value_from_char_buffer(filtration, ptr);
3038 });
3039 }
3040
3065 template <class F>
3066 void deserialize(const char* buffer, const std::size_t buffer_size, F&& deserialize_filtration_value) {
3067 GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty"));
3068 const char* ptr = buffer;
3069 std::int16_t version;
3070 ptr = deserialize_value_from_char_buffer(version, ptr);
3071 if (version != SERIALIZATION_VERSION) {
3072 throw std::invalid_argument("The buffer comes from an non-compatible serialization version of the simplex tree.");
3073 }
3074 ptr = deserialize_value_from_char_buffer(number_of_parameters_, ptr);
3075 // Needs to read size before recursivity to manage new siblings for children
3076 Vertex_handle members_size;
3077 ptr = deserialize_value_from_char_buffer(members_size, ptr);
3078 ptr = rec_deserialize(&root_, members_size, ptr, 0, deserialize_filtration_value);
3079 if (static_cast<std::size_t>(ptr - buffer) != buffer_size) {
3080 throw std::invalid_argument("Deserialization does not match end of buffer");
3081 }
3082 }
3083
3084 private:
3086 template <class F>
3087 const char* rec_deserialize(Siblings* sib,
3088 Vertex_handle members_size,
3089 const char* ptr,
3090 int dim,
3091 [[maybe_unused]] F&& deserialize_filtration_value)
3092 {
3093 // In case buffer is just a 0 char
3094 if (members_size > 0) {
3095 if constexpr (!Options::stable_simplex_handles) sib->members_.reserve(members_size);
3096 Vertex_handle vertex;
3097 // Set explicitly to zero to avoid false positive error raising in debug mode when store_filtration == false
3098 // and to force array like Filtration_value value to be empty.
3099 Filtration_value filtration(Gudhi::simplex_tree::empty_filtration_value);
3100 for (Vertex_handle idx = 0; idx < members_size; idx++) {
3101 ptr = deserialize_value_from_char_buffer(vertex, ptr);
3102 if constexpr (Options::store_filtration) {
3103 ptr = deserialize_filtration_value(filtration, ptr);
3104 }
3105 // Default is no children
3106 // If store_filtration is false, `filtration` is ignored.
3107 sib->members_.emplace_hint(sib->members_.end(), vertex, Node(sib, filtration));
3108 }
3109 Vertex_handle child_size;
3110 for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
3111 update_simplex_tree_after_node_insertion(sh);
3112 ptr = deserialize_value_from_char_buffer(child_size, ptr);
3113 if (child_size > 0) {
3114 Siblings* child = new Siblings(sib, sh->first);
3115 sh->second.assign_children(child);
3116 ptr = rec_deserialize(child, child_size, ptr, dim + 1, deserialize_filtration_value);
3117 }
3118 }
3119 if (dim > dimension_) {
3120 // Update dimension if needed
3121 dimension_ = dim;
3122 }
3123 }
3124 return ptr;
3125 }
3126
3127 public:
3128 // Print a Simplex_tree in os.
3129 friend std::ostream& operator<<(std::ostream& os, const Simplex_tree& st) {
3130 st.for_each_simplex([&](Simplex_handle sh, int dim) {
3131 os << dim << " ";
3132 for (auto v : st.simplex_vertex_range(sh)) {
3133 os << v << " ";
3134 }
3135 // TODO(VR): why adding the key ?? not read ?? << " " << st.key(sh) << " \n";
3136 os << st.filtration(sh) << "\n";
3137 });
3138 return os;
3139 }
3140
3141 friend std::istream& operator>>(std::istream & is, Simplex_tree & st) {
3142 std::vector<Vertex_handle> simplex;
3143 Filtration_value fil;
3144 int max_dim = -1;
3145 while (read_simplex(is, simplex, fil)) {
3146 // read all simplices in the file as a list of vertices
3147 // Warning : simplex_size needs to be casted in int - Can be 0
3148 int dim = static_cast<int> (simplex.size() - 1);
3149 if (max_dim < dim) {
3150 max_dim = dim;
3151 }
3152 // insert every simplex in the simplex tree
3153 st.insert_simplex(simplex, fil);
3154 simplex.clear();
3155 }
3156 st.set_dimension(max_dim);
3157
3158 return is;
3159 }
3160
3161 private:
3162 Vertex_handle null_vertex_;
3164 Siblings root_;
3165 int number_of_parameters_;
3166
3167 // all mutable as their content has no impact on the content of the simplex tree itself
3168 // they correspond to some kind of cache or helper attributes.
3170 mutable std::vector<Simplex_handle> filtration_vect_;
3172 mutable int dimension_;
3173 mutable bool dimension_to_be_lowered_;
3174
3178 static constexpr std::int16_t SERIALIZATION_VERSION = 1;
3179};
3180 // end addtogroup simplex_tree
3182
3183} // namespace Gudhi
3184
3185#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:187
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:295
Iterator over the simplices of the simplicial complex that match the dimension specified by the param...
Definition Simplex_tree_iterators.h:451
Iterator over the simplices of the star of a simplex.
Definition Simplex_tree_star_simplex_iterators.h:162
Data structure to store a set of nodes in a SimplexTree sharing the same parent node.
Definition Simplex_tree_siblings.h:29
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:369
Simplex Tree data structure for representing simplicial complexes.
Definition Simplex_tree.h:102
Simplex_tree(Simplex_tree &&complex_source)
User-defined move constructor relocates the whole tree structure including extra data (Simplex_data) ...
Definition Simplex_tree.h:511
std::pair< Simplex_handle, bool > insert_simplex_and_subfaces(Filtration_maintenance insertion_strategy, const InputVertexRange &n_simplex)
Inserts a N-simplex and all his subfaces, from a N-simplex represented by a range of Vertex_handles,...
Definition Simplex_tree.h:1272
void for_each_simplex(Fun &&fun) const
Definition Simplex_tree.h:2338
Options::Filtration_value Filtration_value
Definition Simplex_tree.h:109
Simplex_tree_boundary_opposite_vertex_simplex_iterator< Simplex_tree > Boundary_opposite_vertex_simplex_iterator
Definition Simplex_tree.h:302
int dimension(Simplex_handle sh) const
Returns the dimension of a simplex.
Definition Simplex_tree.h:901
Filtration_maintenance
List of insertion strategies for insert_simplex_and_subfaces, which takes a simplex and a filtration...
Definition Simplex_tree.h:1176
@ IGNORE_VALIDITY
If the simplex to insert:
Definition Simplex_tree.h:1216
@ INCREASE_NEW
Let be the filtration value given as argument. Inserts the simplex as follows:
Definition Simplex_tree.h:1201
@ LOWER_EXISTING
Let be the filtration value given as argument. Inserts the simplex as follows:
Definition Simplex_tree.h:1189
Siblings const * self_siblings(SimplexHandle sh) const
Definition Simplex_tree.h:1439
std::conditional< Options::link_nodes_by_label, Optimized_cofaces_simplex_filtered_range, std::vector< Simplex_handle > >::type Cofaces_simplex_range
Definition Simplex_tree.h:286
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:1534
Complex_simplex_range complex_simplex_range() const
Returns a range over the simplices of the simplicial complex.
Definition Simplex_tree.h:348
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:2374
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:706
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:2706
std::vector< std::size_t > num_simplices_by_dimension() const
Computes and returns the number of simplices of each dimension in the complex.
Definition Simplex_tree.h:880
bool operator!=(const Simplex_tree< OtherSimplexTreeOptions > &st2) const
Checks if two simplex trees are different.
Definition Simplex_tree.h:716
auto euler_characteristic() const
Computes and returns the Euler characteristic of the non-filtered underlying complex represented by t...
Definition Simplex_tree.h:945
bool prune_above_dimension(int dimension)
Remove all simplices of dimension greater than a given value.
Definition Simplex_tree.h:2487
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:827
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:2720
boost::iterator_range< Boundary_simplex_iterator > Boundary_simplex_range
Definition Simplex_tree.h:298
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const
Returns a range over the vertices of a simplex.
Definition Simplex_tree.h:418
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:456
std::pair< Simplex_handle, bool > insert_simplex_and_subfaces(const InputVertexRange &n_simplex, const Filtration_value &filtration=Filtration_value())
Inserts a N-simplex and all his subfaces, from a N-simplex represented by a range of Vertex_handles,...
Definition Simplex_tree.h:1240
Simplex_tree_simplex_vertex_iterator< Simplex_tree > Simplex_vertex_iterator
Definition Simplex_tree.h:280
void clear()
Remove all the simplices, leaving an empty complex.
Definition Simplex_tree.h:2398
static Simplex_key key(Simplex_handle sh)
Definition Simplex_tree.h:762
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:490
boost::iterator_range< Dimension_simplex_iterator > Dimension_simplex_range
Definition Simplex_tree.h:324
Cofaces_simplex_range star_simplex_range(const Simplex_handle simplex) const
Compute the star of a n simplex.
Definition Simplex_tree.h:1680
boost::transform_iterator< return_first, Dictionary_const_it > Complex_vertex_iterator
Definition Simplex_tree.h:274
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:961
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:2207
boost::iterator_range< Simplex_vertex_iterator > Simplex_vertex_range
Definition Simplex_tree.h:282
void reset_filtration(const Filtration_value &filtration, int min_dim=0)
This function resets the filtration value of all the simplices of dimension at least min_dim....
Definition Simplex_tree.h:2884
void remove_maximal_simplex(Simplex_handle sh)
Remove a maximal simplex.
Definition Simplex_tree.h:2568
std::pair< Filtration_value, Extended_simplex_type > decode_extended_filtration(const 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:2608
void assign_key(Simplex_handle sh, Simplex_key key)
Definition Simplex_tree.h:1425
Options::Simplex_key Simplex_key
Definition Simplex_tree.h:113
Simplex_tree()
Constructs an empty simplex tree.
Definition Simplex_tree.h:466
boost::iterator_range< Complex_simplex_iterator > Complex_simplex_range
Definition Simplex_tree.h:310
Dictionary::const_iterator Simplex_handle
Definition Simplex_tree.h:213
Simplex_tree_boundary_simplex_iterator< Simplex_tree > Boundary_simplex_iterator
Definition Simplex_tree.h:296
void expansion(int max_dimension)
Expands the Simplex_tree containing only its one skeleton until dimension max_dim.
Definition Simplex_tree.h:1834
const Simplex_data & simplex_data(Simplex_handle sh) const
Returns the extra data stored in a simplex.
Definition Simplex_tree.h:819
Simplex_tree_siblings< Simplex_tree, Dictionary > Siblings
Definition Simplex_tree.h:140
void insert_batch_vertices(VertexRange const &vertices, const Filtration_value &filt=Filtration_value())
Inserts several vertices.
Definition Simplex_tree.h:1809
Siblings * self_siblings(SimplexHandle sh)
Definition Simplex_tree.h:1448
Simplex_tree_dimension_simplex_iterator< Simplex_tree > Dimension_simplex_iterator
Definition Simplex_tree.h:322
static const Filtration_value & filtration(Simplex_handle sh)
Definition Simplex_tree.h:781
std::pair< Simplex_handle, Simplex_handle > endpoints(Simplex_handle sh) const
Definition Simplex_tree.h:1432
bool is_empty() const
Returns whether the complex is empty.
Definition Simplex_tree.h:837
Simplex_tree_complex_simplex_iterator< Simplex_tree > Complex_simplex_iterator
Definition Simplex_tree.h:308
Simplex_handle simplex(Simplex_key idx) const
Returns the simplex that has index idx in the filtration.
Definition Simplex_tree.h:772
void print_hasse(std::ostream &os) const
Write the hasse diagram of the simplicial complex in os.
Definition Simplex_tree.h:2316
Simplex_data & simplex_data(Simplex_handle sh)
Returns the extra data stored in a simplex.
Definition Simplex_tree.h:812
void set_num_parameters(int new_number)
Stores the given value as number of parameters of the filtration values.
Definition Simplex_tree.h:937
std::vector< Simplex_handle > Filtration_simplex_range
Definition Simplex_tree.h:326
Filtration_simplex_range::const_iterator Filtration_simplex_iterator
Definition Simplex_tree.h:330
int dimension() const
Returns the dimension of the simplicial complex.
Definition Simplex_tree.h:919
Extended_filtration_data extend_filtration()
Extend filtration for computing extended persistence. This function only uses the filtration values a...
Definition Simplex_tree.h:2646
void maybe_initialize_filtration() const
Initializes the filtration cache if it isn't initialized yet.
Definition Simplex_tree.h:1601
Options::Vertex_handle Vertex_handle
Definition Simplex_tree.h:123
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:362
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:407
bool prune_above_filtration(const Filtration_value &filtration)
Prune above filtration value given as parameter. That is: if is the given filtration value and the ...
Definition Simplex_tree.h:2418
size_t num_vertices() const
Returns the number of vertices in the complex.
Definition Simplex_tree.h:832
Cofaces_simplex_range cofaces_simplex_range(const Simplex_handle simplex, int codimension) const
Compute the cofaces of a n simplex.
Definition Simplex_tree.h:1694
void clear_filtration() const
Clears the filtration cache produced by initialize_filtration().
Definition Simplex_tree.h:1613
Dimension_simplex_range dimension_simplex_range(int dim) const
Returns a range over the simplices of the simplicial complex that match the dimension specified by th...
Definition Simplex_tree.h:374
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:500
size_t num_simplices() const
Returns the number of simplices in the simplex_tree.
Definition Simplex_tree.h:845
boost::iterator_range< Complex_vertex_iterator > Complex_vertex_range
Definition Simplex_tree.h:276
static Simplex_key null_key()
Returns a fixed number not in the interval [0, num_simplices()).
Definition Simplex_tree.h:807
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:2753
void assign_filtration(Simplex_handle sh, const Filtration_value &fv)
Definition Simplex_tree.h:792
int upper_bound_dimension() const
Returns an upper bound on the dimension of the simplicial complex.
Definition Simplex_tree.h:909
Siblings * root()
Definition Simplex_tree.h:1456
int num_parameters() const
Returns the value stored as the number of parameters of the filtration values. The default value stor...
Definition Simplex_tree.h:930
Simplex_tree_skeleton_simplex_iterator< Simplex_tree > Skeleton_simplex_iterator
Definition Simplex_tree.h:315
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:544
const Siblings * root() const
Definition Simplex_tree.h:1459
boost::iterator_range< Boundary_opposite_vertex_simplex_iterator > Boundary_opposite_vertex_simplex_range
Definition Simplex_tree.h:304
void insert_edge_as_flag(Vertex_handle u, Vertex_handle v, const 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:1881
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:526
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:338
void insert_graph(const OneSkeletonGraph &skel_graph)
Inserts a 1-skeleton in an empty Simplex_tree.
Definition Simplex_tree.h:1748
Get_simplex_data_type< Options >::type Simplex_data
Definition Simplex_tree.h:119
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:986
std::pair< Simplex_handle, bool > insert_simplex(const InputVertexRange &simplex, const Filtration_value &filtration=Filtration_value())
Insert a simplex, represented by a range of Vertex_handles, in the simplicial complex.
Definition Simplex_tree.h:1158
boost::iterator_range< Skeleton_simplex_iterator > Skeleton_simplex_range
Definition Simplex_tree.h:318
~Simplex_tree()
Destructor; deallocates the whole tree structure.
Definition Simplex_tree.h:519
void initialize_filtration(Comparator &&is_before_in_filtration, Ignorer &&ignore_simplex) const
Initializes the filtration cache, i.e. sorts the simplices according to the specified order....
Definition Simplex_tree.h:1580
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:802
std::pair< Simplex_handle, bool > insert_simplex_and_subfaces(Filtration_maintenance insertion_strategy, const InputVertexRange &n_simplex, const Filtration_value &filtration)
Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of Vertex_handles,...
Definition Simplex_tree.h:1313
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:439
void set_dimension(int dimension, bool exact=true)
Set a dimension for the simplicial complex.
Definition Simplex_tree.h:1469
Graph simplicial complex methods.
bool is_positive_infinity(const Arithmetic_filtration_value &f)
Returns true if and only if the given filtration value is at infinity. This is the overload for when ...
Definition filtration_value_utils.h:43
bool unify_lifetimes(Arithmetic_filtration_value &f1, const Arithmetic_filtration_value &f2)
Given two filtration values at which a simplex exists, stores in the first value the minimal union of...
Definition filtration_value_utils.h:62
bool intersect_lifetimes(Arithmetic_filtration_value &f1, const Arithmetic_filtration_value &f2)
Given two filtration values, stores in the first value the lowest common upper bound of the two value...
Definition filtration_value_utils.h:79
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.
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 of the template parameter for the class Gudhi::Simplex_tree<SimplexTreeOptions>.
Definition SimplexTreeOptions.h:17
static const bool store_key
If true, each simplex has extra storage for one Simplex_key. Necessary for Persistent_cohomology.
Definition SimplexTreeOptions.h:29
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
static const bool link_nodes_by_label
If true, the lists of Node with same label are stored to enhance cofaces and stars access.
Definition SimplexTreeOptions.h:39
IndexingTag Indexing_tag
Forced for now.
Definition SimplexTreeOptions.h:19
static const bool stable_simplex_handles
If true, Simplex_handle will not be invalidated after insertions or removals.
Definition SimplexTreeOptions.h:41
static constexpr bool contiguous_vertices
If true, the list of vertices present in the complex must always be 0, ..., num_vertices-1,...
Definition SimplexTreeOptions.h:37