multipers 2.3.3b6__cp313-cp313-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.

Potentially problematic release.


This version of multipers might be problematic. Click here for more details.

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