multipers 2.4.0b1__cp312-cp312-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. multipers/.dylibs/libboost_timer.dylib +0 -0
  2. multipers/.dylibs/libc++.1.0.dylib +0 -0
  3. multipers/.dylibs/libtbb.12.17.dylib +0 -0
  4. multipers/__init__.py +33 -0
  5. multipers/_signed_measure_meta.py +426 -0
  6. multipers/_slicer_meta.py +231 -0
  7. multipers/array_api/__init__.py +62 -0
  8. multipers/array_api/numpy.py +124 -0
  9. multipers/array_api/torch.py +133 -0
  10. multipers/data/MOL2.py +458 -0
  11. multipers/data/UCR.py +18 -0
  12. multipers/data/__init__.py +1 -0
  13. multipers/data/graphs.py +466 -0
  14. multipers/data/immuno_regions.py +27 -0
  15. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  16. multipers/data/pytorch2simplextree.py +91 -0
  17. multipers/data/shape3d.py +101 -0
  18. multipers/data/synthetic.py +113 -0
  19. multipers/distances.py +202 -0
  20. multipers/filtration_conversions.pxd +736 -0
  21. multipers/filtration_conversions.pxd.tp +226 -0
  22. multipers/filtrations/__init__.py +21 -0
  23. multipers/filtrations/density.py +529 -0
  24. multipers/filtrations/filtrations.py +480 -0
  25. multipers/filtrations.pxd +534 -0
  26. multipers/filtrations.pxd.tp +332 -0
  27. multipers/function_rips.cpython-312-darwin.so +0 -0
  28. multipers/function_rips.pyx +104 -0
  29. multipers/grids.cpython-312-darwin.so +0 -0
  30. multipers/grids.pyx +538 -0
  31. multipers/gudhi/Persistence_slices_interface.h +213 -0
  32. multipers/gudhi/Simplex_tree_interface.h +274 -0
  33. multipers/gudhi/Simplex_tree_multi_interface.h +648 -0
  34. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
  35. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
  36. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
  37. multipers/gudhi/gudhi/Debug_utils.h +52 -0
  38. multipers/gudhi/gudhi/Degree_rips_bifiltration.h +2307 -0
  39. multipers/gudhi/gudhi/Dynamic_multi_parameter_filtration.h +2524 -0
  40. multipers/gudhi/gudhi/Fields/Multi_field.h +453 -0
  41. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +460 -0
  42. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +444 -0
  43. multipers/gudhi/gudhi/Fields/Multi_field_small.h +584 -0
  44. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +490 -0
  45. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +580 -0
  46. multipers/gudhi/gudhi/Fields/Z2_field.h +391 -0
  47. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +389 -0
  48. multipers/gudhi/gudhi/Fields/Zp_field.h +493 -0
  49. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +384 -0
  50. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +492 -0
  51. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
  52. multipers/gudhi/gudhi/Matrix.h +2200 -0
  53. multipers/gudhi/gudhi/Multi_filtration/Multi_parameter_generator.h +1712 -0
  54. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_conversions.h +237 -0
  55. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_utils.h +225 -0
  56. multipers/gudhi/gudhi/Multi_parameter_filtered_complex.h +485 -0
  57. multipers/gudhi/gudhi/Multi_parameter_filtration.h +2643 -0
  58. multipers/gudhi/gudhi/Multi_persistence/Box.h +233 -0
  59. multipers/gudhi/gudhi/Multi_persistence/Line.h +309 -0
  60. multipers/gudhi/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h +268 -0
  61. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_cohomology.h +159 -0
  62. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_matrix.h +463 -0
  63. multipers/gudhi/gudhi/Multi_persistence/Point.h +853 -0
  64. multipers/gudhi/gudhi/Off_reader.h +173 -0
  65. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +834 -0
  66. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +838 -0
  67. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +833 -0
  68. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1367 -0
  69. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1157 -0
  70. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +869 -0
  71. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +905 -0
  72. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +122 -0
  73. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +260 -0
  74. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +288 -0
  75. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +170 -0
  76. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +247 -0
  77. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +571 -0
  78. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +182 -0
  79. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +130 -0
  80. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +235 -0
  81. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +312 -0
  82. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1092 -0
  83. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +923 -0
  84. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +914 -0
  85. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +930 -0
  86. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1071 -0
  87. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +203 -0
  88. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +886 -0
  89. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +984 -0
  90. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1213 -0
  91. multipers/gudhi/gudhi/Persistence_matrix/index_mapper.h +58 -0
  92. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +227 -0
  93. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +200 -0
  94. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +166 -0
  95. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +319 -0
  96. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +562 -0
  97. multipers/gudhi/gudhi/Persistence_on_a_line.h +152 -0
  98. multipers/gudhi/gudhi/Persistence_on_rectangle.h +617 -0
  99. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
  100. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
  101. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
  102. multipers/gudhi/gudhi/Persistent_cohomology.h +769 -0
  103. multipers/gudhi/gudhi/Points_off_io.h +171 -0
  104. multipers/gudhi/gudhi/Projective_cover_kernel.h +379 -0
  105. multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
  106. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +559 -0
  107. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
  108. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +121 -0
  109. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
  110. multipers/gudhi/gudhi/Simplex_tree/filtration_value_utils.h +155 -0
  111. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
  112. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
  113. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +60 -0
  114. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +105 -0
  115. multipers/gudhi/gudhi/Simplex_tree.h +3170 -0
  116. multipers/gudhi/gudhi/Slicer.h +848 -0
  117. multipers/gudhi/gudhi/Thread_safe_slicer.h +393 -0
  118. multipers/gudhi/gudhi/distance_functions.h +62 -0
  119. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
  120. multipers/gudhi/gudhi/multi_simplex_tree_helpers.h +147 -0
  121. multipers/gudhi/gudhi/persistence_interval.h +263 -0
  122. multipers/gudhi/gudhi/persistence_matrix_options.h +188 -0
  123. multipers/gudhi/gudhi/reader_utils.h +367 -0
  124. multipers/gudhi/gudhi/simple_mdspan.h +484 -0
  125. multipers/gudhi/gudhi/slicer_helpers.h +779 -0
  126. multipers/gudhi/tmp_h0_pers/mma_interface_h0.h +223 -0
  127. multipers/gudhi/tmp_h0_pers/naive_merge_tree.h +536 -0
  128. multipers/io.cpython-312-darwin.so +0 -0
  129. multipers/io.pyx +472 -0
  130. multipers/ml/__init__.py +0 -0
  131. multipers/ml/accuracies.py +90 -0
  132. multipers/ml/invariants_with_persistable.py +79 -0
  133. multipers/ml/kernels.py +176 -0
  134. multipers/ml/mma.py +713 -0
  135. multipers/ml/one.py +472 -0
  136. multipers/ml/point_clouds.py +352 -0
  137. multipers/ml/signed_measures.py +1667 -0
  138. multipers/ml/sliced_wasserstein.py +461 -0
  139. multipers/ml/tools.py +113 -0
  140. multipers/mma_structures.cpython-312-darwin.so +0 -0
  141. multipers/mma_structures.pxd +134 -0
  142. multipers/mma_structures.pyx +1483 -0
  143. multipers/mma_structures.pyx.tp +1126 -0
  144. multipers/multi_parameter_rank_invariant/diff_helpers.h +85 -0
  145. multipers/multi_parameter_rank_invariant/euler_characteristic.h +95 -0
  146. multipers/multi_parameter_rank_invariant/function_rips.h +317 -0
  147. multipers/multi_parameter_rank_invariant/hilbert_function.h +761 -0
  148. multipers/multi_parameter_rank_invariant/persistence_slices.h +149 -0
  149. multipers/multi_parameter_rank_invariant/rank_invariant.h +350 -0
  150. multipers/multiparameter_edge_collapse.py +41 -0
  151. multipers/multiparameter_module_approximation/approximation.h +2541 -0
  152. multipers/multiparameter_module_approximation/debug.h +107 -0
  153. multipers/multiparameter_module_approximation/format_python-cpp.h +292 -0
  154. multipers/multiparameter_module_approximation/utilities.h +428 -0
  155. multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
  156. multipers/multiparameter_module_approximation.pyx +286 -0
  157. multipers/ops.cpython-312-darwin.so +0 -0
  158. multipers/ops.pyx +231 -0
  159. multipers/pickle.py +89 -0
  160. multipers/plots.py +550 -0
  161. multipers/point_measure.cpython-312-darwin.so +0 -0
  162. multipers/point_measure.pyx +409 -0
  163. multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
  164. multipers/simplex_tree_multi.pxd +136 -0
  165. multipers/simplex_tree_multi.pyx +11719 -0
  166. multipers/simplex_tree_multi.pyx.tp +2102 -0
  167. multipers/slicer.cpython-312-darwin.so +0 -0
  168. multipers/slicer.pxd +2097 -0
  169. multipers/slicer.pxd.tp +263 -0
  170. multipers/slicer.pyx +13042 -0
  171. multipers/slicer.pyx.tp +1259 -0
  172. multipers/tensor/tensor.h +672 -0
  173. multipers/tensor.pxd +13 -0
  174. multipers/test.pyx +44 -0
  175. multipers/tests/__init__.py +70 -0
  176. multipers/torch/__init__.py +1 -0
  177. multipers/torch/diff_grids.py +240 -0
  178. multipers/torch/rips_density.py +310 -0
  179. multipers/vector_interface.pxd +46 -0
  180. multipers-2.4.0b1.dist-info/METADATA +131 -0
  181. multipers-2.4.0b1.dist-info/RECORD +184 -0
  182. multipers-2.4.0b1.dist-info/WHEEL +6 -0
  183. multipers-2.4.0b1.dist-info/licenses/LICENSE +21 -0
  184. multipers-2.4.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3170 @@
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>
34
+ #include <gudhi/graph_simplicial_complex.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
+
68
+ namespace Gudhi {
69
+
70
+ /** \addtogroup simplex_tree
71
+ * @{
72
+ */
73
+
74
+ /**
75
+ * \class Extended_simplex_type Simplex_tree.h gudhi/Simplex_tree.h
76
+ * \brief Extended simplex type data structure for representing the type of simplices in an extended filtration.
77
+ *
78
+ * \details The extended simplex type can be either UP (which means
79
+ * that the simplex was present originally, and is thus part of the ascending extended filtration), DOWN (which means
80
+ * that the simplex is the cone of an original simplex, and is thus part of the descending extended filtration) or
81
+ * EXTRA (which means the simplex is the cone point).
82
+ *
83
+ * Details may be found in \cite Cohen-Steiner2009 and section 2.2 in \cite Carriere16.
84
+ *
85
+ */
86
+ enum class Extended_simplex_type {UP, DOWN, EXTRA};
87
+
88
+ /**
89
+ * \class Simplex_tree Simplex_tree.h gudhi/Simplex_tree.h
90
+ * \brief Simplex Tree data structure for representing simplicial complexes.
91
+ *
92
+ * \details Every simplex \f$[v_0, \cdots ,v_d]\f$ admits a canonical orientation
93
+ * induced by the order relation on vertices \f$ v_0 < \cdots < v_d \f$.
94
+ *
95
+ * Details may be found in \cite boissonnatmariasimplextreealgorithmica.
96
+ *
97
+ * \implements FilteredComplex
98
+ *
99
+ */
100
+
101
+ template<typename SimplexTreeOptions = Simplex_tree_options_default>
102
+ class Simplex_tree {
103
+ public:
104
+ typedef SimplexTreeOptions Options;
105
+ typedef typename Options::Indexing_tag Indexing_tag;
106
+ /** \brief Type for the value of the filtration function.
107
+ *
108
+ * Should implement the @ref FiltrationValue concept. */
109
+ typedef typename Options::Filtration_value Filtration_value;
110
+ /** \brief Key associated to each simplex.
111
+ *
112
+ * Must be an integer type. */
113
+ typedef typename Options::Simplex_key Simplex_key;
114
+ /** \brief Extra data stored in each simplex.
115
+ *
116
+ * When extra data type is defined by the user, the extra data gets a
117
+ * <a target="_blank" href="https://en.cppreference.com/w/cpp/language/default_initialization">default-initialization
118
+ * </a> behaviour, which may mean an indeterminate value. */
119
+ typedef typename Get_simplex_data_type<Options>::type Simplex_data;
120
+ /** \brief Type for the vertex handle.
121
+ *
122
+ * Must be a signed integer type. It admits a total order <. */
123
+ typedef typename Options::Vertex_handle Vertex_handle;
124
+
125
+ /* Type of node in the simplex tree. */
126
+ typedef Simplex_tree_node_explicit_storage<Simplex_tree> Node;
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
+
139
+ /** \brief Set of nodes sharing a same parent in the simplex tree. */
140
+ typedef Simplex_tree_siblings<Simplex_tree, Dictionary> Siblings;
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 Filtration_value get_minus_infinity() {
178
+ if constexpr (std::numeric_limits<Filtration_value>::has_infinity) {
179
+ return -inf_;
180
+ } else {
181
+ return std::numeric_limits<Filtration_value>::lowest();
182
+ }
183
+ }
184
+
185
+ private:
186
+ Filtration_value filt_;
187
+
188
+ inline static const Filtration_value inf_ = std::numeric_limits<Filtration_value>::has_infinity
189
+ ? std::numeric_limits<Filtration_value>::infinity()
190
+ : std::numeric_limits<Filtration_value>::max();
191
+ };
192
+
193
+ struct Filtration_simplex_base_dummy {
194
+ Filtration_simplex_base_dummy() {}
195
+ Filtration_simplex_base_dummy(Filtration_value GUDHI_CHECK_code(f)) {
196
+ // GUDHI_CHECK(f == null_, "filtration value specified in the constructor for a complex that does not store them");
197
+ }
198
+ void assign_filtration(const Filtration_value& GUDHI_CHECK_code(f)) {
199
+ // GUDHI_CHECK(f == null_, "filtration value assigned for a complex that does not store them");
200
+ }
201
+ const Filtration_value& filtration() const { return null_; }
202
+
203
+ private:
204
+ inline static const Filtration_value null_{Gudhi::simplex_tree::empty_filtration_value};
205
+ };
206
+ typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real,
207
+ Filtration_simplex_base_dummy>::type Filtration_simplex_base;
208
+
209
+ public:
210
+ /** \brief Handle type to a simplex contained in the simplicial complex represented
211
+ * by the simplex tree.
212
+ *
213
+ * They are essentially pointers into internal vectors, and any insertion or removal
214
+ * of a simplex may invalidate any other Simplex_handle in the complex,
215
+ * unless Options::stable_simplex_handles == true. */
216
+ typedef typename Dictionary::const_iterator Simplex_handle;
217
+
218
+ private:
219
+ typedef typename Dictionary::iterator Dictionary_it;
220
+ typedef typename Dictionary::const_iterator Dictionary_const_it;
221
+ typedef typename Dictionary_it::value_type Dit_value_t;
222
+
223
+ struct return_first {
224
+ Vertex_handle operator()(const Dit_value_t& p_sh) const {
225
+ return p_sh.first;
226
+ }
227
+ };
228
+
229
+ private:
230
+ /** \brief An iterator for an optimized search for the star of a simplex.
231
+ *
232
+ * \details It requires the Options::link_nodes_by_label to be true and store two
233
+ * extra pointers in each node of the simplex tree. The Nodes of same label are
234
+ * linked in a list.
235
+ */
236
+ using Optimized_star_simplex_iterator = Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
237
+ /** \brief Range for an optimized search for the star of a simplex. */
238
+ using Optimized_star_simplex_range = boost::iterator_range<Optimized_star_simplex_iterator>;
239
+
240
+ class Fast_cofaces_predicate {
241
+ Simplex_tree const* st_;
242
+ int codim_;
243
+ int dim_;
244
+ public:
245
+ Fast_cofaces_predicate(Simplex_tree const* st, int codim, int dim)
246
+ : st_(st), codim_(codim), dim_(codim + dim) {}
247
+ bool operator()( const Simplex_handle iter ) const {
248
+ if (codim_ == 0)
249
+ // Always true for a star
250
+ return true;
251
+ // Specific coface case
252
+ return dim_ == st_->dimension(iter);
253
+ }
254
+ };
255
+
256
+ // WARNING: this is safe only because boost::filtered_range is containing a copy of begin and end iterator.
257
+ // This would not be safe if it was containing a pointer to a range (maybe the case for std::views)
258
+ using Optimized_cofaces_simplex_filtered_range = boost::filtered_range<Fast_cofaces_predicate,
259
+ Optimized_star_simplex_range>;
260
+
261
+
262
+ /** The largest dimension supported for simplex trees.
263
+ * 40 seems a conservative bound for now, as 2^41 simplices would not fit on the biggest hard-drive. */
264
+ static constexpr int max_dimension() { return 40; }
265
+ public:
266
+ /** \name Range and iterator types
267
+ *
268
+ * The naming convention is Container_content_(iterator/range). A Container_content_range is
269
+ * essentially an object on which the methods begin() and end() can be called. They both return
270
+ * an object of type Container_content_iterator, and allow the traversal of the range
271
+ * [ begin();end() ).
272
+ * @{ */
273
+
274
+ /** \brief Iterator over the vertices of the simplicial complex.
275
+ *
276
+ * 'value_type' is Vertex_handle. */
277
+ typedef boost::transform_iterator<return_first, Dictionary_const_it> Complex_vertex_iterator;
278
+ /** \brief Range over the vertices of the simplicial complex. */
279
+ typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
280
+ /** \brief Iterator over the vertices of a simplex.
281
+ *
282
+ * 'value_type' is Vertex_handle. */
283
+ typedef Simplex_tree_simplex_vertex_iterator<Simplex_tree> Simplex_vertex_iterator;
284
+ /** \brief Range over the vertices of a simplex. */
285
+ typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
286
+ /** \brief Range over the cofaces of a simplex. */
287
+ typedef typename std::conditional<Options::link_nodes_by_label,
288
+ Optimized_cofaces_simplex_filtered_range, // faster implementation
289
+ std::vector<Simplex_handle>>::type Cofaces_simplex_range;
290
+
291
+ /** \private
292
+ * static_vector still has some overhead compared to a trivial hand-made version using std::aligned_storage, or
293
+ * compared to reusing a static object. */
294
+ using Static_vertex_vector = boost::container::static_vector<Vertex_handle, max_dimension()>;
295
+
296
+ /** \brief Iterator over the simplices of the boundary of a simplex.
297
+ *
298
+ * 'value_type' is Simplex_handle. */
299
+ typedef Simplex_tree_boundary_simplex_iterator<Simplex_tree> Boundary_simplex_iterator;
300
+ /** \brief Range over the simplices of the boundary of a simplex. */
301
+ typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
302
+ /** \brief Iterator over the simplices of the boundary of a simplex and their opposite vertices.
303
+ *
304
+ * 'value_type' is std::pair<Simplex_handle, Vertex_handle>. */
305
+ typedef Simplex_tree_boundary_opposite_vertex_simplex_iterator<Simplex_tree> Boundary_opposite_vertex_simplex_iterator;
306
+ /** \brief Range over the simplices of the boundary of a simplex and their opposite vertices. */
307
+ typedef boost::iterator_range<Boundary_opposite_vertex_simplex_iterator> Boundary_opposite_vertex_simplex_range;
308
+ /** \brief Iterator over the simplices of the simplicial complex.
309
+ *
310
+ * 'value_type' is Simplex_handle. */
311
+ typedef Simplex_tree_complex_simplex_iterator<Simplex_tree> Complex_simplex_iterator;
312
+ /** \brief Range over the simplices of the simplicial complex. */
313
+ typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
314
+ /** \brief Iterator over the simplices of the skeleton of the simplicial complex, for a given
315
+ * dimension.
316
+ *
317
+ * 'value_type' is Simplex_handle. */
318
+ typedef Simplex_tree_skeleton_simplex_iterator<Simplex_tree> Skeleton_simplex_iterator;
319
+ /** \brief Range over the simplices of the skeleton of the simplicial complex, for a given
320
+ * dimension. */
321
+ typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
322
+ /** \brief Iterator over the simplices of the simplicial complex that match the dimension specified by the parameter.
323
+ *
324
+ * 'value_type' is Simplex_handle. */
325
+ typedef Simplex_tree_dimension_simplex_iterator<Simplex_tree> Dimension_simplex_iterator;
326
+ /** \brief Range over the simplices of the simplicial complex that match a given dimension. */
327
+ typedef boost::iterator_range<Dimension_simplex_iterator> Dimension_simplex_range;
328
+ /** \brief Range over the simplices of the simplicial complex, ordered by the filtration. */
329
+ typedef std::vector<Simplex_handle> Filtration_simplex_range;
330
+ /** \brief Iterator over the simplices of the simplicial complex, ordered by the filtration.
331
+ *
332
+ * 'value_type' is Simplex_handle. */
333
+ typedef typename Filtration_simplex_range::const_iterator Filtration_simplex_iterator;
334
+
335
+ /* @} */ // end name range and iterator types
336
+ /** \name Range and iterator methods
337
+ * @{ */
338
+
339
+ /** \brief Returns a range over the vertices of the simplicial complex.
340
+ * The order is increasing according to < on Vertex_handles.*/
341
+ Complex_vertex_range complex_vertex_range() const {
342
+ return Complex_vertex_range(boost::make_transform_iterator(root_.members_.begin(), return_first()),
343
+ boost::make_transform_iterator(root_.members_.end(), return_first()));
344
+ }
345
+
346
+ /** \brief Returns a range over the simplices of the simplicial complex.
347
+ *
348
+ * In the Simplex_tree, the tree is traverse in a depth-first fashion.
349
+ * Consequently, simplices are ordered according to lexicographic order on the list of
350
+ * Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
351
+ Complex_simplex_range complex_simplex_range() const {
352
+ return Complex_simplex_range(Complex_simplex_iterator(this),
353
+ Complex_simplex_iterator());
354
+ }
355
+
356
+ /** \brief Returns a range over the simplices of the dim-skeleton of the simplicial complex.
357
+ *
358
+ * The \f$d\f$-skeleton of a simplicial complex \f$\mathbf{K}\f$ is the simplicial complex containing the
359
+ * simplices of \f$\mathbf{K}\f$ of dimension at most \f$d\f$.
360
+ *
361
+ * @param[in] dim The maximal dimension of the simplices in the skeleton.
362
+ *
363
+ * The simplices are ordered according to lexicographic order on the list of
364
+ * Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
365
+ Skeleton_simplex_range skeleton_simplex_range(int dim) const {
366
+ return Skeleton_simplex_range(Skeleton_simplex_iterator(this, dim),
367
+ Skeleton_simplex_iterator());
368
+ }
369
+
370
+ /** \brief Returns a range over the simplices of the simplicial complex that match the dimension specified by the
371
+ * parameter.
372
+ *
373
+ * @param[in] dim The exact dimension of the simplices.
374
+ *
375
+ * The simplices are ordered according to lexicographic order on the list of
376
+ * Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
377
+ Dimension_simplex_range dimension_simplex_range(int dim) const {
378
+ return Dimension_simplex_range(Dimension_simplex_iterator(this, dim),
379
+ Dimension_simplex_iterator());
380
+ }
381
+
382
+ /** \brief Returns a range over the simplices of the simplicial complex,
383
+ * in the order of the filtration.
384
+ *
385
+ * The filtration is a monotonic function \f$ f: \mathbf{K} \rightarrow \mathbb{R} \f$, i.e. if two simplices
386
+ * \f$\tau\f$ and \f$\sigma\f$ satisfy \f$\tau \subseteq \sigma\f$ then
387
+ * \f$f(\tau) \leq f(\sigma)\f$.
388
+ *
389
+ * The method returns simplices ordered according to increasing filtration values. Ties are
390
+ * resolved by considering inclusion relation (subsimplices appear before their cofaces). If two
391
+ * simplices have same filtration value but are not comparable w.r.t. inclusion, lexicographic
392
+ * order is used.
393
+ *
394
+ * The filtration must be valid. If the filtration has not been initialized yet, the
395
+ * method initializes it (i.e. order the simplices). If the complex has changed since the last time the filtration
396
+ * was initialized, please call `clear_filtration()` or `initialize_filtration()` to recompute it.
397
+ *
398
+ * @note Not thread safe
399
+ */
400
+ Filtration_simplex_range const& filtration_simplex_range(Indexing_tag = Indexing_tag()) const {
401
+ maybe_initialize_filtration();
402
+ return filtration_vect_;
403
+ }
404
+
405
+ /** \brief Returns a range over the vertices of a simplex.
406
+ *
407
+ * The order in which the vertices are visited is the decreasing order for < on Vertex_handles,
408
+ * which is consequently
409
+ * equal to \f$(-1)^{\text{dim} \sigma}\f$ the canonical orientation on the simplex.
410
+ */
411
+ Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const {
412
+ GUDHI_CHECK(sh != null_simplex(), "empty simplex");
413
+ return Simplex_vertex_range(Simplex_vertex_iterator(this, sh),
414
+ Simplex_vertex_iterator(this));
415
+ }
416
+
417
+ /** \brief Returns a range over the simplices of the boundary of a simplex.
418
+ *
419
+ * The boundary of a simplex is the set of codimension \f$1\f$ subsimplices of the simplex.
420
+ * If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation
421
+ * induced by \f$ v_0 < \cdots < v_d \f$, the iterator enumerates the
422
+ * simplices of the boundary in the order:
423
+ * \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from \f$0\f$ to \f$d\f$,
424
+ * where \f$\widehat{v_i}\f$ means that the vertex \f$v_i\f$ is omitted.
425
+ *
426
+ * We note that the alternate sum of the simplices given by the iterator
427
+ * gives \f$(-1)^{\text{dim} \sigma}\f$ the chains corresponding to the boundary
428
+ * of the simplex.
429
+ *
430
+ * @param[in] sh Simplex for which the boundary is computed. */
431
+ template<class SimplexHandle>
432
+ Boundary_simplex_range boundary_simplex_range(SimplexHandle sh) const {
433
+ return Boundary_simplex_range(Boundary_simplex_iterator(this, sh),
434
+ Boundary_simplex_iterator(this));
435
+ }
436
+
437
+ /** \brief Given a simplex, returns a range over the simplices of its boundary and their opposite vertices.
438
+ *
439
+ * The boundary of a simplex is the set of codimension \f$1\f$ subsimplices of the simplex.
440
+ * If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation induced by \f$ v_0 < \cdots < v_d \f$, the
441
+ * iterator enumerates the simplices of the boundary in the order:
442
+ * \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from \f$d\f$ to \f$0\f$, where \f$\widehat{v_i}\f$ means
443
+ * that the vertex \f$v_i\f$, known as the opposite vertex, is omitted from boundary, but returned as the second
444
+ * element of a pair.
445
+ *
446
+ * @param[in] sh Simplex for which the boundary is computed.
447
+ */
448
+ template<class SimplexHandle>
449
+ Boundary_opposite_vertex_simplex_range boundary_opposite_vertex_simplex_range(SimplexHandle sh) const {
450
+ return Boundary_opposite_vertex_simplex_range(Boundary_opposite_vertex_simplex_iterator(this, sh),
451
+ Boundary_opposite_vertex_simplex_iterator(this));
452
+ }
453
+
454
+ /** @} */ // end range and iterator methods
455
+ /** \name Constructor/Destructor
456
+ * @{ */
457
+
458
+ /** \brief Constructs an empty simplex tree. */
459
+ Simplex_tree()
460
+ : null_vertex_(-1),
461
+ root_(nullptr, null_vertex_),
462
+ filtration_vect_(),
463
+ dimension_(-1),
464
+ dimension_to_be_lowered_(false),
465
+ number_of_parameters_(1) {}
466
+
467
+ /**
468
+ * @brief Construct the simplex tree as the copy of a given simplex tree with eventually different template
469
+ * parameters.
470
+ * Therefore, should provide a method converting the filtration values of one tree to the another. All other values
471
+ * are already implicitly convertible if the concept of @ref SimplexTreeOptions is respected (note that there is
472
+ * an eventual loss of precision or an undefined behaviour if a value is converted into a new type too small to
473
+ * contain it). Any extra data (@ref Simplex_data) stored in the simplices are ignored in the copy for now.
474
+ *
475
+ * @tparam OtherSimplexTreeOptions Options of the given simplex tree.
476
+ * @tparam F Method taking an OtherSimplexTreeOptions::Filtration_value as input and returning an
477
+ * Options::Filtration_value.
478
+ * @param complex_source Simplex tree to copy.
479
+ * @param translate_filtration_value Method taking an OtherSimplexTreeOptions::Filtration_value from the source tree
480
+ * as input and returning the corresponding Options::Filtration_value in the new tree.
481
+ */
482
+ template<typename OtherSimplexTreeOptions, typename F>
483
+ Simplex_tree(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
484
+ #ifdef DEBUG_TRACES
485
+ std::clog << "Simplex_tree custom copy constructor" << std::endl;
486
+ #endif // DEBUG_TRACES
487
+ copy_from(complex_source, translate_filtration_value);
488
+ }
489
+
490
+ /** \brief User-defined copy constructor reproduces the whole tree structure including extra data (@ref Simplex_data)
491
+ * stored in the simplices.
492
+ */
493
+ Simplex_tree(const Simplex_tree& complex_source) {
494
+ #ifdef DEBUG_TRACES
495
+ std::clog << "Simplex_tree copy constructor" << std::endl;
496
+ #endif // DEBUG_TRACES
497
+ copy_from(complex_source);
498
+ }
499
+
500
+ /** \brief User-defined move constructor relocates the whole tree structure including extra data (@ref Simplex_data)
501
+ * stored in the simplices.
502
+ * \exception std::invalid_argument In debug mode, if the complex_source is invalid.
503
+ */
504
+ Simplex_tree(Simplex_tree && complex_source) {
505
+ #ifdef DEBUG_TRACES
506
+ std::clog << "Simplex_tree move constructor" << std::endl;
507
+ #endif // DEBUG_TRACES
508
+ move_from(complex_source);
509
+ }
510
+
511
+ /** \brief Destructor; deallocates the whole tree structure. */
512
+ ~Simplex_tree() {
513
+ root_members_recursive_deletion();
514
+ }
515
+
516
+ /** \brief User-defined copy assignment reproduces the whole tree structure including extra data (@ref Simplex_data)
517
+ * stored in the simplices.
518
+ */
519
+ Simplex_tree& operator= (const Simplex_tree& complex_source) {
520
+ #ifdef DEBUG_TRACES
521
+ std::clog << "Simplex_tree copy assignment" << std::endl;
522
+ #endif // DEBUG_TRACES
523
+ // Self-assignment detection
524
+ if (&complex_source != this) {
525
+ // We start by deleting root_ if not empty
526
+ root_members_recursive_deletion();
527
+
528
+ copy_from(complex_source);
529
+ }
530
+ return *this;
531
+ }
532
+
533
+ /** \brief User-defined move assignment relocates the whole tree structure including extra data (@ref Simplex_data)
534
+ * stored in the simplices.
535
+ * \exception std::invalid_argument In debug mode, if the complex_source is invalid.
536
+ */
537
+ Simplex_tree& operator=(Simplex_tree&& complex_source) {
538
+ #ifdef DEBUG_TRACES
539
+ std::clog << "Simplex_tree move assignment" << std::endl;
540
+ #endif // DEBUG_TRACES
541
+ // Self-assignment detection
542
+ if (&complex_source != this) {
543
+ // root_ deletion in case it was not empty
544
+ root_members_recursive_deletion();
545
+
546
+ move_from(complex_source);
547
+ }
548
+ return *this;
549
+ }
550
+ /** @} */ // end constructor/destructor
551
+
552
+ private:
553
+ // Copy from complex_source to "this"
554
+ void copy_from(const Simplex_tree& complex_source) {
555
+ null_vertex_ = complex_source.null_vertex_;
556
+ filtration_vect_.clear();
557
+ number_of_parameters_ = complex_source.number_of_parameters_;
558
+ dimension_ = complex_source.dimension_;
559
+ dimension_to_be_lowered_ = complex_source.dimension_to_be_lowered_;
560
+ auto root_source = complex_source.root_;
561
+
562
+ // root members copy
563
+ root_.members() =
564
+ Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
565
+ // Needs to reassign children
566
+ for (auto& map_el : root_.members()) {
567
+ map_el.second.assign_children(&root_);
568
+ }
569
+ // Specific for optional data
570
+ if constexpr (!std::is_same_v<Simplex_data, No_simplex_data>) {
571
+ auto dst_iter = root_.members().begin();
572
+ auto src_iter = root_source.members().begin();
573
+
574
+ while(dst_iter != root_.members().end() || src_iter != root_source.members().end()) {
575
+ dst_iter->second.data() = src_iter->second.data();
576
+ dst_iter++;
577
+ src_iter++;
578
+ }
579
+ // Check in debug mode members data were copied
580
+ assert(dst_iter == root_.members().end());
581
+ assert(src_iter == root_source.members().end());
582
+ }
583
+ rec_copy<Options::store_key, true>(
584
+ &root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
585
+ }
586
+
587
+ // Copy from complex_source to "this"
588
+ template<typename OtherSimplexTreeOptions, typename F>
589
+ void copy_from(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
590
+ null_vertex_ = complex_source.null_vertex_;
591
+ filtration_vect_.clear();
592
+ number_of_parameters_ = complex_source.number_of_parameters_;
593
+ dimension_ = complex_source.dimension_;
594
+ dimension_to_be_lowered_ = complex_source.dimension_to_be_lowered_;
595
+ auto root_source = complex_source.root_;
596
+
597
+ // root members copy
598
+ if constexpr (!Options::stable_simplex_handles) root_.members().reserve(root_source.size());
599
+ for (auto& p : root_source.members()){
600
+ if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) {
601
+ auto it = root_.members().try_emplace(
602
+ root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()), p.second.key());
603
+ } else {
604
+ auto it = root_.members().try_emplace(
605
+ root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()));
606
+ }
607
+ }
608
+
609
+ rec_copy<OtherSimplexTreeOptions::store_key, false>(&root_, &root_source, translate_filtration_value);
610
+ }
611
+
612
+ /** \brief depth first search, inserts simplices when reaching a leaf. */
613
+ template<bool store_key, bool copy_simplex_data, typename OtherSiblings, typename F>
614
+ void rec_copy(Siblings *sib, OtherSiblings *sib_source, F&& translate_filtration_value) {
615
+ auto sh_source = sib_source->members().begin();
616
+ for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh, ++sh_source) {
617
+ update_simplex_tree_after_node_insertion(sh);
618
+ if (has_children(sh_source)) {
619
+ Siblings * newsib = new Siblings(sib, sh_source->first);
620
+ if constexpr (!Options::stable_simplex_handles) {
621
+ newsib->members_.reserve(sh_source->second.children()->members().size());
622
+ }
623
+ for (auto & child : sh_source->second.children()->members()){
624
+ Dictionary_it new_it{};
625
+ if constexpr (store_key && Options::store_key) {
626
+ new_it = newsib->members_.emplace_hint(
627
+ newsib->members_.end(),
628
+ child.first,
629
+ Node(newsib, translate_filtration_value(child.second.filtration()), child.second.key()));
630
+ } else {
631
+ new_it = newsib->members_.emplace_hint(newsib->members_.end(),
632
+ child.first,
633
+ Node(newsib, translate_filtration_value(child.second.filtration())));
634
+ }
635
+ // Specific for optional data
636
+ if constexpr (copy_simplex_data && !std::is_same_v<Simplex_data, No_simplex_data>) {
637
+ new_it->second.data() = child.second.data();
638
+ }
639
+ }
640
+ rec_copy<store_key, copy_simplex_data>(newsib, sh_source->second.children(), translate_filtration_value);
641
+ sh->second.assign_children(newsib);
642
+ }
643
+ }
644
+ }
645
+
646
+ // Move from complex_source to "this"
647
+ void move_from(Simplex_tree& complex_source) {
648
+ null_vertex_ = std::move(complex_source.null_vertex_);
649
+ root_ = std::move(complex_source.root_);
650
+ filtration_vect_ = std::move(complex_source.filtration_vect_);
651
+ number_of_parameters_ = std::exchange(complex_source.number_of_parameters_, 1);
652
+ dimension_ = std::exchange(complex_source.dimension_, -1);
653
+ dimension_to_be_lowered_ = std::exchange(complex_source.dimension_to_be_lowered_, false);
654
+ if constexpr (Options::link_nodes_by_label) {
655
+ nodes_label_to_list_.swap(complex_source.nodes_label_to_list_);
656
+ }
657
+ // Need to update root members (children->oncles and children need to point on the new root pointer)
658
+ for (auto& map_el : root_.members()) {
659
+ if (map_el.second.children() != &(complex_source.root_)) {
660
+ // reset children->oncles with the moved root_ pointer value
661
+ map_el.second.children()->oncles_ = &root_;
662
+ } else {
663
+ // if simplex is of dimension 0, oncles_ shall be nullptr
664
+ GUDHI_CHECK(map_el.second.children()->oncles_ == nullptr,
665
+ std::invalid_argument("Simplex_tree move constructor from an invalid Simplex_tree"));
666
+ // and children points on root_ - to be moved
667
+ map_el.second.assign_children(&root_);
668
+ }
669
+ }
670
+ }
671
+
672
+ // delete all root_.members() recursively
673
+ void root_members_recursive_deletion() {
674
+ for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
675
+ if (has_children(sh)) {
676
+ rec_delete(sh->second.children());
677
+ }
678
+ }
679
+ root_.members().clear();
680
+ }
681
+
682
+ // Recursive deletion
683
+ void rec_delete(Siblings * sib) {
684
+ for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
685
+ if (has_children(sh)) {
686
+ rec_delete(sh->second.children());
687
+ }
688
+ }
689
+ delete sib;
690
+ }
691
+
692
+ public:
693
+ template<typename> friend class Simplex_tree;
694
+
695
+ /** \brief Checks if two simplex trees are equal. Any extra data (@ref Simplex_data) stored in the simplices are
696
+ * ignored in the comparison.
697
+ */
698
+ template <class OtherSimplexTreeOptions>
699
+ bool operator==(const Simplex_tree<OtherSimplexTreeOptions>& st2) const {
700
+ if ((null_vertex_ != st2.null_vertex_) ||
701
+ (dimension_ != st2.dimension_ && !dimension_to_be_lowered_ && !st2.dimension_to_be_lowered_) ||
702
+ (number_of_parameters_ != st2.number_of_parameters_))
703
+ return false;
704
+ return rec_equal(&root_, &st2.root_);
705
+ }
706
+
707
+ /** \brief Checks if two simplex trees are different. */
708
+ template<class OtherSimplexTreeOptions>
709
+ bool operator!=(const Simplex_tree<OtherSimplexTreeOptions>& st2) const {
710
+ return (!(*this == st2));
711
+ }
712
+
713
+ private:
714
+ /** rec_equal: Checks recursively whether or not two simplex trees are equal, using depth first search. */
715
+ template<class OtherSiblings>
716
+ bool rec_equal(Siblings const* s1, OtherSiblings const* s2) const {
717
+ if (s1->members().size() != s2->members().size())
718
+ return false;
719
+ auto sh2 = s2->members().begin();
720
+ for (auto sh1 = s1->members().begin();
721
+ (sh1 != s1->members().end() && sh2 != s2->members().end());
722
+ ++sh1, ++sh2) {
723
+ if (sh1->first != sh2->first || !(sh1->second.filtration() == sh2->second.filtration()))
724
+ return false;
725
+ if (has_children(sh1) != has_children(sh2))
726
+ return false;
727
+ // Recursivity on children only if both have children
728
+ else if (has_children(sh1))
729
+ if (!rec_equal(sh1->second.children(), sh2->second.children()))
730
+ return false;
731
+ }
732
+ return true;
733
+ }
734
+
735
+ /** \brief Returns the filtration value of a simplex.
736
+ *
737
+ * Same as `filtration()`, but does not handle `null_simplex()`.
738
+ */
739
+ static const Filtration_value& filtration_(Simplex_handle sh) {
740
+ GUDHI_CHECK (sh != null_simplex(), "null simplex");
741
+ return sh->second.filtration();
742
+ }
743
+
744
+ // Transform a Dictionary_const_it into a Dictionary_it
745
+ Dictionary_it _to_node_it(Simplex_handle sh) {
746
+ return self_siblings(sh)->to_non_const_it(sh);
747
+ }
748
+
749
+ public:
750
+ /** \brief Returns the key associated to a simplex.
751
+ *
752
+ * If no key has been assigned, returns `null_key()`.
753
+ * \pre SimplexTreeOptions::store_key
754
+ */
755
+ static Simplex_key key(Simplex_handle sh) {
756
+ return sh->second.key();
757
+ }
758
+
759
+ /** \brief Returns the simplex that has index idx in the filtration.
760
+ *
761
+ * The filtration must be initialized.
762
+ *
763
+ * @note Not thread safe
764
+ */
765
+ Simplex_handle simplex(Simplex_key idx) const {
766
+ return filtration_vect_[idx];
767
+ }
768
+
769
+ /** \brief Returns the filtration value of a simplex.
770
+ *
771
+ * Called on the null_simplex, it returns infinity.
772
+ * If SimplexTreeOptions::store_filtration is false, returns 0.
773
+ */
774
+ static const Filtration_value& filtration(Simplex_handle sh) {
775
+ if (sh != null_simplex()) {
776
+ return sh->second.filtration();
777
+ } else {
778
+ return Filtration_simplex_base_real::get_infinity();
779
+ }
780
+ }
781
+
782
+ /**
783
+ * @brief Returns a non-const reference to the filtration value of a simplex.
784
+ *
785
+ * @pre Only available if @ref SimplexTreeOptions::store_filtration is true.
786
+ *
787
+ * @exception std::invalid_argument if sh is a @ref null_simplex.
788
+ */
789
+ Filtration_value& get_filtration_value(Simplex_handle sh) {
790
+ static_assert(Options::store_filtration,
791
+ "A modifiable reference to a filtration value cannot be returned if no filtration value is stored.");
792
+
793
+ if (sh == null_simplex()) {
794
+ throw std::invalid_argument("Cannot bind reference to filtration value of a null simplex.");
795
+ }
796
+ return _to_node_it(sh)->second.filtration();
797
+ }
798
+
799
+ /**
800
+ * @brief Returns a const reference to the filtration value of a simplex.
801
+ *
802
+ * @pre Only available if @ref SimplexTreeOptions::store_filtration is true. If false is necessary,
803
+ * use @ref filtration instead.
804
+ *
805
+ * @note It is a more strict variant of @ref filtration, as it will not compile if
806
+ * @ref SimplexTreeOptions::store_filtration is false and it will throw if the given simplex is a @ref null_simplex.
807
+ *
808
+ * @exception std::invalid_argument if sh is a @ref null_simplex. Note that @ref filtration returns
809
+ * infinity instead of throwing in this case.
810
+ */
811
+ const Filtration_value& get_filtration_value(Simplex_handle sh) const {
812
+ static_assert(
813
+ Options::store_filtration,
814
+ "Filtration values are not stored. If you still want something to be returned, use `filtration(sh)` instead.");
815
+
816
+ if (sh == null_simplex()) {
817
+ throw std::invalid_argument(
818
+ "Null simplices are not associated to filtration values. If you still want something to be returned, use "
819
+ "`filtration(sh)` instead.");
820
+ }
821
+ return sh->second.filtration();
822
+ }
823
+
824
+ /** \brief Sets the filtration value of a simplex.
825
+ * \exception std::invalid_argument In debug mode, if sh is a null_simplex.
826
+ */
827
+ void assign_filtration(Simplex_handle sh, const Filtration_value& fv) {
828
+ GUDHI_CHECK(sh != null_simplex(),
829
+ std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex"));
830
+ _to_node_it(sh)->second.assign_filtration(fv);
831
+ }
832
+
833
+ /** \brief Returns a Simplex_handle different from all Simplex_handles
834
+ * associated to the simplices in the simplicial complex.
835
+ *
836
+ * One can call filtration(null_simplex()). */
837
+ static Simplex_handle null_simplex() {
838
+ return Dictionary_const_it();
839
+ }
840
+
841
+ /** \brief Returns a fixed number not in the interval [0, `num_simplices()`). */
842
+ static Simplex_key null_key() {
843
+ return -1;
844
+ }
845
+
846
+ /** \brief Returns the extra data stored in a simplex. */
847
+ Simplex_data& simplex_data(Simplex_handle sh) {
848
+ GUDHI_CHECK(sh != null_simplex(),
849
+ std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
850
+ return _to_node_it(sh)->second.data();
851
+ }
852
+
853
+ /** \brief Returns the extra data stored in a simplex. */
854
+ const Simplex_data& simplex_data(Simplex_handle sh) const {
855
+ GUDHI_CHECK(sh != null_simplex(),
856
+ std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
857
+ return sh->second.data();
858
+ }
859
+
860
+ /** \brief Returns a Vertex_handle different from all Vertex_handles associated
861
+ * to the vertices of the simplicial complex. */
862
+ Vertex_handle null_vertex() const {
863
+ return null_vertex_;
864
+ }
865
+
866
+ /** \brief Returns the number of vertices in the complex. */
867
+ size_t num_vertices() const {
868
+ return root_.members_.size();
869
+ }
870
+
871
+ /** \brief Returns whether the complex is empty. */
872
+ bool is_empty() const {
873
+ return root_.members_.empty();
874
+ }
875
+
876
+ public:
877
+ /** \brief Returns the number of simplices in the simplex_tree.
878
+ *
879
+ * This function takes time linear in the number of simplices. */
880
+ size_t num_simplices() const {
881
+ return num_simplices(root());
882
+ }
883
+
884
+ private:
885
+ /** \brief Returns the number of simplices in the simplex_tree. */
886
+ size_t num_simplices(const Siblings * sib) const {
887
+ auto sib_begin = sib->members().begin();
888
+ auto sib_end = sib->members().end();
889
+ size_t simplices_number = sib->members().size();
890
+ for (auto sh = sib_begin; sh != sib_end; ++sh) {
891
+ if (has_children(sh)) {
892
+ simplices_number += num_simplices(sh->second.children());
893
+ }
894
+ }
895
+ return simplices_number;
896
+ }
897
+
898
+ /**
899
+ * @brief Returns the dimension of the given sibling simplices.
900
+ *
901
+ * @param curr_sib Pointer to the sibling container.
902
+ * @return Height of the siblings in the tree (root counts as zero to make the height correspond to the dimension).
903
+ */
904
+ int dimension(Siblings const* curr_sib) const {
905
+ int dim = -1;
906
+ while (curr_sib != nullptr) {
907
+ ++dim;
908
+ curr_sib = curr_sib->oncles();
909
+ }
910
+ return dim;
911
+ }
912
+
913
+ public:
914
+ /** \brief Returns the number of simplices of each dimension in the simplex tree. */
915
+ std::vector<size_t> num_simplices_by_dimension() const {
916
+ if (is_empty()) return {};
917
+ // std::min in case the upper bound got crazy
918
+ std::vector<size_t> res(std::min(upper_bound_dimension()+1, max_dimension()+1));
919
+ auto fun = [&res](Simplex_handle, int dim) -> void { ++res[dim]; };
920
+ for_each_simplex(fun);
921
+ if (dimension_to_be_lowered_) {
922
+ GUDHI_CHECK(res.front() != 0, std::logic_error("Bug in Gudhi: non-empty complex has no vertex"));
923
+ while (res.back() == 0) res.pop_back();
924
+ dimension_ = static_cast<int>(res.size()) - 1;
925
+ dimension_to_be_lowered_ = false;
926
+ } else {
927
+ GUDHI_CHECK(res.back() != 0,
928
+ std::logic_error("Bug in Gudhi: there is no simplex of dimension the dimension of the complex"));
929
+ }
930
+ return res;
931
+ }
932
+
933
+ /** \brief Returns the dimension of a simplex.
934
+ *
935
+ * Must be different from null_simplex().*/
936
+ int dimension(Simplex_handle sh) const {
937
+ return dimension(self_siblings(sh));
938
+ }
939
+
940
+ /** \brief Returns an upper bound on the dimension of the simplicial complex.
941
+ *
942
+ * @note Not thread safe
943
+ */
944
+ int upper_bound_dimension() const {
945
+ return dimension_;
946
+ }
947
+
948
+ /** \brief Returns the dimension of the simplicial complex.
949
+ * \details This function is not constant time because it can recompute dimension if required (can be triggered by
950
+ * `remove_maximal_simplex()` or `prune_above_filtration()`).
951
+ *
952
+ * @note Not thread safe
953
+ */
954
+ int dimension() const {
955
+ if (dimension_to_be_lowered_)
956
+ lower_upper_bound_dimension();
957
+ return dimension_;
958
+ }
959
+
960
+ /**
961
+ * @brief Returns the value stored as the number of parameters of the filtration values.
962
+ * The default value stored at construction of the simplex tree is 1. The user needs to set it by hand
963
+ * with @ref set_num_parameters if any other value is needed.
964
+ */
965
+ int num_parameters() const {
966
+ return number_of_parameters_;
967
+ }
968
+
969
+ /**
970
+ * @brief Stores the given value as number of parameters of the filtration values.
971
+ * At construction, the default number of parameters is set to 1.
972
+ *
973
+ * Note that there will not be any verification to ensure that the filtration values stored have this amount
974
+ * of parameters.
975
+ */
976
+ void set_num_parameters(int new_number) {
977
+ number_of_parameters_ = new_number;
978
+ }
979
+
980
+ /** \brief Returns true if the node in the simplex tree pointed by
981
+ * the given simplex handle has children.*/
982
+ template<class SimplexHandle>
983
+ bool has_children(SimplexHandle sh) const {
984
+ // Here we rely on the root using null_vertex(), which cannot match any real vertex.
985
+ return (sh->second.children()->parent() == sh->first);
986
+ }
987
+
988
+ private:
989
+ friend class Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
990
+
991
+ /** \brief Returns the children of the node in the simplex tree pointed by sh.
992
+ * \exception std::invalid_argument In debug mode, if sh has no child.
993
+ */
994
+ Siblings const* children(Simplex_handle sh) const {
995
+ GUDHI_CHECK(has_children(sh), std::invalid_argument("Simplex_tree::children - argument has no child"));
996
+ return sh->second.children();
997
+ }
998
+
999
+ public:
1000
+ /** \brief Given a range of Vertex_handles, returns the Simplex_handle
1001
+ * of the simplex in the simplicial complex containing the corresponding
1002
+ * vertices. Return null_simplex() if the simplex is not in the complex.
1003
+ *
1004
+ * The type InputVertexRange must be a range of <CODE>Vertex_handle</CODE>
1005
+ * on which we can call std::begin() function
1006
+ */
1007
+ template<class InputVertexRange = std::initializer_list<Vertex_handle>>
1008
+ Simplex_handle find(const InputVertexRange & s) const {
1009
+ auto first = std::begin(s);
1010
+ auto last = std::end(s);
1011
+
1012
+ if (first == last)
1013
+ return null_simplex(); // ----->>
1014
+
1015
+ // Copy before sorting
1016
+ std::vector<Vertex_handle> copy(first, last);
1017
+ std::sort(std::begin(copy), std::end(copy));
1018
+ return find_simplex(copy);
1019
+ }
1020
+
1021
+ private:
1022
+ /** Find function, with a sorted range of vertices. */
1023
+ Simplex_handle find_simplex(const std::vector<Vertex_handle> & simplex) const {
1024
+ Siblings const* tmp_sib = &root_;
1025
+ Dictionary_const_it tmp_dit;
1026
+ auto vi = simplex.begin();
1027
+ if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
1028
+ // Equivalent to the first iteration of the normal loop
1029
+ GUDHI_CHECK(contiguous_vertices(), "non-contiguous vertices");
1030
+ Vertex_handle v = *vi++;
1031
+ if(v < 0 || v >= static_cast<Vertex_handle>(root_.members_.size()))
1032
+ return null_simplex();
1033
+ tmp_dit = root_.members_.begin() + v;
1034
+ if (vi == simplex.end())
1035
+ return tmp_dit;
1036
+ if (!has_children(tmp_dit))
1037
+ return null_simplex();
1038
+ tmp_sib = tmp_dit->second.children();
1039
+ }
1040
+ for (;;) {
1041
+ tmp_dit = tmp_sib->members_.find(*vi++);
1042
+ if (tmp_dit == tmp_sib->members_.end())
1043
+ return null_simplex();
1044
+ if (vi == simplex.end())
1045
+ return tmp_dit;
1046
+ if (!has_children(tmp_dit))
1047
+ return null_simplex();
1048
+ tmp_sib = tmp_dit->second.children();
1049
+ }
1050
+ }
1051
+
1052
+ /** \brief Returns the Simplex_handle corresponding to the 0-simplex
1053
+ * representing the vertex with Vertex_handle v. */
1054
+ Simplex_handle find_vertex(Vertex_handle v) const {
1055
+ if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
1056
+ assert(contiguous_vertices());
1057
+ return root_.members_.begin() + v;
1058
+ } else {
1059
+ return root_.members_.find(v);
1060
+ }
1061
+ }
1062
+
1063
+ public:
1064
+ /** \private \brief Test if the vertices have contiguous numbering: 0, 1, etc. */
1065
+ bool contiguous_vertices() const {
1066
+ if (root_.members_.empty()) return true;
1067
+ if (root_.members_.begin()->first != 0) return false;
1068
+ if (std::prev(root_.members_.end())->first != static_cast<Vertex_handle>(root_.members_.size() - 1)) return false;
1069
+ return true;
1070
+ }
1071
+
1072
+ private:
1073
+ /**
1074
+ * @brief Inserts a Node in the set of siblings nodes. Calls `update_simplex_tree_after_node_insertion`
1075
+ * if the insertion succeeded.
1076
+ *
1077
+ * @tparam update_fil If true, as well as Options::store_filtration, and the node is already present, assigns
1078
+ * the "union" of the input filtration_value and the value already present in the node as filtration value.
1079
+ * @tparam update_children If true and the node has no children, create a child with sib as uncle.
1080
+ * @tparam set_to_null If true and the node is already present, sets the returned simplex handle to null if the
1081
+ * filtration value of the simplex was not modified.
1082
+ * @tparam Filt Filtration value type.
1083
+ * @param sib Sibling in where the node has to be inserted.
1084
+ * @param v Label of the node.
1085
+ * @param filtration_value Filtration value stored in the node.
1086
+ * @return Pair of the iterator to the new node and a boolean indicating if the insertion succeeded.
1087
+ */
1088
+ template<bool update_fil, bool update_children, bool set_to_null, class Filt>
1089
+ std::pair<Dictionary_it, bool> insert_node_(Siblings *sib, Vertex_handle v, Filt&& filtration_value) {
1090
+ std::pair<Dictionary_it, bool> ins = sib->members_.try_emplace(v, sib, std::forward<Filt>(filtration_value));
1091
+
1092
+ if constexpr (update_children){
1093
+ if (!(has_children(ins.first))) {
1094
+ ins.first->second.assign_children(new Siblings(sib, v));
1095
+ }
1096
+ }
1097
+
1098
+ if (ins.second){
1099
+ // Only required when insertion is successful
1100
+ update_simplex_tree_after_node_insertion(ins.first);
1101
+ return ins;
1102
+ }
1103
+
1104
+ if constexpr (Options::store_filtration && update_fil){
1105
+ if (unify_lifetimes(ins.first->second.filtration(), filtration_value)) return ins;
1106
+ }
1107
+
1108
+ if constexpr (set_to_null){
1109
+ ins.first = Dictionary_it();
1110
+ }
1111
+
1112
+ return ins;
1113
+ }
1114
+
1115
+ protected:
1116
+ /** \brief Inserts a simplex represented by a range of vertex.
1117
+ * @param[in] simplex range of Vertex_handles, representing the vertices of the new simplex. The range must be
1118
+ * sorted by increasing vertex handle order, and not empty.
1119
+ * @param[in] filtration the filtration value assigned to the new simplex.
1120
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
1121
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
1122
+ * to the new simplex.
1123
+ * If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
1124
+ * fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
1125
+ * we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
1126
+ * output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
1127
+ * null_simplex.
1128
+ *
1129
+ */
1130
+ template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>>
1131
+ std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex,
1132
+ const Filtration_value& filtration) {
1133
+ Siblings * curr_sib = &root_;
1134
+ std::pair<Dictionary_it, bool> res_insert;
1135
+ auto vi = simplex.begin();
1136
+
1137
+ for (; vi != std::prev(simplex.end()); ++vi) {
1138
+ GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
1139
+ res_insert = insert_node_<false, true, false>(curr_sib, *vi, filtration);
1140
+ curr_sib = res_insert.first->second.children();
1141
+ }
1142
+
1143
+ GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
1144
+ res_insert = insert_node_<true, false, true>(curr_sib, *vi, filtration);
1145
+ if (res_insert.second){
1146
+ int dim = static_cast<int>(boost::size(simplex)) - 1;
1147
+ if (dim > dimension_) {
1148
+ // Update dimension if needed
1149
+ dimension_ = dim;
1150
+ }
1151
+ }
1152
+ return res_insert;
1153
+ }
1154
+
1155
+ public:
1156
+ /** \brief Insert a simplex, represented by a range of Vertex_handles, in the simplicial complex.
1157
+ *
1158
+ * @param[in] simplex range of Vertex_handles, representing the vertices of the new simplex
1159
+ * @param[in] filtration the filtration value assigned to the new simplex.
1160
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
1161
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
1162
+ * to the new simplex.
1163
+ * If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
1164
+ * fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
1165
+ * we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
1166
+ * output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
1167
+ * null_simplex.
1168
+ *
1169
+ * All subsimplices do not necessary need to be already in the simplex tree to proceed to an
1170
+ * insertion. However, the property of being a simplicial complex will be violated. This allows
1171
+ * us to insert a stream of simplices contained in a simplicial complex without considering any
1172
+ * order on them.
1173
+ *
1174
+ * The filtration value
1175
+ * assigned to the new simplex must preserve the monotonicity of the filtration.
1176
+ *
1177
+ * The type InputVertexRange must be a range for which .begin() and
1178
+ * .end() return input iterators, with 'value_type' Vertex_handle. */
1179
+ template<class InputVertexRange = std::initializer_list<Vertex_handle>>
1180
+ std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange & simplex,
1181
+ const Filtration_value& filtration = Filtration_value()) {
1182
+ auto first = std::begin(simplex);
1183
+ auto last = std::end(simplex);
1184
+
1185
+ if (first == last)
1186
+ return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
1187
+
1188
+ // Copy before sorting
1189
+ std::vector<Vertex_handle> copy(first, last);
1190
+ std::sort(std::begin(copy), std::end(copy));
1191
+ return insert_simplex_raw(copy, filtration);
1192
+ }
1193
+
1194
+ /**
1195
+ * @brief List of insertion strategies for @ref insert_simplex_and_subfaces, which takes a simplex \f$ \sigma \f$
1196
+ * and a filtration value \f$ f \f$ as argument.
1197
+ */
1198
+ enum class Insertion_strategy : std::uint8_t {
1199
+ /**
1200
+ * @brief Let \f$ f \f$ be the filtration value given as argument. Inserts the simplex \f$ \sigma \f$ as follows:
1201
+ * - to \f$ f \f$, if \f$ \sigma \f$ didn't existed yet,
1202
+ * - if \f$ \sigma \f$ was already inserted, let \f$ f' \f$ be its filtration value. The new filtration value will
1203
+ * be @ref FiltrationValue::unify_lifetimes (\f$ f \f$, \f$ f' \f$).
1204
+ *
1205
+ * Then, the filtration values of all faces of \f$ \sigma \f$ are pushed to the union of their old value and of
1206
+ * the new value of \f$ \sigma \f$.
1207
+ */
1208
+ LOWEST,
1209
+ /**
1210
+ * @brief Let \f$ f \f$ be the filtration value given as argument. Inserts the simplex \f$ \sigma \f$ as follows:
1211
+ * - if \f$ \sigma \f$ was not inserted yet, then \f$ \sigma \f$ and all its faces, which are not already included
1212
+ * in the complex, are inserted at either \f$ f \f$ or at the first possible filtration value when \f$ f \f$ is too
1213
+ * low (to insure the validity of the filtration). This is computed with @ref FiltrationValue::intersect_lifetimes.
1214
+ * - if \f$ \sigma \f$ existed already, then nothing is done.
1215
+ */
1216
+ HIGHEST,
1217
+ /**
1218
+ * @brief Ignores the filtration value given as argument and inserts the simplex and all its faces as soon as
1219
+ * the already inserted faces allows it. That is, the filtration value of a not already inserted face will be
1220
+ * the "intersection" of the filtration values of all it facets, computed with
1221
+ * @ref FiltrationValue::intersect_lifetimes "". If none of the faces were already inserted, everything will simply
1222
+ * be placed at minus infinity. Therefore, this strategy is equivalent to @ref Insertion_strategy::HIGHEST with
1223
+ * the given filtration value as minus infinity.
1224
+ */
1225
+ FIRST_POSSIBLE,
1226
+ /**
1227
+ * @brief If the simplex to insert:
1228
+ * - already exists in the simplex tree, its filtration value is replaced by the new given one,
1229
+ * - does not exists yet, it and all its non-existing faces are inserted at the given filtration value,
1230
+ * none of the already inserted faces are touched.
1231
+ *
1232
+ * This option is mainly usefull when the methods @ref FiltrationValue::intersect_lifetimes or
1233
+ * @ref FiltrationValue::unify_lifetimes are heavy for the associated filtration value class and the user
1234
+ * wants to avoid calling them just to ensure a valid filtration. It is therefore **responsibility of the user to
1235
+ * ensure a valid filtration** at the end of the construction, before any filtration related method is used
1236
+ * (@ref filtration_simplex_range for example).
1237
+ */
1238
+ FORCE
1239
+ };
1240
+
1241
+ /**
1242
+ * @brief Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of
1243
+ * Vertex_handles, in the simplicial complex.
1244
+ *
1245
+ * @tparam InputVertexRange Range of @ref Vertex_handle.
1246
+ * @param[in] Nsimplex Vertices of the new N-simplex.
1247
+ * @param[in] filtration Ignored if @ref SimplexTreeOptions::store_filtration is false.
1248
+ * Otherwise, see `insertion_strategy` below. Default value: default constructor.
1249
+ * @param insertion_strategy Ignored if @ref SimplexTreeOptions::store_filtration is false.
1250
+ * Indicates where to insert the simplex and its faces in the filtration with respect to the value given by
1251
+ * `filtration`. See @ref Insertion_strategy for more details. Default value: @ref Insertion_strategy::LOWEST.
1252
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
1253
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
1254
+ * to the new simplex.
1255
+ * If the insertion fails (the simplex is already there), the bool is set to false. In that case, the Simplex_handle
1256
+ * part is set to:
1257
+ * - null_simplex, if the filtration value of the simplex is not modified,
1258
+ * - the simplex handle assigned to the simplex, if the filtration value of the simplex is modified.
1259
+ */
1260
+ template <class InputVertexRange = std::initializer_list<Vertex_handle>>
1261
+ std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(
1262
+ const InputVertexRange& Nsimplex,
1263
+ const Filtration_value& filtration = Filtration_value(),
1264
+ Insertion_strategy insertion_strategy = Insertion_strategy::LOWEST)
1265
+ {
1266
+ auto first = std::begin(Nsimplex);
1267
+ auto last = std::end(Nsimplex);
1268
+
1269
+ if (first == last) return {null_simplex(), true}; // FIXME: false would make more sense to me.
1270
+
1271
+ thread_local std::vector<Vertex_handle> copy;
1272
+ copy.clear();
1273
+ copy.insert(copy.end(), first, last);
1274
+ std::sort(copy.begin(), copy.end());
1275
+ auto last_unique = std::unique(copy.begin(), copy.end());
1276
+ copy.erase(last_unique, copy.end());
1277
+ GUDHI_CHECK_code(for (Vertex_handle v : copy)
1278
+ GUDHI_CHECK(v != null_vertex(), "cannot use the dummy null_vertex() as a real vertex"););
1279
+ // Update dimension if needed. We could wait to see if the insertion succeeds, but I doubt there is much to gain.
1280
+ dimension_ = (std::max)(dimension_, static_cast<int>(std::distance(copy.begin(), copy.end())) - 1);
1281
+
1282
+ if constexpr (Options::store_filtration){
1283
+ switch (insertion_strategy) {
1284
+ case Insertion_strategy::LOWEST:
1285
+ return _rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
1286
+ case Insertion_strategy::HIGHEST:
1287
+ return _insert_simplex_and_subfaces_at_highest(root(), copy.begin(), copy.end(), filtration);
1288
+ case Insertion_strategy::FIRST_POSSIBLE:
1289
+ return _insert_simplex_and_subfaces_at_highest(
1290
+ root(), copy.begin(), copy.end(), Filtration_simplex_base_real::get_minus_infinity());
1291
+ case Insertion_strategy::FORCE:
1292
+ return _insert_simplex_and_subfaces_forcing_filtration_value(root(), copy.begin(), copy.end(), filtration);
1293
+ default:
1294
+ throw std::invalid_argument("Given insertion strategy is not available.");
1295
+ }
1296
+ } else {
1297
+ return _rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
1298
+ }
1299
+ }
1300
+
1301
+ private:
1302
+ // To insert {1,2,3,4}, we insert {2,3,4} twice, once at the root, and once below 1.
1303
+ template <class ForwardVertexIterator, bool update_fil = true>
1304
+ std::pair<Simplex_handle, bool> _rec_insert_simplex_and_subfaces_sorted(Siblings* sib,
1305
+ ForwardVertexIterator first,
1306
+ ForwardVertexIterator last,
1307
+ const Filtration_value& filt) {
1308
+ // An alternative strategy would be:
1309
+ // - try to find the complete simplex, if found (and low filtration) exit
1310
+ // - insert all the vertices at once in sib
1311
+ // - loop over those (new or not) simplices, with a recursive call(++first, last)
1312
+ Vertex_handle vertex_one = *first;
1313
+
1314
+ // insert_node_<bool update_fil, bool update_children, bool set_to_null>(sib, ...)
1315
+ // update_fil: if true, calls `unify_lifetimes` on the new and old filtration value of the node
1316
+ // update_children: if true, assign a child to the node if he didn't had one
1317
+ // set_to_null: if true, sets returned iterator to null simplex
1318
+
1319
+ if (++first == last) return insert_node_<update_fil, false, update_fil>(sib, vertex_one, filt);
1320
+
1321
+ // TODO: have special code here, we know we are building the whole subtree from scratch.
1322
+ auto insertion_result = insert_node_<update_fil, true, false>(sib, vertex_one, filt);
1323
+
1324
+ auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, update_fil>(
1325
+ insertion_result.first->second.children(), first, last, filt);
1326
+ // No need to continue if the full simplex was already there with a low enough filtration value.
1327
+ if (res.first != null_simplex())
1328
+ _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, update_fil>(sib, first, last, filt);
1329
+ return res;
1330
+ }
1331
+
1332
+ void _make_subfiltration_non_decreasing(Simplex_handle sh) {
1333
+ Filtration_value& f = get_filtration_value(sh);
1334
+ for (auto sh_b : boundary_simplex_range(sh)) {
1335
+ _make_subfiltration_non_decreasing(sh_b);
1336
+ intersect_lifetimes(f, filtration(sh_b));
1337
+ }
1338
+ }
1339
+
1340
+ template <class ForwardVertexIterator>
1341
+ std::pair<Simplex_handle, bool> _insert_simplex_and_subfaces_at_highest(Siblings* sib,
1342
+ ForwardVertexIterator first,
1343
+ ForwardVertexIterator last,
1344
+ const Filtration_value& filt) {
1345
+ auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, false>(sib, first, last, filt);
1346
+ if (res.second) {
1347
+ _make_subfiltration_non_decreasing(res.first);
1348
+ } else {
1349
+ res.first = null_simplex();
1350
+ }
1351
+ return res;
1352
+ }
1353
+
1354
+ template <class ForwardVertexIterator>
1355
+ std::pair<Simplex_handle, bool> _insert_simplex_and_subfaces_forcing_filtration_value(Siblings* sib,
1356
+ ForwardVertexIterator first,
1357
+ ForwardVertexIterator last,
1358
+ const Filtration_value& filt) {
1359
+ auto res = _rec_insert_simplex_and_subfaces_sorted<ForwardVertexIterator, false>(sib, first, last, filt);
1360
+ if (!res.second) {
1361
+ Filtration_value& f = _to_node_it(res.first)->second.filtration();
1362
+ // could handle the case were f == filt to set res.first to null_simplex, but I don't think it the point of
1363
+ // this strategy and it seems better to avoid `operator==` which is also not trivial for vectors etc.
1364
+ f = filt;
1365
+ }
1366
+ return res;
1367
+ }
1368
+
1369
+ public:
1370
+ /** \brief Assign a value 'key' to the key of the simplex
1371
+ * represented by the Simplex_handle 'sh'. */
1372
+ void assign_key(Simplex_handle sh, Simplex_key key) {
1373
+ _to_node_it(sh)->second.assign_key(key);
1374
+ }
1375
+
1376
+ /** Returns the two Simplex_handle corresponding to the endpoints of
1377
+ * and edge. sh must point to a 1-dimensional simplex. This is an
1378
+ * optimized version of the boundary computation. */
1379
+ std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) const {
1380
+ assert(dimension(sh) == 1);
1381
+ return { find_vertex(sh->first), find_vertex(self_siblings(sh)->parent()) };
1382
+ }
1383
+
1384
+ /** Returns the Siblings containing a simplex.*/
1385
+ template<class SimplexHandle>
1386
+ Siblings const* self_siblings(SimplexHandle sh) const {
1387
+ if (sh->second.children()->parent() == sh->first)
1388
+ return sh->second.children()->oncles();
1389
+ else
1390
+ return sh->second.children();
1391
+ }
1392
+
1393
+ /** Returns the Siblings containing a simplex.*/
1394
+ template<class SimplexHandle>
1395
+ Siblings* self_siblings(SimplexHandle sh) {
1396
+ // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
1397
+ // Doing it the other way is not safe
1398
+ return const_cast<Siblings*>(std::as_const(*this).self_siblings(sh));
1399
+ }
1400
+
1401
+ public:
1402
+ /** Returns a pointer to the root nodes of the simplex tree. */
1403
+ Siblings* root() { return &root_; }
1404
+
1405
+ /** Returns a pointer to the root nodes of the simplex tree. */
1406
+ const Siblings * root() const {
1407
+ return &root_;
1408
+ }
1409
+
1410
+ /** \brief Set a dimension for the simplicial complex.
1411
+ * \details
1412
+ * If `exact` is false, `dimension` is only an upper bound on the dimension of the complex.
1413
+ * This function must be used with caution because it disables or limits the on-demand recomputation of the dimension
1414
+ * (the need for recomputation can be caused by `remove_maximal_simplex()` or `prune_above_filtration()`).
1415
+ */
1416
+ void set_dimension(int dimension, bool exact=true) {
1417
+ dimension_to_be_lowered_ = !exact;
1418
+ dimension_ = dimension;
1419
+ }
1420
+
1421
+ private:
1422
+ /** \brief Returns true iff the list of vertices of sh1
1423
+ * is smaller than the list of vertices of sh2 w.r.t.
1424
+ * lexicographic order on the lists read in reverse.
1425
+ *
1426
+ * It defines a StrictWeakOrdering on simplices. The Simplex_vertex_iterators
1427
+ * must traverse the Vertex_handle in decreasing order. Reverse lexicographic order satisfy
1428
+ * the property that a subsimplex of a simplex is always strictly smaller with this order. */
1429
+ bool reverse_lexicographic_order(Simplex_handle sh1, Simplex_handle sh2) const {
1430
+ Simplex_vertex_range rg1 = simplex_vertex_range(sh1);
1431
+ Simplex_vertex_range rg2 = simplex_vertex_range(sh2);
1432
+ Simplex_vertex_iterator it1 = rg1.begin();
1433
+ Simplex_vertex_iterator it2 = rg2.begin();
1434
+ while (it1 != rg1.end() && it2 != rg2.end()) {
1435
+ if (*it1 == *it2) {
1436
+ ++it1;
1437
+ ++it2;
1438
+ } else {
1439
+ return *it1 < *it2;
1440
+ }
1441
+ }
1442
+ return ((it1 == rg1.end()) && (it2 != rg2.end()));
1443
+ }
1444
+
1445
+ /** \brief StrictWeakOrdering, for the simplices, defined by the filtration.
1446
+ *
1447
+ * It corresponds to the partial order
1448
+ * induced by the filtration values, with ties resolved using reverse lexicographic order.
1449
+ * Reverse lexicographic order has the property to always consider the subsimplex of a simplex
1450
+ * to be smaller. The filtration function must be monotonic. */
1451
+ struct is_before_in_totally_ordered_filtration {
1452
+ explicit is_before_in_totally_ordered_filtration(Simplex_tree const* st)
1453
+ : st_(st) { }
1454
+
1455
+ bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const {
1456
+ // Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
1457
+ if (!(sh1->second.filtration() == sh2->second.filtration())) {
1458
+ return sh1->second.filtration() < sh2->second.filtration();
1459
+ }
1460
+ // is sh1 a proper subface of sh2
1461
+ return st_->reverse_lexicographic_order(sh1, sh2);
1462
+ }
1463
+
1464
+ Simplex_tree const* st_;
1465
+ };
1466
+
1467
+ public:
1468
+ /** \brief Initializes the filtration cache, i.e. sorts the
1469
+ * simplices according to their order in the filtration.
1470
+ * Assumes that the filtration values have a total order.
1471
+ * If not, please use @ref initialize_filtration(Comparator&&, Ignorer&&) const instead.
1472
+ * Two simplices with same filtration value are ordered by a reverse lexicographic order.
1473
+ *
1474
+ * It always recomputes the cache, even if one already exists.
1475
+ *
1476
+ * Any insertion, deletion or change of filtration value invalidates this cache,
1477
+ * which can be cleared with clear_filtration().
1478
+ *
1479
+ * @note Not thread safe
1480
+ */
1481
+ void initialize_filtration(bool ignore_infinite_values = false) const {
1482
+ if (ignore_infinite_values){
1483
+ initialize_filtration(is_before_in_totally_ordered_filtration(this), [&](Simplex_handle sh) -> bool {
1484
+ return is_positive_infinity(filtration(sh));
1485
+ });
1486
+ } else {
1487
+ initialize_filtration(is_before_in_totally_ordered_filtration(this), [](Simplex_handle) -> bool {
1488
+ return false;
1489
+ });
1490
+ }
1491
+ }
1492
+
1493
+ /**
1494
+ * @brief Initializes the filtration cache, i.e. sorts the simplices according to the specified order.
1495
+ * The given order has to totally order all simplices in the simplex tree such that the resulting order is a valid
1496
+ * 1-parameter filtration.
1497
+ * For example, if the stored filtration is a bi-filtration (or 2-parameter filtration), the given method can
1498
+ * indicate, that the simplices have to be ordered by the first parameter and in case of equality by reverse
1499
+ * lexicographic order. This would generate a valid 1-parameter filtration which can than be interpreted as a line
1500
+ * in the 2-parameter module and used with @ref filtration_simplex_range "". Also, the persistence along this line
1501
+ * can than be computed with @ref Gudhi::persistent_cohomology::Persistent_cohomology "Persistent_cohomology".
1502
+ * Just note that it is important to call @ref initialize_filtration(Comparator&&, Ignorer&&) const explicitly before
1503
+ * call @ref Gudhi::persistent_cohomology::Persistent_cohomology::compute_persistent_cohomology
1504
+ * "Persistent_cohomology::compute_persistent_cohomology" in that case, otherwise, the computation of persistence
1505
+ * will fail (if no call to @ref initialize_filtration() or @ref initialize_filtration(Comparator&&, Ignorer&&) const
1506
+ * was made, it will call it it-self with no arguments and the simplices will be wrongly sorted).
1507
+ *
1508
+ * It always recomputes the cache, even if one already exists.
1509
+ *
1510
+ * Any insertion, deletion or change of filtration value invalidates this cache,
1511
+ * which can be cleared with @ref clear_filtration().
1512
+ *
1513
+ * @tparam Comparator Method type taking two Simplex_handle as input and returns a bool.
1514
+ * @tparam Ignorer Method type taking one Simplex_handle as input and returns a bool.
1515
+ * @param is_before_in_filtration Method used to compare two simplices with respect to their position in the
1516
+ * wanted filtration. Takes two Simplex_handle as input and returns true if and only if the first simplex appears
1517
+ * strictly before the second simplex in the resulting 1-parameter filtration.
1518
+ * @param ignore_simplex Method taking a simplex handle as input and returns true if and only if the corresponding
1519
+ * simplex should be ignored in the filtration and therefore not be cached. But note that it is important that
1520
+ * the resulting filtration is still a valid filtration, that is all proper faces of a not ignored simplex
1521
+ * should also not be ignored and have to appear before this simplex. There will be no tests to ensure that, so
1522
+ * it is of the users responsibility.
1523
+ *
1524
+ * @note Not thread safe
1525
+ */
1526
+ template<typename Comparator, typename Ignorer>
1527
+ void initialize_filtration(Comparator&& is_before_in_filtration, Ignorer&& ignore_simplex) const {
1528
+ filtration_vect_.clear();
1529
+ filtration_vect_.reserve(num_simplices());
1530
+ for (Simplex_handle sh : complex_simplex_range()) {
1531
+ if (ignore_simplex(sh)) continue;
1532
+ filtration_vect_.push_back(sh);
1533
+ }
1534
+
1535
+ #ifdef GUDHI_USE_TBB
1536
+ tbb::parallel_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
1537
+ #else
1538
+ std::stable_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
1539
+ #endif
1540
+ }
1541
+
1542
+ /** \brief Initializes the filtration cache if it isn't initialized yet.
1543
+ *
1544
+ * Automatically called by filtration_simplex_range().
1545
+ *
1546
+ * @note Not thread safe
1547
+ */
1548
+ void maybe_initialize_filtration() const {
1549
+ if (filtration_vect_.empty()) {
1550
+ initialize_filtration();
1551
+ }
1552
+ }
1553
+ /** \brief Clears the filtration cache produced by initialize_filtration().
1554
+ *
1555
+ * Useful when initialize_filtration() has already been called and we perform an operation
1556
+ * (say an insertion) that invalidates the cache.
1557
+ *
1558
+ * @note Not thread safe
1559
+ */
1560
+ void clear_filtration() const {
1561
+ filtration_vect_.clear();
1562
+ }
1563
+
1564
+ private:
1565
+ /** Recursive search of cofaces
1566
+ * This function uses DFS
1567
+ *\param vertices contains a list of vertices, which represent the vertices of the simplex not found yet.
1568
+ *\param curr_sib pointer to the siblings to iterate over for this iteration of the recursion.
1569
+ *\param curr_nbVertices represents the number of vertices of the simplex we reached by going through the tree.
1570
+ *\param cofaces contains a list of Simplex_handle, representing all the cofaces asked.
1571
+ *\param star true if we need the star of the simplex
1572
+ *\param nbVertices number of vertices of the cofaces we search
1573
+ * Prefix actions : When the bottom vertex matches with the current vertex in the tree, we remove the bottom vertex from vertices.
1574
+ * Infix actions : Then we call or not the recursion.
1575
+ * Postfix actions : Finally, we add back the removed vertex into vertices, and remove this vertex from curr_nbVertices so that we didn't change the parameters.
1576
+ * If the vertices list is empty, we need to check if curr_nbVertices matches with the dimension of the cofaces asked.
1577
+ */
1578
+ void rec_coface(std::vector<Vertex_handle> &vertices, Siblings const*curr_sib, int curr_nbVertices,
1579
+ std::vector<Simplex_handle>& cofaces, bool star, int nbVertices) const {
1580
+ if (!(star || curr_nbVertices <= nbVertices)) // dimension of actual simplex <= nbVertices
1581
+ return;
1582
+ for (Simplex_handle simplex = curr_sib->members().begin(); simplex != curr_sib->members().end(); ++simplex) {
1583
+ if (vertices.empty()) {
1584
+ // If we reached the end of the vertices, and the simplex has more vertices than the given simplex
1585
+ // => we found a coface
1586
+
1587
+ // Add a coface if we want the star or if the number of vertices of the current simplex matches with nbVertices
1588
+ bool addCoface = (star || curr_nbVertices == nbVertices);
1589
+ if (addCoface)
1590
+ cofaces.push_back(simplex);
1591
+ if ((!addCoface || star) && has_children(simplex)) // Rec call
1592
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1593
+ } else {
1594
+ if (simplex->first == vertices.back()) {
1595
+ // If curr_sib matches with the top vertex
1596
+ bool equalDim = (star || curr_nbVertices == nbVertices); // dimension of actual simplex == nbVertices
1597
+ bool addCoface = vertices.size() == 1 && equalDim;
1598
+ if (addCoface)
1599
+ cofaces.push_back(simplex);
1600
+ if ((!addCoface || star) && has_children(simplex)) {
1601
+ // Rec call
1602
+ Vertex_handle tmp = vertices.back();
1603
+ vertices.pop_back();
1604
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1605
+ vertices.push_back(tmp);
1606
+ }
1607
+ } else if (simplex->first > vertices.back()) {
1608
+ return;
1609
+ } else {
1610
+ // (simplex->first < vertices.back()
1611
+ if (has_children(simplex))
1612
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
1613
+ }
1614
+ }
1615
+ }
1616
+ }
1617
+
1618
+ public:
1619
+ /** \brief Compute the star of a n simplex
1620
+ * \param simplex represent the simplex of which we search the star
1621
+ * \return Vector of Simplex_tree::Simplex_handle (empty vector if no star found) when
1622
+ * SimplexTreeOptions::link_nodes_by_label is false.
1623
+ *
1624
+ * Simplex_tree::Simplex_handle range for an optimized search for the star of a simplex when
1625
+ * SimplexTreeOptions::link_nodes_by_label is true.
1626
+ */
1627
+ Cofaces_simplex_range star_simplex_range(const Simplex_handle simplex) const {
1628
+ return cofaces_simplex_range(simplex, 0);
1629
+ }
1630
+
1631
+ /** \brief Compute the cofaces of a n simplex
1632
+ * \param simplex represent the n-simplex of which we search the n+codimension cofaces
1633
+ * \param codimension The function returns the n+codimension-cofaces of the n-simplex. If codimension = 0, return all
1634
+ * cofaces (equivalent of star function)
1635
+ * \return Vector of Simplex_tree::Simplex_handle (empty vector if no cofaces found) when
1636
+ * SimplexTreeOptions::link_nodes_by_label is false.
1637
+ *
1638
+ * Simplex_tree::Simplex_handle range for an optimized search for the coface of a simplex when
1639
+ * SimplexTreeOptions::link_nodes_by_label is true.
1640
+ */
1641
+ Cofaces_simplex_range cofaces_simplex_range(const Simplex_handle simplex, int codimension) const{
1642
+ // codimension must be positive or null integer
1643
+ assert(codimension >= 0);
1644
+
1645
+ if constexpr (Options::link_nodes_by_label) {
1646
+ Simplex_vertex_range rg = simplex_vertex_range(simplex);
1647
+ Static_vertex_vector simp(rg.begin(), rg.end());
1648
+ // must be sorted in decreasing order
1649
+ assert(std::is_sorted(simp.begin(), simp.end(), std::greater<Vertex_handle>()));
1650
+ auto range = Optimized_star_simplex_range(Optimized_star_simplex_iterator(this, std::move(simp)),
1651
+ Optimized_star_simplex_iterator());
1652
+ // Lazy filtered range
1653
+ Fast_cofaces_predicate select(this, codimension, this->dimension(simplex));
1654
+ return boost::adaptors::filter(range, select);
1655
+ } else {
1656
+ Cofaces_simplex_range cofaces;
1657
+ Simplex_vertex_range rg = simplex_vertex_range(simplex);
1658
+ std::vector<Vertex_handle> copy(rg.begin(), rg.end());
1659
+ if (codimension + static_cast<int>(copy.size()) > dimension_ + 1 ||
1660
+ (codimension == 0 && static_cast<int>(copy.size()) > dimension_)) // n+codimension greater than dimension_
1661
+ return cofaces;
1662
+ // must be sorted in decreasing order
1663
+ assert(std::is_sorted(copy.begin(), copy.end(), std::greater<Vertex_handle>()));
1664
+ bool star = codimension == 0;
1665
+ rec_coface(copy, &root_, 1, cofaces, star, codimension + static_cast<int>(copy.size()));
1666
+ return cofaces;
1667
+ }
1668
+ }
1669
+
1670
+ public:
1671
+ /** \brief Inserts a 1-skeleton in an empty Simplex_tree.
1672
+ *
1673
+ * The Simplex_tree must contain no simplex when the method is
1674
+ * called.
1675
+ *
1676
+ * Inserts all vertices and edges given by a OneSkeletonGraph.
1677
+ * OneSkeletonGraph must be a model of
1678
+ * <a href="https://www.boost.org/doc/libs/release/libs/graph/doc/VertexAndEdgeListGraph.html">boost::VertexAndEdgeListGraph</a>
1679
+ * and <a href="https://www.boost.org/doc/libs/release/libs/graph/doc/PropertyGraph.html">boost::PropertyGraph</a>.
1680
+ *
1681
+ * The vertex filtration value is accessible through the property tag
1682
+ * vertex_filtration_t.
1683
+ * The edge filtration value is accessible through the property tag
1684
+ * edge_filtration_t.
1685
+ *
1686
+ * boost::graph_traits<OneSkeletonGraph>::vertex_descriptor
1687
+ * must be Vertex_handle.
1688
+ * boost::graph_traits<OneSkeletonGraph>::directed_category
1689
+ * can be directed_tag (the fastest, the least RAM use), undirected_tag or even
1690
+ * bidirected_tag.
1691
+ *
1692
+ * If an edge appears with multiplicity, the function will arbitrarily pick
1693
+ * one representative to read the filtration value. */
1694
+ template<class OneSkeletonGraph>
1695
+ void insert_graph(const OneSkeletonGraph& skel_graph) {
1696
+ // the simplex tree must be empty
1697
+ assert(num_simplices() == 0);
1698
+
1699
+ // is there a better way to let the compiler know that we don't mean Simplex_tree::num_vertices?
1700
+ using boost::num_vertices;
1701
+
1702
+ if (num_vertices(skel_graph) == 0) {
1703
+ return;
1704
+ }
1705
+ if (num_edges(skel_graph) == 0) {
1706
+ dimension_ = 0;
1707
+ } else {
1708
+ dimension_ = 1;
1709
+ }
1710
+
1711
+ if constexpr (!Options::stable_simplex_handles)
1712
+ root_.members_.reserve(num_vertices(skel_graph)); // probably useless in most cases
1713
+ auto verts = vertices(skel_graph) | boost::adaptors::transformed([&](auto v){
1714
+ return Dit_value_t(v, Node(&root_, get(vertex_filtration_t(), skel_graph, v))); });
1715
+ root_.members_.insert(boost::begin(verts), boost::end(verts));
1716
+ // This automatically sorts the vertices, the graph concept doesn't guarantee the order in which we iterate.
1717
+
1718
+ for (Dictionary_it it = boost::begin(root_.members_); it != boost::end(root_.members_); it++) {
1719
+ update_simplex_tree_after_node_insertion(it);
1720
+ }
1721
+
1722
+ std::pair<typename boost::graph_traits<OneSkeletonGraph>::edge_iterator,
1723
+ typename boost::graph_traits<OneSkeletonGraph>::edge_iterator> boost_edges = edges(skel_graph);
1724
+ // boost_edges.first is the equivalent to boost_edges.begin()
1725
+ // boost_edges.second is the equivalent to boost_edges.end()
1726
+ for (; boost_edges.first != boost_edges.second; boost_edges.first++) {
1727
+ auto edge = *(boost_edges.first);
1728
+ auto u = source(edge, skel_graph);
1729
+ auto v = target(edge, skel_graph);
1730
+ if (u == v) throw std::invalid_argument("Self-loops are not simplicial");
1731
+ // We cannot skip edges with the wrong orientation and expect them to
1732
+ // come a second time with the right orientation, that does not always
1733
+ // happen in practice. emplace() should be a NOP when an element with the
1734
+ // same key is already there, so seeing the same edge multiple times is
1735
+ // ok.
1736
+ // Should we actually forbid multiple edges? That would be consistent
1737
+ // with rejecting self-loops.
1738
+ if (v < u) std::swap(u, v);
1739
+ auto sh = _to_node_it(find_vertex(u));
1740
+ if (!has_children(sh)) {
1741
+ sh->second.assign_children(new Siblings(&root_, sh->first));
1742
+ }
1743
+
1744
+ insert_node_<false, false, false>(sh->second.children(), v, get(edge_filtration_t(), skel_graph, edge));
1745
+ }
1746
+ }
1747
+
1748
+ /** \brief Inserts several vertices.
1749
+ * @param[in] vertices A range of Vertex_handle
1750
+ * @param[in] filt filtration value of the new vertices (the same for all)
1751
+ *
1752
+ * This may be faster than inserting the vertices one by one, especially in a random order.
1753
+ * The complex does not need to be empty before calling this function. However, if a vertex is
1754
+ * already present, its filtration value is not modified, unlike with other insertion functions. */
1755
+ template <class VertexRange = std::initializer_list<Vertex_handle> >
1756
+ void insert_batch_vertices(VertexRange const& vertices, const Filtration_value& filt = Filtration_value()) {
1757
+ auto verts = vertices | boost::adaptors::transformed([&](auto v){
1758
+ return Dit_value_t(v, Node(&root_, filt)); });
1759
+ root_.members_.insert(boost::begin(verts), boost::end(verts));
1760
+ if (dimension_ < 0 && !root_.members_.empty()) dimension_ = 0;
1761
+ if constexpr (Options::link_nodes_by_label) {
1762
+ for (auto sh = root_.members().begin(); sh != root_.members().end(); sh++) {
1763
+ // update newly inserted simplex (the one that are not linked)
1764
+ if (!sh->second.list_max_vertex_hook_.is_linked())
1765
+ update_simplex_tree_after_node_insertion(sh);
1766
+ }
1767
+ }
1768
+ }
1769
+
1770
+ /** \brief Expands the Simplex_tree containing only its one skeleton
1771
+ * until dimension max_dim.
1772
+ *
1773
+ * The expanded simplicial complex until dimension \f$d\f$
1774
+ * attached to a graph \f$G\f$ is the maximal simplicial complex of
1775
+ * dimension at most \f$d\f$ admitting the graph \f$G\f$ as \f$1\f$-skeleton.
1776
+ * The filtration value assigned to a simplex is the maximal filtration
1777
+ * value of one of its edges.
1778
+ *
1779
+ * The Simplex_tree must contain no simplex of dimension bigger than
1780
+ * 1 when calling the method. */
1781
+ void expansion(int max_dimension) {
1782
+ if (max_dimension <= 1) return;
1783
+ clear_filtration(); // Drop the cache.
1784
+ dimension_ = max_dimension;
1785
+ for (Dictionary_it root_it = root_.members_.begin();
1786
+ root_it != root_.members_.end(); ++root_it) {
1787
+ if (has_children(root_it)) {
1788
+ siblings_expansion(root_it->second.children(), max_dimension - 1);
1789
+ }
1790
+ }
1791
+ dimension_ = max_dimension - dimension_;
1792
+ }
1793
+
1794
+ /**
1795
+ * @brief Adds a new vertex or a new edge in a flag complex, as well as all
1796
+ * simplices of its star, defined to maintain the property
1797
+ * of the complex to be a flag complex, truncated at dimension dim_max.
1798
+ * To insert a new edge, the two given vertex handles have to correspond
1799
+ * to the two end points of the edge. To insert a new vertex, the handles
1800
+ * have to be twice the same and correspond to the number you want assigned
1801
+ * to it. I.e., to insert vertex \f$i\f$, give \f$u = v = i\f$.
1802
+ * The method assumes that the given edge was not already contained in
1803
+ * the simplex tree, so the behaviour is undefined if called on an existing
1804
+ * edge. Also, the vertices of an edge have to be inserted before the edge.
1805
+ *
1806
+ * @param[in] u,v Vertex_handle representing the new edge
1807
+ * (@p v != @p u) or the new vertex (@p v == @p u).
1808
+ * @param[in] fil Filtration value of the edge.
1809
+ * @param[in] dim_max Maximal dimension of the expansion.
1810
+ * If set to -1, the expansion goes as far as possible.
1811
+ * @param[out] added_simplices Contains at the end all new
1812
+ * simplices induced by the insertion of the edge.
1813
+ * The container is not emptied and new simplices are
1814
+ * appended at the end.
1815
+ *
1816
+ * @pre `SimplexTreeOptions::link_nodes_by_label` must be true.
1817
+ * @pre When inserting the edge `[u,v]`, the vertices @p u and @p v have to be
1818
+ * already inserted in the simplex tree.
1819
+ *
1820
+ * @warning If the edges and vertices are not inserted in the order of their
1821
+ * filtration values, the method `make_filtration_non_decreasing()` has to be
1822
+ * called at the end of the insertions to restore the intended filtration.
1823
+ * Note that even then, an edge has to be inserted after its vertices.
1824
+ * @warning The method assumes that the given edge or vertex was not already
1825
+ * contained in the simplex tree, so the behaviour is undefined if called on
1826
+ * an existing simplex.
1827
+ */
1828
+ void insert_edge_as_flag( Vertex_handle u
1829
+ , Vertex_handle v
1830
+ , const Filtration_value& fil
1831
+ , int dim_max
1832
+ , std::vector<Simplex_handle>& added_simplices)
1833
+ {
1834
+ /**
1835
+ * In term of edges in the graph, inserting edge `[u,v]` only affects
1836
+ * the subtree rooted at @p u.
1837
+ *
1838
+ * For a new node with label @p v, we first do a local expansion for
1839
+ * computing the children of this new node, and then a standard expansion
1840
+ * for its children.
1841
+ * Nodes with label @p v (and their subtrees) already in the tree
1842
+ * do not get affected.
1843
+ *
1844
+ * Nodes with label @p u get affected only if a Node with label @p v is in their same
1845
+ * siblings set.
1846
+ * We then try to insert "punctually" @p v all over the subtree rooted
1847
+ * at `Node(u)`. Each insertion of a Node with @p v label induces a local
1848
+ * expansion at this Node (as explained above) and a sequence of "punctual"
1849
+ * insertion of `Node(v)` in the subtree rooted at sibling nodes of the new node,
1850
+ * on its left.
1851
+ */
1852
+
1853
+ static_assert(Options::link_nodes_by_label, "Options::link_nodes_by_label must be true");
1854
+
1855
+ if (u == v) { // Are we inserting a vertex?
1856
+ auto res_ins = insert_node_<false, false, false>(&root_, u, fil);
1857
+ if (res_ins.second) { //if the vertex is not in the complex, insert it
1858
+ added_simplices.push_back(res_ins.first); //no more insert in root_.members()
1859
+ if (dimension_ == -1) dimension_ = 0;
1860
+ }
1861
+ return; //because the vertex is isolated, no more insertions.
1862
+ }
1863
+ // else, we are inserting an edge: ensure that u < v
1864
+ if (v < u) { std::swap(u,v); }
1865
+
1866
+ //Note that we copy Simplex_handle (aka map iterators) in added_simplices
1867
+ //while we are still modifying the Simplex_tree. Insertions in siblings may
1868
+ //invalidate Simplex_handles; we take care of this fact by first doing all
1869
+ //insertion in a Sibling, then inserting all handles in added_simplices.
1870
+
1871
+ #ifdef GUDHI_DEBUG
1872
+ //check whether vertices u and v are in the tree. If not, return an error.
1873
+ auto sh_u = root_.members().find(u);
1874
+ GUDHI_CHECK(sh_u != root_.members().end() &&
1875
+ root_.members().find(v) != root_.members().end(),
1876
+ std::invalid_argument(
1877
+ "Simplex_tree::insert_edge_as_flag - inserts an edge whose vertices are not in the complex")
1878
+ );
1879
+ GUDHI_CHECK(!has_children(sh_u) ||
1880
+ sh_u->second.children()->members().find(v) == sh_u->second.children()->members().end(),
1881
+ std::invalid_argument(
1882
+ "Simplex_tree::insert_edge_as_flag - inserts an already existing edge")
1883
+ );
1884
+ #endif
1885
+
1886
+ // to update dimension
1887
+ const auto tmp_dim = dimension_;
1888
+ auto tmp_max_dim = dimension_;
1889
+
1890
+ //for all siblings containing a Node labeled with u (including the root), run
1891
+ //compute_punctual_expansion
1892
+ //todo parallelize
1893
+ List_max_vertex* nodes_with_label_u = nodes_by_label(u);//all Nodes with u label
1894
+
1895
+ GUDHI_CHECK(nodes_with_label_u != nullptr,
1896
+ "Simplex_tree::insert_edge_as_flag - cannot find the list of Nodes with label u");
1897
+
1898
+ for (auto&& node_as_hook : *nodes_with_label_u)
1899
+ {
1900
+ Node& node_u = static_cast<Node&>(node_as_hook); //corresponding node, has label u
1901
+ Simplex_handle sh_u = simplex_handle_from_node(node_u);
1902
+ Siblings* sib_u = self_siblings(sh_u);
1903
+ if (sib_u->members().find(v) != sib_u->members().end()) { //v is the label of a sibling of node_u
1904
+ int curr_dim = dimension(sib_u);
1905
+ if (dim_max == -1 || curr_dim < dim_max){
1906
+ if (!has_children(sh_u)) {
1907
+ //then node_u was a leaf and now has a new child Node labeled v
1908
+ //the child v is created in compute_punctual_expansion
1909
+ node_u.assign_children(new Siblings(sib_u, u));
1910
+ }
1911
+ dimension_ = dim_max - curr_dim - 1;
1912
+ compute_punctual_expansion(
1913
+ v,
1914
+ node_u.children(),
1915
+ fil,
1916
+ dim_max - curr_dim - 1, //>= 0 if dim_max >= 0, <0 otherwise
1917
+ added_simplices );
1918
+ dimension_ = dim_max - dimension_;
1919
+ if (dimension_ > tmp_max_dim) tmp_max_dim = dimension_;
1920
+ }
1921
+ }
1922
+ }
1923
+ if (tmp_dim <= tmp_max_dim){
1924
+ dimension_ = tmp_max_dim;
1925
+ dimension_to_be_lowered_ = false;
1926
+ } else {
1927
+ dimension_ = tmp_dim;
1928
+ }
1929
+ }
1930
+
1931
+ private:
1932
+ /** \brief Inserts a Node with label @p v in the set of siblings sib, and percolate the
1933
+ * expansion on the subtree rooted at sib. Sibling sib must not contain
1934
+ * @p v.
1935
+ * The percolation of the expansion is twofold:
1936
+ * 1- the newly inserted Node labeled @p v in sib has a subtree computed
1937
+ * via create_local_expansion.
1938
+ * 2- All Node in the members of sib, with label @p x and @p x < @p v,
1939
+ * need in turn a local_expansion by @p v iff N^+(x) contains @p v.
1940
+ */
1941
+ void compute_punctual_expansion( Vertex_handle v
1942
+ , Siblings * sib
1943
+ , const Filtration_value& fil
1944
+ , int k //k == dim_max - dimension simplices in sib
1945
+ , std::vector<Simplex_handle>& added_simplices )
1946
+ { //insertion always succeeds because the edge {u,v} used to not be here.
1947
+ auto res_ins_v = sib->members().emplace(v, Node(sib,fil));
1948
+ added_simplices.push_back(res_ins_v.first); //no more insertion in sib
1949
+ update_simplex_tree_after_node_insertion(res_ins_v.first);
1950
+
1951
+ if (k == 0) { // reached the maximal dimension. if max_dim == -1, k is never equal to 0.
1952
+ dimension_ = 0; // to keep track of the max height of the recursion tree
1953
+ return;
1954
+ }
1955
+
1956
+ //create the subtree of new Node(v)
1957
+ create_local_expansion( res_ins_v.first
1958
+ , sib
1959
+ , fil
1960
+ , k
1961
+ , added_simplices );
1962
+
1963
+ //punctual expansion in nodes on the left of v, i.e. with label x < v
1964
+ for (auto sh = sib->members().begin(); sh != res_ins_v.first; ++sh)
1965
+ { //if v belongs to N^+(x), punctual expansion
1966
+ Simplex_handle root_sh = find_vertex(sh->first); //Node(x), x < v
1967
+ if (has_children(root_sh) &&
1968
+ root_sh->second.children()->members().find(v) != root_sh->second.children()->members().end())
1969
+ { //edge {x,v} is in the complex
1970
+ if (!has_children(sh)){
1971
+ sh->second.assign_children(new Siblings(sib, sh->first));
1972
+ }
1973
+ //insert v in the children of sh, and expand.
1974
+ compute_punctual_expansion( v
1975
+ , sh->second.children()
1976
+ , fil
1977
+ , k-1
1978
+ , added_simplices );
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
+ /** \brief After the insertion of edge `{u,v}`, expansion of a subtree rooted at @p v, where the
1984
+ * Node with label @p v has just been inserted, and its parent is a Node labeled with
1985
+ * @p u. sh has no children here.
1986
+ *
1987
+ * k must be > 0
1988
+ */
1989
+ void create_local_expansion(
1990
+ Dictionary_it sh_v //Node with label v which has just been inserted
1991
+ , Siblings *curr_sib //Siblings containing the node sh_v
1992
+ , const Filtration_value& fil_uv //Fil value of the edge uv in the zz filtration
1993
+ , int k //Stopping condition for recursion based on max dim
1994
+ , std::vector<Simplex_handle> &added_simplices) //range of all new simplices
1995
+ { //pick N^+(v)
1996
+ //intersect N^+(v) with labels y > v in curr_sib
1997
+ Dictionary_it next_it = sh_v;
1998
+ ++next_it;
1999
+
2000
+ if (dimension_ > k) {
2001
+ dimension_ = k; //to keep track of the max height of the recursion tree
2002
+ }
2003
+
2004
+ create_expansion<true>(curr_sib, sh_v, next_it, fil_uv, k, &added_simplices);
2005
+ }
2006
+ //TODO boost::container::ordered_unique_range_t in the creation of a Siblings
2007
+
2008
+ /** \brief Global expansion of a subtree in the simplex tree.
2009
+ *
2010
+ * The filtration value is absolute and defined by `Filtration_value fil`.
2011
+ * The new Node are also connected appropriately in the coface
2012
+ * data structure.
2013
+ *
2014
+ * Only called in the case of `void insert_edge_as_flag(...)`.
2015
+ */
2016
+ void siblings_expansion(
2017
+ Siblings * siblings // must contain elements
2018
+ , const Filtration_value& fil
2019
+ , int k // == max_dim expansion - dimension curr siblings
2020
+ , std::vector<Simplex_handle> & added_simplices )
2021
+ {
2022
+ if (dimension_ > k) {
2023
+ dimension_ = k; //to keep track of the max height of the recursion tree
2024
+ }
2025
+ if (k == 0) { return; } //max dimension
2026
+ Dictionary_it next = ++(siblings->members().begin());
2027
+
2028
+ for (Dictionary_it s_h = siblings->members().begin();
2029
+ next != siblings->members().end(); ++s_h, ++next)
2030
+ { //find N^+(s_h)
2031
+ create_expansion<true>(siblings, s_h, next, fil, k, &added_simplices);
2032
+ }
2033
+ }
2034
+
2035
+ /** \brief Recursive expansion of the simplex tree.
2036
+ * Only called in the case of `void expansion(int max_dim)`. */
2037
+ void siblings_expansion(Siblings * siblings, // must contain elements
2038
+ int k) {
2039
+ if (k >= 0 && dimension_ > k) {
2040
+ dimension_ = k;
2041
+ }
2042
+ if (k == 0)
2043
+ return;
2044
+ Dictionary_it next = siblings->members().begin();
2045
+ ++next;
2046
+
2047
+ for (Dictionary_it s_h = siblings->members().begin();
2048
+ s_h != siblings->members().end(); ++s_h, ++next)
2049
+ {
2050
+ create_expansion<false>(siblings, s_h, next, s_h->second.filtration(), k);
2051
+ }
2052
+ }
2053
+
2054
+ /** \brief Recursive expansion of the simplex tree.
2055
+ * The method is used with `force_filtration_value == true` by `void insert_edge_as_flag(...)` and with
2056
+ * `force_filtration_value == false` by `void expansion(int max_dim)`. Therefore, `added_simplices` is assumed
2057
+ * to bon non-null in the first case and null in the second.*/
2058
+ template<bool force_filtration_value>
2059
+ void create_expansion(Siblings * siblings,
2060
+ Dictionary_it& s_h,
2061
+ Dictionary_it& next,
2062
+ const Filtration_value& fil,
2063
+ int k,
2064
+ std::vector<Simplex_handle>* added_simplices = nullptr)
2065
+ {
2066
+ Simplex_handle root_sh = find_vertex(s_h->first);
2067
+ thread_local std::vector<std::pair<Vertex_handle, Node> > inter;
2068
+
2069
+ if (!has_children(root_sh)) return;
2070
+
2071
+ intersection<force_filtration_value>(
2072
+ inter, // output intersection
2073
+ next, // begin
2074
+ siblings->members().end(), // end
2075
+ root_sh->second.children()->members().begin(),
2076
+ root_sh->second.children()->members().end(),
2077
+ fil);
2078
+ if (inter.size() != 0) {
2079
+ Siblings * new_sib = new Siblings(siblings, // oncles
2080
+ s_h->first, // parent
2081
+ inter); // boost::container::ordered_unique_range_t
2082
+ for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) {
2083
+ update_simplex_tree_after_node_insertion(it);
2084
+ if constexpr (force_filtration_value){
2085
+ //the way create_expansion is used, added_simplices != nullptr when force_filtration_value == true
2086
+ added_simplices->push_back(it);
2087
+ }
2088
+ }
2089
+ inter.clear();
2090
+ s_h->second.assign_children(new_sib);
2091
+ if constexpr (force_filtration_value){
2092
+ siblings_expansion(new_sib, fil, k - 1, *added_simplices);
2093
+ } else {
2094
+ siblings_expansion(new_sib, k - 1);
2095
+ }
2096
+ } else {
2097
+ // ensure the children property
2098
+ s_h->second.assign_children(siblings);
2099
+ inter.clear();
2100
+ }
2101
+ }
2102
+
2103
+ /** \brief Intersects Dictionary 1 [begin1;end1) with Dictionary 2 [begin2,end2)
2104
+ * and assigns the maximal possible Filtration_value to the Nodes. */
2105
+ template<bool force_filtration_value = false>
2106
+ static void intersection(std::vector<std::pair<Vertex_handle, Node> >& intersection,
2107
+ Dictionary_const_it begin1, Dictionary_const_it end1,
2108
+ Dictionary_const_it begin2, Dictionary_const_it end2,
2109
+ const Filtration_value& filtration_) {
2110
+ if (begin1 == end1 || begin2 == end2)
2111
+ return; // ----->>
2112
+ while (true) {
2113
+ if (begin1->first == begin2->first) {
2114
+ if constexpr (force_filtration_value){
2115
+ intersection.emplace_back(begin1->first, Node(nullptr, filtration_));
2116
+ } else {
2117
+ Filtration_value filt = begin1->second.filtration();
2118
+ intersect_lifetimes(filt, begin2->second.filtration());
2119
+ intersect_lifetimes(filt, filtration_);
2120
+ intersection.emplace_back(begin1->first, Node(nullptr, filt));
2121
+ }
2122
+ if (++begin1 == end1 || ++begin2 == end2)
2123
+ return; // ----->>
2124
+ } else if (begin1->first < begin2->first) {
2125
+ if (++begin1 == end1)
2126
+ return;
2127
+ } else /* begin1->first > begin2->first */ {
2128
+ if (++begin2 == end2)
2129
+ return; // ----->>
2130
+ }
2131
+ }
2132
+ }
2133
+
2134
+ public:
2135
+ /** \brief Expands a simplex tree containing only a graph. Simplices corresponding to cliques in the graph are added
2136
+ * incrementally, faces before cofaces, unless the simplex has dimension larger than `max_dim` or `block_simplex`
2137
+ * returns true for this simplex.
2138
+ *
2139
+ * @param[in] max_dim Expansion maximal dimension value.
2140
+ * @param[in] block_simplex Blocker oracle. Its concept is <CODE>bool block_simplex(Simplex_handle sh)</CODE>
2141
+ *
2142
+ * The function identifies a candidate simplex whose faces are all already in the complex, inserts
2143
+ * it with a filtration value corresponding to the maximum of the filtration values of the faces, then calls
2144
+ * `block_simplex` on a `Simplex_handle` for this new simplex. If `block_simplex` returns true, the simplex is
2145
+ * removed, otherwise it is kept. Note that the evaluation of `block_simplex` is a good time to update the
2146
+ * filtration value of the simplex if you want a customized value. The algorithm then proceeds with the next
2147
+ * candidate.
2148
+ *
2149
+ * @warning several candidates of the same dimension may be inserted simultaneously before calling `block_simplex`,
2150
+ * so if you examine the complex in `block_simplex`, you may hit a few simplices of the same dimension that have not
2151
+ * been vetted by `block_simplex` yet, or have already been rejected but not yet removed.
2152
+ */
2153
+ template< typename Blocker >
2154
+ void expansion_with_blockers(int max_dim, Blocker block_simplex) {
2155
+ // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
2156
+ for (auto& simplex : boost::adaptors::reverse(root_.members())) {
2157
+ if (has_children(&simplex)) {
2158
+ siblings_expansion_with_blockers(simplex.second.children(), max_dim, max_dim - 1, block_simplex);
2159
+ }
2160
+ }
2161
+ }
2162
+
2163
+ private:
2164
+ /** \brief Recursive expansion with blockers of the simplex tree.*/
2165
+ template< typename Blocker >
2166
+ void siblings_expansion_with_blockers(Siblings* siblings, int max_dim, int k, Blocker block_simplex) {
2167
+ if (dimension_ < max_dim - k) {
2168
+ dimension_ = max_dim - k;
2169
+ }
2170
+ if (k == 0)
2171
+ return;
2172
+ // No need to go deeper
2173
+ if (siblings->members().size() < 2)
2174
+ return;
2175
+ // Reverse loop starting before the last one for 'next' to be the last one
2176
+ for (auto simplex = std::next(siblings->members().rbegin()); simplex != siblings->members().rend(); simplex++) {
2177
+ std::vector<std::pair<Vertex_handle, Node> > intersection;
2178
+ for(auto next = siblings->members().rbegin(); next != simplex; next++) {
2179
+ bool to_be_inserted = true;
2180
+ Filtration_value filt = simplex->second.filtration();
2181
+ // If all the boundaries are present, 'next' needs to be inserted
2182
+ for (Simplex_handle border : boundary_simplex_range(simplex)) {
2183
+ Simplex_handle border_child = find_child(border, next->first);
2184
+ if (border_child == null_simplex()) {
2185
+ to_be_inserted=false;
2186
+ break;
2187
+ }
2188
+ intersect_lifetimes(filt, filtration(border_child));
2189
+ }
2190
+ if (to_be_inserted) {
2191
+ intersection.emplace_back(next->first, Node(nullptr, filt));
2192
+ }
2193
+ }
2194
+ if (intersection.size() != 0) {
2195
+ // Reverse the order to insert
2196
+ Siblings * new_sib = new Siblings(
2197
+ siblings, // oncles
2198
+ simplex->first, // parent
2199
+ boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t
2200
+ simplex->second.assign_children(new_sib);
2201
+ std::vector<Vertex_handle> blocked_new_sib_vertex_list;
2202
+ // As all intersections are inserted, we can call the blocker function on all new_sib members
2203
+ for (auto new_sib_member = new_sib->members().begin();
2204
+ new_sib_member != new_sib->members().end();
2205
+ new_sib_member++) {
2206
+ update_simplex_tree_after_node_insertion(new_sib_member);
2207
+ bool blocker_result = block_simplex(new_sib_member);
2208
+ // new_sib member has been blocked by the blocker function
2209
+ // add it to the list to be removed - do not perform it while looping on it
2210
+ if (blocker_result) {
2211
+ blocked_new_sib_vertex_list.push_back(new_sib_member->first);
2212
+ // update data structures for all deleted simplices
2213
+ // can be done in the loop as part of another data structure
2214
+ update_simplex_tree_before_node_removal(new_sib_member);
2215
+ }
2216
+ }
2217
+ if (blocked_new_sib_vertex_list.size() == new_sib->members().size()) {
2218
+ // Specific case where all have to be deleted
2219
+ delete new_sib;
2220
+ // ensure the children property
2221
+ simplex->second.assign_children(siblings);
2222
+ } else {
2223
+ for (auto& blocked_new_sib_member : blocked_new_sib_vertex_list) {
2224
+ new_sib->members().erase(blocked_new_sib_member);
2225
+ }
2226
+ // ensure recursive call
2227
+ siblings_expansion_with_blockers(new_sib, max_dim, k - 1, block_simplex);
2228
+ }
2229
+ } else {
2230
+ // ensure the children property
2231
+ simplex->second.assign_children(siblings);
2232
+ }
2233
+ }
2234
+ }
2235
+
2236
+ /** \private Returns the Simplex_handle composed of the vertex list (from the Simplex_handle), plus the given
2237
+ * Vertex_handle if the Vertex_handle is found in the Simplex_handle children list.
2238
+ * Returns null_simplex() if it does not exist
2239
+ */
2240
+ Simplex_handle find_child(Simplex_handle sh, Vertex_handle vh) const {
2241
+ if (!has_children(sh))
2242
+ return null_simplex();
2243
+
2244
+ Simplex_handle child = sh->second.children()->find(vh);
2245
+ // Specific case of boost::flat_map does not find, returns boost::flat_map::end()
2246
+ // in simplex tree we want a null_simplex()
2247
+ if (child == sh->second.children()->members().end())
2248
+ return null_simplex();
2249
+
2250
+ return child;
2251
+ }
2252
+
2253
+ public:
2254
+ /** \brief Write the hasse diagram of the simplicial complex in os.
2255
+ *
2256
+ * Each row in the file correspond to a simplex. A line is written:
2257
+ * dim idx_1 ... idx_k fil where dim is the dimension of the simplex,
2258
+ * idx_1 ... idx_k are the row index (starting from 0) of the simplices of the boundary
2259
+ * of the simplex, and fil is its filtration value.
2260
+ *
2261
+ * @note Not thread safe
2262
+ */
2263
+ void print_hasse(std::ostream& os) const {
2264
+ os << num_simplices() << " " << std::endl;
2265
+ // TODO: should use complex_simplex_range ?
2266
+ for (auto sh : filtration_simplex_range()) {
2267
+ os << dimension(sh) << " ";
2268
+ for (auto b_sh : boundary_simplex_range(sh)) {
2269
+ os << key(b_sh) << " ";
2270
+ }
2271
+ os << filtration(sh) << " \n";
2272
+ }
2273
+ }
2274
+
2275
+ public:
2276
+ /** Calls a function on each simplex. The order ensures that faces are visited before cofaces.
2277
+ * While it is fine to modify the data of a simplex (filtration, key) in the function, modifying
2278
+ * the structure itself (insertion, removal) is not supported.
2279
+ *
2280
+ * @param[in] fun Function that takes as argument a Simplex_handle and an int (representing the dimension of this
2281
+ * simplex). It may return void or bool, and in the second case returning true means that the iteration will skip
2282
+ * the children of this simplex (a subset of the cofaces).
2283
+ */
2284
+ template<class Fun>
2285
+ void for_each_simplex(Fun&& fun) const {
2286
+ // Wrap callback so it always returns bool
2287
+ auto f = [&fun](Simplex_handle sh, int dim) -> bool {
2288
+ if constexpr (std::is_same_v<void, decltype(fun(sh, dim))>) {
2289
+ fun(sh, dim);
2290
+ return false;
2291
+ } else {
2292
+ return fun(sh, dim);
2293
+ }
2294
+ };
2295
+ if (!is_empty())
2296
+ rec_for_each_simplex(root(), 0, f);
2297
+ }
2298
+
2299
+ private:
2300
+ template<class Fun>
2301
+ void rec_for_each_simplex(const Siblings* sib, int dim, Fun&& fun) const {
2302
+ Simplex_handle sh = sib->members().end();
2303
+ GUDHI_CHECK(sh != sib->members().begin(), "Bug in Gudhi: only the root siblings may be empty");
2304
+ do {
2305
+ --sh;
2306
+ if (!fun(sh, dim) && has_children(sh)) {
2307
+ rec_for_each_simplex(sh->second.children(), dim+1, fun);
2308
+ }
2309
+ // We could skip checking has_children for the first element of the iteration, we know it returns false.
2310
+ }
2311
+ while(sh != sib->members().begin());
2312
+ }
2313
+
2314
+ public:
2315
+ /** \brief This function ensures that each simplex has a higher filtration value than its faces by increasing the
2316
+ * filtration values.
2317
+ * @return True if any filtration value was modified, false if the filtration was already non-decreasing.
2318
+ *
2319
+ * If a simplex has a `NaN` filtration value, it is considered lower than any other defined filtration value.
2320
+ */
2321
+ bool make_filtration_non_decreasing() {
2322
+ bool modified = false;
2323
+ auto fun = [&modified, this](Simplex_handle sh, int dim) -> void {
2324
+ if (dim == 0) return;
2325
+
2326
+ Filtration_value& current_filt = _to_node_it(sh)->second.filtration();
2327
+
2328
+ // Find the maximum filtration value in the border and assigns it if it is greater than the current
2329
+ for (Simplex_handle b : boundary_simplex_range(sh)) {
2330
+ // considers NaN as the lowest possible value.
2331
+ // stores the filtration modification information
2332
+ modified |= intersect_lifetimes(current_filt, b->second.filtration());
2333
+ }
2334
+ };
2335
+ // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
2336
+ for_each_simplex(fun);
2337
+
2338
+ if (modified)
2339
+ clear_filtration(); // Drop the cache.
2340
+ return modified;
2341
+ }
2342
+
2343
+ public:
2344
+ /** \brief Remove all the simplices, leaving an empty complex. */
2345
+ void clear() {
2346
+ root_members_recursive_deletion();
2347
+ clear_filtration();
2348
+ dimension_ = -1;
2349
+ dimension_to_be_lowered_ = false;
2350
+ if constexpr (Options::link_nodes_by_label)
2351
+ nodes_label_to_list_.clear();
2352
+ }
2353
+
2354
+ /** \brief Prune above filtration value given as parameter. That is: if \f$ f \f$ is the given filtration value
2355
+ * and \f$ f_s \f$ the filtration value associated to the simplex \f$ s \f$ contained in the tree, \f$ s \f$ is
2356
+ * removed if and only if \f$ f < f_s \f$ or \f$ f_s = NaN \f$, where \f$ < \f$ is the
2357
+ * @ref FiltrationValue::operator< "operator<" defined for the type
2358
+ * @ref SimplexTreeOptions::Filtration_value "Simplex_tree::Filtration_value".
2359
+ * @param[in] filtration Maximum threshold value.
2360
+ * @return True if any simplex was removed, false if all simplices already had a value below the threshold.
2361
+ * \post Note that the dimension of the simplicial complex may be lower after calling `prune_above_filtration()`
2362
+ * than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper
2363
+ * bound. If you care, you can call `dimension()` to recompute the exact dimension.
2364
+ */
2365
+ bool prune_above_filtration(const Filtration_value& filtration) {
2366
+ if (is_positive_infinity(filtration))
2367
+ return false; // ---->>
2368
+ bool modified = rec_prune_above_filtration(root(), filtration);
2369
+ if(modified)
2370
+ clear_filtration(); // Drop the cache.
2371
+ return modified;
2372
+ }
2373
+
2374
+ private:
2375
+ bool rec_prune_above_filtration(Siblings* sib, const Filtration_value& filt) {
2376
+ auto&& list = sib->members();
2377
+ bool modified = false;
2378
+ bool emptied = false;
2379
+ Simplex_handle last;
2380
+
2381
+ auto to_remove = [this, filt](Dit_value_t& simplex) {
2382
+ //if filt and simplex.second.filtration() are non comparable, should return false.
2383
+ //if simplex.second.filtration() is NaN, should return true.
2384
+ if (filt < simplex.second.filtration() || !(simplex.second.filtration() == simplex.second.filtration())) {
2385
+ if (has_children(&simplex)) rec_delete(simplex.second.children());
2386
+ // dimension may need to be lowered
2387
+ dimension_to_be_lowered_ = true;
2388
+ return true;
2389
+ }
2390
+ return false;
2391
+ };
2392
+
2393
+ //TODO: `if constexpr` replaceable by `std::erase_if` in C++20? Has a risk of additional runtime,
2394
+ //so to benchmark first.
2395
+ if constexpr (Options::stable_simplex_handles) {
2396
+ modified = false;
2397
+ for (auto sh = list.begin(); sh != list.end();) {
2398
+ if (to_remove(*sh)) {
2399
+ sh = list.erase(sh);
2400
+ modified = true;
2401
+ } else {
2402
+ ++sh;
2403
+ }
2404
+ }
2405
+ emptied = (list.empty() && sib != root());
2406
+ } else {
2407
+ last = std::remove_if(list.begin(), list.end(), to_remove);
2408
+ modified = (last != list.end());
2409
+ emptied = (last == list.begin() && sib != root());
2410
+ }
2411
+
2412
+ if (emptied) {
2413
+ // Removing the whole siblings, parent becomes a leaf.
2414
+ sib->oncles()->members()[sib->parent()].assign_children(sib->oncles());
2415
+ delete sib;
2416
+ // dimension may need to be lowered
2417
+ dimension_to_be_lowered_ = true;
2418
+ return true;
2419
+ } else {
2420
+ // Keeping some elements of siblings. Remove the others, and recurse in the remaining ones.
2421
+ if constexpr (!Options::stable_simplex_handles) list.erase(last, list.end());
2422
+ for (auto&& simplex : list)
2423
+ if (has_children(&simplex)) modified |= rec_prune_above_filtration(simplex.second.children(), filt);
2424
+ }
2425
+
2426
+ return modified;
2427
+ }
2428
+
2429
+ public:
2430
+ /** \brief Remove all simplices of dimension greater than a given value.
2431
+ * @param[in] dimension Maximum dimension value.
2432
+ * @return True if any simplex was removed, false if all simplices already had a value below the dimension.
2433
+ */
2434
+ bool prune_above_dimension(int dimension) {
2435
+ if (dimension >= dimension_)
2436
+ return false;
2437
+
2438
+ bool modified = false;
2439
+ if (dimension < 0) {
2440
+ if (num_vertices() > 0) {
2441
+ root_members_recursive_deletion();
2442
+ modified = true;
2443
+ }
2444
+ // Force dimension to -1, in case user calls `prune_above_dimension(-10)`
2445
+ dimension = -1;
2446
+ } else {
2447
+ modified = rec_prune_above_dimension(root(), dimension, 0);
2448
+ }
2449
+ if(modified) {
2450
+ // Thanks to `if (dimension >= dimension_)` and dimension forced to -1 `if (dimension < 0)`, we know the new dimension
2451
+ dimension_ = dimension;
2452
+ clear_filtration(); // Drop the cache.
2453
+ }
2454
+ return modified;
2455
+ }
2456
+
2457
+ private:
2458
+ bool rec_prune_above_dimension(Siblings* sib, int dim, int actual_dim) {
2459
+ bool modified = false;
2460
+ auto&& list = sib->members();
2461
+
2462
+ for (auto&& simplex : list)
2463
+ if (has_children(&simplex)) {
2464
+ if (actual_dim >= dim) {
2465
+ rec_delete(simplex.second.children());
2466
+ simplex.second.assign_children(sib);
2467
+ modified = true;
2468
+ } else {
2469
+ modified |= rec_prune_above_dimension(simplex.second.children(), dim, actual_dim + 1);
2470
+ }
2471
+ }
2472
+
2473
+ return modified;
2474
+ }
2475
+
2476
+ private:
2477
+ /** \brief Deep search simplex tree dimension recompute.
2478
+ * @return True if the dimension was modified, false otherwise.
2479
+ * \pre Be sure the simplex tree has not a too low dimension value as the deep search stops when the former dimension
2480
+ * has been reached (cf. `upper_bound_dimension()` and `set_dimension()` methods).
2481
+ */
2482
+ bool lower_upper_bound_dimension() const {
2483
+ // reset automatic detection to recompute
2484
+ dimension_to_be_lowered_ = false;
2485
+ int new_dimension = -1;
2486
+ // Browse the tree from the left to the right as higher dimension cells are more likely on the left part of the tree
2487
+ for (Simplex_handle sh : complex_simplex_range()) {
2488
+ #ifdef DEBUG_TRACES
2489
+ for (auto vertex : simplex_vertex_range(sh)) {
2490
+ std::clog << " " << vertex;
2491
+ }
2492
+ std::clog << std::endl;
2493
+ #endif // DEBUG_TRACES
2494
+
2495
+ int sh_dimension = dimension(sh);
2496
+ if (sh_dimension >= dimension_)
2497
+ // Stop browsing as soon as the dimension is reached, no need to go further
2498
+ return false;
2499
+ new_dimension = (std::max)(new_dimension, sh_dimension);
2500
+ }
2501
+ dimension_ = new_dimension;
2502
+ return true;
2503
+ }
2504
+
2505
+
2506
+ public:
2507
+ /** \brief Remove a maximal simplex.
2508
+ * @param[in] sh Simplex handle on the maximal simplex to remove.
2509
+ * \pre Please check the simplex has no coface before removing it.
2510
+ * \exception std::invalid_argument In debug mode, if sh has children.
2511
+ * \post Note that the dimension of the simplicial complex may be lower after calling `remove_maximal_simplex()`
2512
+ * than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper
2513
+ * bound. If you care, you can call `dimension()` to recompute the exact dimension.
2514
+ */
2515
+ void remove_maximal_simplex(Simplex_handle sh) {
2516
+ // Guarantee the simplex has no children
2517
+ GUDHI_CHECK(!has_children(sh),
2518
+ std::invalid_argument("Simplex_tree::remove_maximal_simplex - argument has children"));
2519
+
2520
+ update_simplex_tree_before_node_removal(sh);
2521
+
2522
+ // Simplex is a leaf, it means the child is the Siblings owning the leaf
2523
+ Dictionary_it nodeIt = _to_node_it(sh);
2524
+ Siblings* child = nodeIt->second.children();
2525
+
2526
+ if ((child->size() > 1) || (child == root())) {
2527
+ // Not alone, just remove it from members
2528
+ // Special case when child is the root of the simplex tree, just remove it from members
2529
+ child->erase(nodeIt);
2530
+ } else {
2531
+ // Sibling is emptied : must be deleted, and its parent must point on his own Sibling
2532
+ child->oncles()->members().at(child->parent()).assign_children(child->oncles());
2533
+ delete child;
2534
+ // dimension may need to be lowered
2535
+ dimension_to_be_lowered_ = true;
2536
+ }
2537
+ }
2538
+
2539
+ /** \brief Retrieve the original filtration value for a given simplex in the Simplex_tree. Since the
2540
+ * computation of extended persistence requires modifying the filtration values, this function can be used
2541
+ * to recover the original values. Moreover, computing extended persistence requires adding new simplices
2542
+ * in the Simplex_tree. Hence, this function also outputs the type of each simplex. It can be either UP (which means
2543
+ * that the simplex was present originally, and is thus part of the ascending extended filtration), DOWN (which means
2544
+ * that the simplex is the cone of an original simplex, and is thus part of the descending extended filtration) or
2545
+ * EXTRA (which means the simplex is the cone point). See the definition of Extended_simplex_type. Note that if the simplex type is DOWN, the original filtration value
2546
+ * is set to be the original filtration value of the corresponding (not coned) original simplex.
2547
+ * \pre This function should be called only if `extend_filtration()` has been called first!
2548
+ * \post The output filtration value is supposed to be the same, but might be a little different, than the
2549
+ * original filtration value, due to the internal transformation (scaling to [-2,-1]) that is
2550
+ * performed on the original filtration values during the computation of extended persistence.
2551
+ * @param[in] f Filtration value of the simplex in the extended (i.e., modified) filtration.
2552
+ * @param[in] efd Structure containing the minimum and maximum values of the original filtration. This the output of `extend_filtration()`.
2553
+ * @return A pair containing the original filtration value of the simplex as well as the simplex type.
2554
+ */
2555
+ std::pair<Filtration_value, Extended_simplex_type> decode_extended_filtration(
2556
+ const Filtration_value& f,
2557
+ const Extended_filtration_data& efd) const
2558
+ {
2559
+ std::pair<Filtration_value, Extended_simplex_type> p;
2560
+ const Filtration_value& minval = efd.minval;
2561
+ const Filtration_value& maxval = efd.maxval;
2562
+ if (f >= -2 && f <= -1) {
2563
+ p.first = minval + (maxval - minval) * (f + 2);
2564
+ p.second = Extended_simplex_type::UP;
2565
+ } else if (f >= 1 && f <= 2) {
2566
+ p.first = minval - (maxval - minval) * (f - 2);
2567
+ p.second = Extended_simplex_type::DOWN;
2568
+ } else {
2569
+ p.first = std::numeric_limits<Filtration_value>::quiet_NaN();
2570
+ p.second = Extended_simplex_type::EXTRA;
2571
+ }
2572
+ return p;
2573
+ };
2574
+
2575
+ //TODO: externalize this method and `decode_extended_filtration`
2576
+ /** \brief Extend filtration for computing extended persistence.
2577
+ * This function only uses the filtration values at the 0-dimensional simplices,
2578
+ * and computes the extended persistence diagram induced by the lower-star filtration
2579
+ * computed with these values.
2580
+ * \post Note that after calling this function, the filtration
2581
+ * values are actually modified. The function `decode_extended_filtration()`
2582
+ * retrieves the original values and outputs the extended simplex type.
2583
+ *
2584
+ * @warning Currently only works for @ref SimplexTreeOptions::Filtration_value which are
2585
+ * float types like `float` or `double`.
2586
+ *
2587
+ * @exception std::invalid_argument In debug mode if the Simplex tree contains a vertex with the largest
2588
+ * Vertex_handle, as this method requires to create an extra vertex internally.
2589
+ * @return A data structure containing the maximum and minimum values of the original filtration.
2590
+ * It is meant to be provided as input to `decode_extended_filtration()` in order to retrieve
2591
+ * the original filtration values for each simplex.
2592
+ */
2593
+ Extended_filtration_data extend_filtration() {
2594
+ clear_filtration(); // Drop the cache.
2595
+
2596
+ // Compute maximum and minimum of filtration values
2597
+ Vertex_handle maxvert = std::numeric_limits<Vertex_handle>::min();
2598
+ Filtration_value minval = Filtration_simplex_base_real::get_infinity();
2599
+ Filtration_value maxval = Filtration_simplex_base_real::get_minus_infinity();
2600
+ for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
2601
+ const Filtration_value& f = this->filtration(sh);
2602
+ minval = std::min(minval, f);
2603
+ maxval = std::max(maxval, f);
2604
+ maxvert = std::max(sh->first, maxvert);
2605
+ }
2606
+
2607
+ GUDHI_CHECK(maxvert < std::numeric_limits<Vertex_handle>::max(), std::invalid_argument("Simplex_tree contains a vertex with the largest Vertex_handle"));
2608
+ maxvert++;
2609
+
2610
+ Simplex_tree st_copy = *this;
2611
+
2612
+ // Add point for coning the simplicial complex
2613
+ this->insert_simplex_raw({maxvert}, -3);
2614
+
2615
+ Filtration_value scale = maxval-minval;
2616
+ if (scale != 0)
2617
+ scale = 1 / scale;
2618
+
2619
+ // For each simplex
2620
+ std::vector<Vertex_handle> vr;
2621
+ for (auto sh_copy : st_copy.complex_simplex_range()) {
2622
+ auto&& simplex_range = st_copy.simplex_vertex_range(sh_copy);
2623
+ vr.assign(simplex_range.begin(), simplex_range.end());
2624
+ auto sh = this->find(vr);
2625
+
2626
+ // Create cone on simplex
2627
+ vr.push_back(maxvert);
2628
+ if (this->dimension(sh) == 0) {
2629
+ const Filtration_value& v = this->filtration(sh);
2630
+ Filtration_value scaled_v = (v - minval) * scale;
2631
+ // Assign ascending value between -2 and -1 to vertex
2632
+ this->assign_filtration(sh, -2 + scaled_v);
2633
+ // Assign descending value between 1 and 2 to cone on vertex
2634
+ this->insert_simplex(vr, 2 - scaled_v);
2635
+ } else {
2636
+ // Assign value -3 to simplex and cone on simplex
2637
+ this->assign_filtration(sh, -3);
2638
+ this->insert_simplex(vr, -3);
2639
+ }
2640
+ }
2641
+
2642
+ // Automatically assign good values for simplices
2643
+ this->make_filtration_non_decreasing();
2644
+
2645
+ // Return the filtration data
2646
+ return Extended_filtration_data(minval, maxval);
2647
+ }
2648
+
2649
+ /** \brief Returns a vertex of `sh` that has the same filtration value as `sh` if it exists, and `null_vertex()` otherwise.
2650
+ *
2651
+ * For a lower-star filtration built with `make_filtration_non_decreasing()`, this is a way to invert the process and find out which vertex had its filtration value propagated to `sh`.
2652
+ * If several vertices have the same filtration value, the one it returns is arbitrary. */
2653
+ Vertex_handle vertex_with_same_filtration(Simplex_handle sh) const {
2654
+ auto filt = filtration_(sh);
2655
+ for(auto v : simplex_vertex_range(sh))
2656
+ if(filtration_(find_vertex(v)) == filt)
2657
+ return v;
2658
+ return null_vertex();
2659
+ }
2660
+
2661
+ /** \brief Returns an edge of `sh` that has the same filtration value as `sh` if it exists, and `null_simplex()` otherwise.
2662
+ *
2663
+ * For a flag-complex built with `expansion()`, this is a way to invert the process and find out which edge had its filtration value propagated to `sh`.
2664
+ * If several edges have the same filtration value, the one it returns is arbitrary.
2665
+ *
2666
+ * \pre `sh` must have dimension at least 1. */
2667
+ Simplex_handle edge_with_same_filtration(Simplex_handle sh) const {
2668
+ // See issue #251 for potential speed improvements.
2669
+ auto&& vertices = simplex_vertex_range(sh); // vertices in decreasing order
2670
+ auto end = std::end(vertices);
2671
+ auto vi = std::begin(vertices);
2672
+ GUDHI_CHECK(vi != end, "empty simplex");
2673
+ auto v0 = *vi;
2674
+ ++vi;
2675
+ GUDHI_CHECK(vi != end, "simplex of dimension 0");
2676
+ if(std::next(vi) == end) return sh; // shortcut for dimension 1
2677
+ Static_vertex_vector suffix;
2678
+ suffix.push_back(v0);
2679
+ auto filt = filtration_(sh);
2680
+ do
2681
+ {
2682
+ Vertex_handle v = *vi;
2683
+ auto&& children1 = find_vertex(v)->second.children()->members_;
2684
+ for(auto w : suffix){
2685
+ // Can we take advantage of the fact that suffix is ordered?
2686
+ Simplex_handle s = children1.find(w);
2687
+ if(filtration_(s) == filt)
2688
+ return s;
2689
+ }
2690
+ suffix.push_back(v);
2691
+ }
2692
+ while(++vi != end);
2693
+ return null_simplex();
2694
+ }
2695
+
2696
+ /** \brief Returns a minimal face of `sh` that has the same filtration value as `sh`.
2697
+ *
2698
+ * For a filtration built with `make_filtration_non_decreasing()`, this is a way to invert the process and find out which simplex had its filtration value propagated to `sh`.
2699
+ * If several minimal (for inclusion) simplices have the same filtration value, the one it returns is arbitrary, and it is not guaranteed to be the one with smallest dimension. */
2700
+ Simplex_handle minimal_simplex_with_same_filtration(Simplex_handle sh) const {
2701
+ auto filt = filtration_(sh);
2702
+ // Naive implementation, it can be sped up.
2703
+ for(auto b : boundary_simplex_range(sh))
2704
+ if(filtration_(b) == filt)
2705
+ return minimal_simplex_with_same_filtration(b);
2706
+ return sh; // None of its faces has the same filtration.
2707
+ }
2708
+
2709
+ public:
2710
+ // intrusive list of Nodes with same label using the hooks
2711
+ typedef boost::intrusive::member_hook<Hooks_simplex_base_link_nodes, typename Hooks_simplex_base_link_nodes::Member_hook_t,
2712
+ &Hooks_simplex_base_link_nodes::list_max_vertex_hook_>
2713
+ List_member_hook_t;
2714
+ // auto_unlink in Member_hook_t is incompatible with constant time size
2715
+ typedef boost::intrusive::list<Hooks_simplex_base_link_nodes, List_member_hook_t,
2716
+ boost::intrusive::constant_time_size<false>> List_max_vertex;
2717
+ // type of hooks stored in each Node, Node inherits from Hooks_simplex_base
2718
+ typedef typename std::conditional<Options::link_nodes_by_label, Hooks_simplex_base_link_nodes,
2719
+ Hooks_simplex_base_dummy>::type Hooks_simplex_base;
2720
+
2721
+ /** Data structure to access all Nodes with a given label u. Can be used for faster
2722
+ * computation. */
2723
+ private:
2724
+ // if Options::link_nodes_by_label is true, store the lists of Nodes with same label, empty otherwise.
2725
+ // unordered_map Vertex_handle v -> list of all Nodes with label v.
2726
+ std::unordered_map<Vertex_handle, List_max_vertex> nodes_label_to_list_;
2727
+
2728
+ List_max_vertex* nodes_by_label(Vertex_handle v) {
2729
+ // Scott Meyers in Effective C++ 3rd Edition. On page 23, Item 3: a non const method can safely call a const one
2730
+ // Doing it the other way is not safe
2731
+ return const_cast<List_max_vertex*>(std::as_const(*this).nodes_by_label(v));
2732
+ }
2733
+
2734
+ List_max_vertex const* nodes_by_label(Vertex_handle v) const {
2735
+ if constexpr (Options::link_nodes_by_label) {
2736
+ auto it_v = nodes_label_to_list_.find(v);
2737
+ if (it_v != nodes_label_to_list_.end()) {
2738
+ return &(it_v->second);
2739
+ } else {
2740
+ return nullptr;
2741
+ }
2742
+ }
2743
+ return nullptr;
2744
+ }
2745
+
2746
+ /** \brief Helper method that returns the corresponding Simplex_handle from a member element defined by a node.
2747
+ */
2748
+ static Simplex_handle simplex_handle_from_node(const Node& node) {
2749
+ if constexpr (Options::stable_simplex_handles){
2750
+ //Relies on the Dictionary type to be boost::container::map<Vertex_handle, Node>.
2751
+ //If the type changes or boost fundamentally changes something on the structure of their map,
2752
+ //a safer/more general but much slower version is:
2753
+ // if (node.children()->parent() == label) { // verifies if node is a leaf
2754
+ // return children->oncles()->find(label);
2755
+ // } else {
2756
+ // return children->members().find(label);
2757
+ // }
2758
+ //Requires an additional parameter "Vertex_handle label" which is the label of the node.
2759
+
2760
+ Dictionary_const_it testIt = node.children()->members().begin();
2761
+ const Node* testNode = &testIt->second;
2762
+ auto testIIt = testIt.get();
2763
+ auto testPtr = testIIt.pointed_node();
2764
+ //distance between node and pointer to map pair in memory
2765
+ auto shift = (const char*)(testNode) - (const char*)(testPtr);
2766
+
2767
+ //decltype(testPtr) = boost::intrusive::compact_rbtree_node<void*>*
2768
+ decltype(testPtr) sh_ptr = decltype(testPtr)((const char*)(&node) - shift); //shifts from node to pointer
2769
+ //decltype(testIIt) =
2770
+ //boost::intrusive::tree_iterator<
2771
+ // boost::intrusive::bhtraits<
2772
+ // boost::container::base_node<
2773
+ // std::pair<const int, Simplex_tree_node_explicit_storage<Simplex_tree>>,
2774
+ // boost::container::dtl::intrusive_tree_hook<void*, boost::container::red_black_tree, true>, true>,
2775
+ // boost::intrusive::rbtree_node_traits<void*, true>,
2776
+ // boost::intrusive::normal_link,
2777
+ // boost::intrusive::dft_tag,
2778
+ // 3>,
2779
+ // false>
2780
+ decltype(testIIt) sh_ii;
2781
+ sh_ii = sh_ptr; //creates ``subiterator'' from pointer
2782
+ Dictionary_const_it sh(sh_ii); //creates iterator from subiterator
2783
+
2784
+ return sh;
2785
+ } else {
2786
+ //node needs to be casted as non const, because a const pair* cannot be casted into a Simplex_handle
2787
+ return (Simplex_handle)(boost::intrusive::get_parent_from_member<Dit_value_t>(const_cast<Node*>(&node),
2788
+ &Dit_value_t::second));
2789
+ }
2790
+ }
2791
+
2792
+ // Give access to Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator and keep nodes_by_label and
2793
+ // simplex_handle_from_node private
2794
+ friend class Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator<Simplex_tree>;
2795
+
2796
+ private:
2797
+ // update all extra data structures in the Simplex_tree. Must be called after all
2798
+ // simplex insertions.
2799
+ void update_simplex_tree_after_node_insertion(Simplex_handle sh) {
2800
+ #ifdef DEBUG_TRACES
2801
+ std::clog << "update_simplex_tree_after_node_insertion" << std::endl;
2802
+ #endif // DEBUG_TRACES
2803
+ if constexpr (Options::link_nodes_by_label) {
2804
+ // Creates an entry with sh->first if not already in the map and insert sh->second at the end of the list
2805
+ nodes_label_to_list_[sh->first].push_back(_to_node_it(sh)->second);
2806
+ }
2807
+ }
2808
+
2809
+ // update all extra data structures in the Simplex_tree. Must be called before
2810
+ // all simplex removals
2811
+ void update_simplex_tree_before_node_removal(Simplex_handle sh) {
2812
+ #ifdef DEBUG_TRACES
2813
+ std::clog << "update_simplex_tree_before_node_removal" << std::endl;
2814
+ #endif // DEBUG_TRACES
2815
+ if constexpr (Options::link_nodes_by_label) {
2816
+ _to_node_it(sh)->second.unlink_hooks(); // remove from lists of same label Nodes
2817
+ if (nodes_label_to_list_[sh->first].empty())
2818
+ nodes_label_to_list_.erase(sh->first);
2819
+ }
2820
+ }
2821
+
2822
+ public:
2823
+ /** \brief This function resets the filtration value of all the simplices of dimension at least min_dim. Resets all
2824
+ * the Simplex_tree when `min_dim = 0`.
2825
+ * `reset_filtration` may break the filtration property with `min_dim > 0`, and it is the user's responsibility to
2826
+ * make it a valid filtration (using a large enough `filtration`, or calling `make_filtration_non_decreasing`
2827
+ * afterwards for instance).
2828
+ * @param[in] filtration The new filtration value.
2829
+ * @param[in] min_dim The minimal dimension. Default value is 0.
2830
+ */
2831
+ void reset_filtration(const Filtration_value& filtration, int min_dim = 0) {
2832
+ rec_reset_filtration(&root_, filtration, min_dim);
2833
+ clear_filtration(); // Drop the cache.
2834
+ }
2835
+
2836
+ private:
2837
+ /** \brief Recursively resets filtration value when minimal depth <= 0.
2838
+ * @param[in] sib Siblings to be parsed.
2839
+ * @param[in] filt_value The new filtration value.
2840
+ * @param[in] min_depth The minimal depth.
2841
+ */
2842
+ void rec_reset_filtration(Siblings * sib, const Filtration_value& filt_value, int min_depth) {
2843
+ for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
2844
+ if (min_depth <= 0) {
2845
+ sh->second.assign_filtration(filt_value);
2846
+ }
2847
+ if (has_children(sh)) {
2848
+ rec_reset_filtration(sh->second.children(), filt_value, min_depth - 1);
2849
+ }
2850
+ }
2851
+ }
2852
+
2853
+ std::size_t num_simplices_and_filtration_serialization_size(Siblings const* sib, std::size_t& fv_byte_size) const {
2854
+ using namespace Gudhi::simplex_tree;
2855
+
2856
+ auto sib_begin = sib->members().begin();
2857
+ auto sib_end = sib->members().end();
2858
+ size_t simplices_number = sib->members().size();
2859
+ for (auto sh = sib_begin; sh != sib_end; ++sh) {
2860
+ if constexpr (SimplexTreeOptions::store_filtration)
2861
+ fv_byte_size += get_serialization_size_of(sh->second.filtration());
2862
+ if (has_children(sh)) {
2863
+ simplices_number += num_simplices_and_filtration_serialization_size(sh->second.children(), fv_byte_size);
2864
+ }
2865
+ }
2866
+ return simplices_number;
2867
+ }
2868
+
2869
+ public:
2870
+ /**
2871
+ * @private
2872
+ * @brief Returns the serialization required buffer size.
2873
+ *
2874
+ * @return The exact serialization required size in number of bytes.
2875
+ *
2876
+ * @warning It is meant to return the same size with the same SimplexTreeOptions and on a computer with the same
2877
+ * architecture.
2878
+ */
2879
+ std::size_t get_serialization_size() const {
2880
+ // commented for the time the released gudhi version adds number_of_parameters_
2881
+ // multipers uses it to convert a gudhi.SimplexTree to a multipers.SimplexTree
2882
+ const std::size_t np_byte_size = 0; //sizeof(decltype(number_of_parameters_));
2883
+ const std::size_t vh_byte_size = sizeof(Vertex_handle);
2884
+ std::size_t fv_byte_size = 0;
2885
+ const std::size_t tree_size = num_simplices_and_filtration_serialization_size(&root_, fv_byte_size);
2886
+ const std::size_t buffer_byte_size = np_byte_size + vh_byte_size + fv_byte_size + tree_size * 2 * vh_byte_size;
2887
+ #ifdef DEBUG_TRACES
2888
+ std::clog << "Gudhi::simplex_tree::get_serialization_size - buffer size = " << buffer_byte_size << std::endl;
2889
+ #endif // DEBUG_TRACES
2890
+ return buffer_byte_size;
2891
+ }
2892
+
2893
+ /**
2894
+ * @private
2895
+ * @brief Serialize the Simplex tree - Flatten it in a user given array of char
2896
+ *
2897
+ * @param[in] buffer An array of char allocated with enough space (cf. Gudhi::simplex_tree::get_serialization_size)
2898
+ * @param[in] buffer_size The buffer size.
2899
+ *
2900
+ * @exception std::invalid_argument If serialization does not match exactly the buffer_size value.
2901
+ *
2902
+ * @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
2903
+ * SimplexTreeOptions and on a computer with the same architecture.
2904
+ *
2905
+ * Serialize/Deserialize ignores any extra data (@ref Simplex_data) stored in the simplices for now.
2906
+ */
2907
+ /* Let's take the following simplicial complex as example: */
2908
+ /* (vertices are represented as letters to ease the understanding) */
2909
+ /* o---o---o */
2910
+ /* a b\X/c */
2911
+ /* o */
2912
+ /* d */
2913
+ /* The simplex tree is: */
2914
+ /* a o b o c o d o */
2915
+ /* | |\ | */
2916
+ /* b o c o o d o d */
2917
+ /* | */
2918
+ /* d o */
2919
+ /* The serialization is (without filtration values that comes right after vertex handle value): */
2920
+ /* 04(number of vertices)0a 0b 0c 0d(list of vertices)01(number of [a] children)0b([a,b] simplex) */
2921
+ /* 00(number of [a,b] children)02(number of [b] children)0c 0d(list of [b] children)01(number of [b,c] children) */
2922
+ /* 0d(list of [b,c] children)00(number of [b,c,d] children)00(number of [b,d] children)01(number of [c] children) */
2923
+ /* 0d(list of [c] children)00(number of [b,d] children)00(number of [d] children) */
2924
+ /* Without explanation and with filtration values: */
2925
+ /* 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 */
2926
+ void serialize(char* buffer, const std::size_t buffer_size) const {
2927
+ char* buffer_end = buffer;
2928
+ // commented for the time the released gudhi version adds number_of_parameters_
2929
+ // multipers uses it to convert a gudhi.SimplexTree to a multipers.SimplexTree
2930
+ // buffer_end = serialize_value_to_char_buffer(number_of_parameters_, buffer_end);
2931
+ buffer_end = rec_serialize(&root_, buffer_end);
2932
+ if (static_cast<std::size_t>(buffer_end - buffer) != buffer_size)
2933
+ throw std::invalid_argument("Serialization does not match end of buffer");
2934
+ }
2935
+
2936
+ private:
2937
+ /** \brief Serialize each element of the sibling and recursively call serialization. */
2938
+ char* rec_serialize(Siblings const *sib, char* buffer) const {
2939
+ char* ptr = buffer;
2940
+ ptr = serialize_value_to_char_buffer(static_cast<Vertex_handle>(sib->members().size()), ptr);
2941
+ #ifdef DEBUG_TRACES
2942
+ std::clog << "\n" << sib->members().size() << " : ";
2943
+ #endif // DEBUG_TRACES
2944
+ for (auto& map_el : sib->members()) {
2945
+ ptr = serialize_value_to_char_buffer(map_el.first, ptr); // Vertex
2946
+ if (Options::store_filtration)
2947
+ ptr = serialize_value_to_char_buffer(map_el.second.filtration(), ptr); // Filtration
2948
+ #ifdef DEBUG_TRACES
2949
+ std::clog << " [ " << map_el.first << " | " << map_el.second.filtration() << " ] ";
2950
+ #endif // DEBUG_TRACES
2951
+ }
2952
+ for (auto& map_el : sib->members()) {
2953
+ if (has_children(&map_el)) {
2954
+ ptr = rec_serialize(map_el.second.children(), ptr);
2955
+ } else {
2956
+ ptr = serialize_value_to_char_buffer(static_cast<Vertex_handle>(0), ptr);
2957
+ #ifdef DEBUG_TRACES
2958
+ std::cout << "\n0 : ";
2959
+ #endif // DEBUG_TRACES
2960
+ }
2961
+ }
2962
+ return ptr;
2963
+ }
2964
+
2965
+ public:
2966
+ /**
2967
+ * @private
2968
+ * @brief Deserialize the array of char (flatten version of the tree) to initialize a Simplex tree.
2969
+ * It is the user's responsibility to provide an 'empty' Simplex_tree, there is no guarantee otherwise.
2970
+ *
2971
+ * @param[in] buffer A pointer on a buffer that contains a serialized Simplex_tree.
2972
+ * @param[in] buffer_size The size of the buffer.
2973
+ *
2974
+ * @exception std::invalid_argument In case the deserialization does not finish at the correct buffer_size.
2975
+ * @exception std::logic_error In debug mode, if the Simplex_tree is not 'empty'.
2976
+ *
2977
+ * @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
2978
+ * SimplexTreeOptions and on a computer with the same architecture.
2979
+ *
2980
+ * Serialize/Deserialize ignores any extra data (@ref Simplex_data) stored in the simplices for now.
2981
+ */
2982
+ void deserialize(const char* buffer, const std::size_t buffer_size) {
2983
+ deserialize(buffer, buffer_size, [](Filtration_value& filtration, const char* ptr) {
2984
+ return deserialize_value_from_char_buffer(filtration, ptr);
2985
+ });
2986
+ }
2987
+
2988
+ /**
2989
+ * @private
2990
+ * @brief Deserialize the array of char (flattened version of the tree) to initialize a Simplex tree.
2991
+ * It is the user's responsibility to provide an 'empty' Simplex_tree, there is no guarantee otherwise.
2992
+ *
2993
+ * @tparam F Method taking a reference to a @ref Filtration_value and a `const char*` as input and returning a
2994
+ * `const char*`.
2995
+ * @param[in] buffer A pointer on a buffer that contains a serialized Simplex_tree.
2996
+ * @param[in] buffer_size The size of the buffer.
2997
+ * @param[in] deserialize_filtration_value To provide if the type of @ref Filtration_value does not correspond to
2998
+ * the filtration value type of the serialized simplex tree. The method should be able to read from a buffer
2999
+ * (second argument) the serialized filtration value and turn it into an object of type @ref Filtration_value that is
3000
+ * stored in the first argument of the method. It then returns the new position of the buffer pointer after the
3001
+ * reading.
3002
+ *
3003
+ * @exception std::invalid_argument In case the deserialization does not finish at the correct buffer_size.
3004
+ * @exception std::logic_error In debug mode, if the Simplex_tree is not 'empty'.
3005
+ *
3006
+ * @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
3007
+ * SimplexTreeOptions (except for the @ref Filtration_value type) and on a computer with the same architecture.
3008
+ *
3009
+ */
3010
+ template <class F>
3011
+ void deserialize(const char* buffer, const std::size_t buffer_size, F&& deserialize_filtration_value) {
3012
+ GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty"));
3013
+ const char* ptr = buffer;
3014
+ // commented for the time the released gudhi version adds number_of_parameters_
3015
+ // multipers uses it to convert a gudhi.SimplexTree to a multipers.SimplexTree
3016
+ // ptr = deserialize_value_from_char_buffer(number_of_parameters_, ptr);
3017
+
3018
+ // Needs to read size before recursivity to manage new siblings for children
3019
+ Vertex_handle members_size;
3020
+ ptr = deserialize_value_from_char_buffer(members_size, ptr);
3021
+ ptr = rec_deserialize(&root_, members_size, ptr, 0, deserialize_filtration_value);
3022
+ if (static_cast<std::size_t>(ptr - buffer) != buffer_size) {
3023
+ throw std::invalid_argument("Deserialization does not match end of buffer");
3024
+ }
3025
+ }
3026
+
3027
+ private:
3028
+ /** \brief Serialize each element of the sibling and recursively call serialization. */
3029
+ template <class F>
3030
+ const char* rec_deserialize(Siblings* sib,
3031
+ Vertex_handle members_size,
3032
+ const char* ptr,
3033
+ int dim,
3034
+ [[maybe_unused]] F&& deserialize_filtration_value)
3035
+ {
3036
+ // In case buffer is just a 0 char
3037
+ if (members_size > 0) {
3038
+ if constexpr (!Options::stable_simplex_handles) sib->members_.reserve(members_size);
3039
+ Vertex_handle vertex;
3040
+ // Set explicitly to zero to avoid false positive error raising in debug mode when store_filtration == false
3041
+ // and to force array like Filtration_value value to be empty.
3042
+ Filtration_value filtration(Gudhi::simplex_tree::empty_filtration_value);
3043
+ for (Vertex_handle idx = 0; idx < members_size; idx++) {
3044
+ ptr = deserialize_value_from_char_buffer(vertex, ptr);
3045
+ if constexpr (Options::store_filtration) {
3046
+ ptr = deserialize_filtration_value(filtration, ptr);
3047
+ }
3048
+ // Default is no children
3049
+ // If store_filtration is false, `filtration` is ignored.
3050
+ sib->members_.emplace_hint(sib->members_.end(), vertex, Node(sib, filtration));
3051
+ }
3052
+ Vertex_handle child_size;
3053
+ for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
3054
+ update_simplex_tree_after_node_insertion(sh);
3055
+ ptr = deserialize_value_from_char_buffer(child_size, ptr);
3056
+ if (child_size > 0) {
3057
+ Siblings* child = new Siblings(sib, sh->first);
3058
+ sh->second.assign_children(child);
3059
+ ptr = rec_deserialize(child, child_size, ptr, dim + 1, deserialize_filtration_value);
3060
+ }
3061
+ }
3062
+ if (dim > dimension_) {
3063
+ // Update dimension if needed
3064
+ dimension_ = dim;
3065
+ }
3066
+ }
3067
+ return ptr;
3068
+ }
3069
+
3070
+ private:
3071
+ Vertex_handle null_vertex_;
3072
+ /** \brief Set of simplex tree Nodes representing the vertices.*/
3073
+ Siblings root_;
3074
+
3075
+ // all mutable as their content has no impact on the content of the simplex tree itself
3076
+ // they correspond to some kind of cache or helper attributes.
3077
+ /** \brief Simplices ordered according to a filtration.*/
3078
+ mutable std::vector<Simplex_handle> filtration_vect_;
3079
+ /** \brief Upper bound on the dimension of the simplicial complex.*/
3080
+ mutable int dimension_;
3081
+ mutable bool dimension_to_be_lowered_;
3082
+ int number_of_parameters_; /**< Stores the number of parameters set by the user. */
3083
+ };
3084
+
3085
+ // Print a Simplex_tree in os.
3086
+ template<typename...T>
3087
+ std::ostream& operator<<(std::ostream & os, const Simplex_tree<T...> & st) {
3088
+ using handle = typename Simplex_tree<T...>::Simplex_handle;
3089
+
3090
+ if (st.num_parameters() > 1) os << st.num_parameters() << "\n";
3091
+
3092
+ // lexicographical order to ensure total order even with custom filtration values
3093
+ st.initialize_filtration(
3094
+ [&](handle sh1, handle sh2) {
3095
+ if (st.dimension(sh1) < st.dimension(sh2)) return true;
3096
+ if (st.dimension(sh1) > st.dimension(sh2)) return false;
3097
+
3098
+ auto rg1 = st.simplex_vertex_range(sh1);
3099
+ auto rg2 = st.simplex_vertex_range(sh2);
3100
+ auto it1 = rg1.begin();
3101
+ auto it2 = rg2.begin();
3102
+ // same size
3103
+ while (it1 != rg1.end()) {
3104
+ if (*it1 == *it2) {
3105
+ ++it1;
3106
+ ++it2;
3107
+ } else {
3108
+ return *it1 < *it2;
3109
+ }
3110
+ }
3111
+ return false;
3112
+ },
3113
+ [](handle) -> bool { return false; });
3114
+
3115
+ for (auto sh : st.filtration_simplex_range()) {
3116
+ os << st.dimension(sh) << " ";
3117
+ for (auto v : st.simplex_vertex_range(sh)) {
3118
+ os << v << " ";
3119
+ }
3120
+ os << st.filtration(sh) << "\n"; // TODO(VR): why adding the key ?? not read ?? << " " << st.key(sh) << " \n";
3121
+ }
3122
+ st.clear_filtration();
3123
+ return os;
3124
+ }
3125
+
3126
+ template <typename... T>
3127
+ std::istream& operator>>(std::istream& is, Simplex_tree<T...>& st) {
3128
+ typedef Simplex_tree<T...> ST;
3129
+ std::vector<typename ST::Vertex_handle> simplex;
3130
+ typename ST::Filtration_value fil;
3131
+ int max_dim = -1;
3132
+
3133
+ // searching for number of parameters which are potentially specified in the first line
3134
+ // if nothing is specified, the value is assumed to be 1
3135
+ std::string first_line;
3136
+ auto pos = is.tellg();
3137
+ getline(is, first_line);
3138
+ std::stringstream fl_stream;
3139
+ fl_stream << first_line;
3140
+ int num_param, dummy;
3141
+ fl_stream >> num_param; // tries to retrieve first numerical value
3142
+ if (fl_stream.fail()) throw std::invalid_argument("Incoming stream should not start with a non integer.");
3143
+ fl_stream >> dummy; // if number of parameters were specified, this should fail
3144
+ if (!fl_stream.fail()) {
3145
+ num_param = 1;
3146
+ is.seekg(pos, std::ios_base::beg);
3147
+ }
3148
+
3149
+ while (read_simplex(is, simplex, fil)) {
3150
+ // read all simplices in the file as a list of vertices
3151
+ // Warning : simplex_size needs to be casted in int - Can be 0
3152
+ int dim = static_cast<int>(simplex.size() - 1);
3153
+ if (max_dim < dim) {
3154
+ max_dim = dim;
3155
+ }
3156
+ // insert every simplex in the simplex tree
3157
+ st.insert_simplex(simplex, fil);
3158
+ simplex.clear();
3159
+ }
3160
+ st.set_dimension(max_dim);
3161
+ st.set_num_parameters(num_param);
3162
+
3163
+ return is;
3164
+ }
3165
+
3166
+ /** @}*/ // end addtogroup simplex_tree
3167
+
3168
+ } // namespace Gudhi
3169
+
3170
+ #endif // SIMPLEX_TREE_H_