multipers 2.2.3__cp311-cp311-win_amd64.whl → 2.3.1__cp311-cp311-win_amd64.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 (182) hide show
  1. multipers/__init__.py +33 -31
  2. multipers/_signed_measure_meta.py +430 -430
  3. multipers/_slicer_meta.py +211 -212
  4. multipers/data/MOL2.py +458 -458
  5. multipers/data/UCR.py +18 -18
  6. multipers/data/graphs.py +466 -466
  7. multipers/data/immuno_regions.py +27 -27
  8. multipers/data/pytorch2simplextree.py +90 -90
  9. multipers/data/shape3d.py +101 -101
  10. multipers/data/synthetic.py +113 -111
  11. multipers/distances.py +198 -198
  12. multipers/filtration_conversions.pxd.tp +84 -84
  13. multipers/filtrations/__init__.py +18 -0
  14. multipers/{ml/convolutions.py → filtrations/density.py} +563 -520
  15. multipers/filtrations/filtrations.py +289 -0
  16. multipers/filtrations.pxd +224 -224
  17. multipers/function_rips.cp311-win_amd64.pyd +0 -0
  18. multipers/function_rips.pyx +105 -105
  19. multipers/grids.cp311-win_amd64.pyd +0 -0
  20. multipers/grids.pyx +350 -350
  21. multipers/gudhi/Persistence_slices_interface.h +132 -132
  22. multipers/gudhi/Simplex_tree_interface.h +239 -245
  23. multipers/gudhi/Simplex_tree_multi_interface.h +516 -561
  24. multipers/gudhi/cubical_to_boundary.h +59 -59
  25. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -450
  26. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -1070
  27. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -579
  28. multipers/gudhi/gudhi/Debug_utils.h +45 -45
  29. multipers/gudhi/gudhi/Fields/Multi_field.h +484 -484
  30. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +455 -455
  31. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +450 -450
  32. multipers/gudhi/gudhi/Fields/Multi_field_small.h +531 -531
  33. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +507 -507
  34. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +531 -531
  35. multipers/gudhi/gudhi/Fields/Z2_field.h +355 -355
  36. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +376 -376
  37. multipers/gudhi/gudhi/Fields/Zp_field.h +420 -420
  38. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +400 -400
  39. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +418 -418
  40. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -337
  41. multipers/gudhi/gudhi/Matrix.h +2107 -2107
  42. multipers/gudhi/gudhi/Multi_critical_filtration.h +1038 -1038
  43. multipers/gudhi/gudhi/Multi_persistence/Box.h +171 -171
  44. multipers/gudhi/gudhi/Multi_persistence/Line.h +282 -282
  45. multipers/gudhi/gudhi/Off_reader.h +173 -173
  46. multipers/gudhi/gudhi/One_critical_filtration.h +1433 -1431
  47. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +769 -769
  48. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +686 -686
  49. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +842 -842
  50. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1350 -1350
  51. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1105 -1105
  52. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +859 -859
  53. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +910 -910
  54. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +139 -139
  55. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +230 -230
  56. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +211 -211
  57. multipers/gudhi/gudhi/Persistence_matrix/boundary_cell_position_to_id_mapper.h +60 -60
  58. multipers/gudhi/gudhi/Persistence_matrix/boundary_face_position_to_id_mapper.h +60 -60
  59. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +136 -136
  60. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +190 -190
  61. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +616 -616
  62. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +150 -150
  63. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +106 -106
  64. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +219 -219
  65. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +327 -327
  66. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1140 -1140
  67. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +934 -934
  68. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +934 -934
  69. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +980 -980
  70. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1092 -1092
  71. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +192 -192
  72. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +921 -921
  73. multipers/gudhi/gudhi/Persistence_matrix/columns/small_vector_column.h +1093 -1093
  74. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +1012 -1012
  75. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1244 -1244
  76. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +186 -186
  77. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +164 -164
  78. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +156 -156
  79. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +376 -376
  80. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +540 -540
  81. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -118
  82. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -173
  83. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -128
  84. multipers/gudhi/gudhi/Persistent_cohomology.h +745 -745
  85. multipers/gudhi/gudhi/Points_off_io.h +171 -171
  86. multipers/gudhi/gudhi/Simple_object_pool.h +69 -69
  87. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +463 -463
  88. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -83
  89. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +106 -106
  90. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -277
  91. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -62
  92. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -27
  93. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +62 -62
  94. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +157 -157
  95. multipers/gudhi/gudhi/Simplex_tree.h +2794 -2794
  96. multipers/gudhi/gudhi/Simplex_tree_multi.h +152 -163
  97. multipers/gudhi/gudhi/distance_functions.h +62 -62
  98. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -104
  99. multipers/gudhi/gudhi/persistence_interval.h +253 -253
  100. multipers/gudhi/gudhi/persistence_matrix_options.h +170 -170
  101. multipers/gudhi/gudhi/reader_utils.h +367 -367
  102. multipers/gudhi/mma_interface_coh.h +256 -255
  103. multipers/gudhi/mma_interface_h0.h +223 -231
  104. multipers/gudhi/mma_interface_matrix.h +291 -282
  105. multipers/gudhi/naive_merge_tree.h +536 -575
  106. multipers/gudhi/scc_io.h +310 -289
  107. multipers/gudhi/truc.h +957 -888
  108. multipers/io.cp311-win_amd64.pyd +0 -0
  109. multipers/io.pyx +714 -711
  110. multipers/ml/accuracies.py +90 -90
  111. multipers/ml/invariants_with_persistable.py +79 -79
  112. multipers/ml/kernels.py +176 -176
  113. multipers/ml/mma.py +713 -714
  114. multipers/ml/one.py +472 -472
  115. multipers/ml/point_clouds.py +352 -346
  116. multipers/ml/signed_measures.py +1589 -1589
  117. multipers/ml/sliced_wasserstein.py +461 -461
  118. multipers/ml/tools.py +113 -113
  119. multipers/mma_structures.cp311-win_amd64.pyd +0 -0
  120. multipers/mma_structures.pxd +127 -127
  121. multipers/mma_structures.pyx +4 -8
  122. multipers/mma_structures.pyx.tp +1083 -1085
  123. multipers/multi_parameter_rank_invariant/diff_helpers.h +84 -93
  124. multipers/multi_parameter_rank_invariant/euler_characteristic.h +97 -97
  125. multipers/multi_parameter_rank_invariant/function_rips.h +322 -322
  126. multipers/multi_parameter_rank_invariant/hilbert_function.h +769 -769
  127. multipers/multi_parameter_rank_invariant/persistence_slices.h +148 -148
  128. multipers/multi_parameter_rank_invariant/rank_invariant.h +369 -369
  129. multipers/multiparameter_edge_collapse.py +41 -41
  130. multipers/multiparameter_module_approximation/approximation.h +2298 -2295
  131. multipers/multiparameter_module_approximation/combinatory.h +129 -129
  132. multipers/multiparameter_module_approximation/debug.h +107 -107
  133. multipers/multiparameter_module_approximation/format_python-cpp.h +286 -286
  134. multipers/multiparameter_module_approximation/heap_column.h +238 -238
  135. multipers/multiparameter_module_approximation/images.h +79 -79
  136. multipers/multiparameter_module_approximation/list_column.h +174 -174
  137. multipers/multiparameter_module_approximation/list_column_2.h +232 -232
  138. multipers/multiparameter_module_approximation/ru_matrix.h +347 -347
  139. multipers/multiparameter_module_approximation/set_column.h +135 -135
  140. multipers/multiparameter_module_approximation/structure_higher_dim_barcode.h +36 -36
  141. multipers/multiparameter_module_approximation/unordered_set_column.h +166 -166
  142. multipers/multiparameter_module_approximation/utilities.h +403 -419
  143. multipers/multiparameter_module_approximation/vector_column.h +223 -223
  144. multipers/multiparameter_module_approximation/vector_matrix.h +331 -331
  145. multipers/multiparameter_module_approximation/vineyards.h +464 -464
  146. multipers/multiparameter_module_approximation/vineyards_trajectories.h +649 -649
  147. multipers/multiparameter_module_approximation.cp311-win_amd64.pyd +0 -0
  148. multipers/multiparameter_module_approximation.pyx +218 -217
  149. multipers/pickle.py +90 -53
  150. multipers/plots.py +342 -334
  151. multipers/point_measure.cp311-win_amd64.pyd +0 -0
  152. multipers/point_measure.pyx +322 -320
  153. multipers/simplex_tree_multi.cp311-win_amd64.pyd +0 -0
  154. multipers/simplex_tree_multi.pxd +133 -133
  155. multipers/simplex_tree_multi.pyx +115 -48
  156. multipers/simplex_tree_multi.pyx.tp +1947 -1935
  157. multipers/slicer.cp311-win_amd64.pyd +0 -0
  158. multipers/slicer.pxd +301 -120
  159. multipers/slicer.pxd.tp +218 -214
  160. multipers/slicer.pyx +1570 -507
  161. multipers/slicer.pyx.tp +931 -914
  162. multipers/tensor/tensor.h +672 -672
  163. multipers/tensor.pxd +13 -13
  164. multipers/test.pyx +44 -44
  165. multipers/tests/__init__.py +57 -57
  166. multipers/torch/diff_grids.py +217 -217
  167. multipers/torch/rips_density.py +310 -304
  168. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/LICENSE +21 -21
  169. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/METADATA +21 -11
  170. multipers-2.3.1.dist-info/RECORD +182 -0
  171. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/WHEEL +1 -1
  172. multipers/tests/test_diff_helper.py +0 -73
  173. multipers/tests/test_hilbert_function.py +0 -82
  174. multipers/tests/test_mma.py +0 -83
  175. multipers/tests/test_point_clouds.py +0 -49
  176. multipers/tests/test_python-cpp_conversion.py +0 -82
  177. multipers/tests/test_signed_betti.py +0 -181
  178. multipers/tests/test_signed_measure.py +0 -89
  179. multipers/tests/test_simplextreemulti.py +0 -221
  180. multipers/tests/test_slicer.py +0 -221
  181. multipers-2.2.3.dist-info/RECORD +0 -189
  182. {multipers-2.2.3.dist-info → multipers-2.3.1.dist-info}/top_level.txt +0 -0
@@ -1,2295 +1,2298 @@
1
-
2
- /* This file is part of the MMA Library -
3
- * https://gitlab.inria.fr/dloiseau/multipers - which is released under MIT. See
4
- * file LICENSE for full license details. Author(s): David Loiseaux
5
- *
6
- * Copyright (C) 2021 Inria
7
- *
8
- * Modification(s):
9
- * - 2022/03 Hannah Schreiber: Integration of the new Vineyard_persistence
10
- * class, renaming and cleanup.
11
- * - 2022/05 Hannah Schreiber: Addition of Summand class and Module class.
12
- */
13
- /**
14
- * @file approximation.h
15
- * @author David Loiseaux, Hannah Schreiber
16
- * @brief Contains the functions related to the approximation of n-modules.
17
- */
18
-
19
- #ifndef APPROXIMATION_H_INCLUDED
20
- #define APPROXIMATION_H_INCLUDED
21
-
22
- #include <algorithm>
23
- #include <cmath>
24
- #include <cstddef>
25
- #include <cstdint>
26
- #include <iostream>
27
- #include <iterator>
28
- #include <limits>
29
- #include <oneapi/tbb/parallel_for.h>
30
- #include <string>
31
- #include <utility>
32
- #include <vector>
33
-
34
- #include "utilities.h"
35
-
36
- #include "debug.h"
37
- #include <Persistence_slices_interface.h>
38
- #include <gudhi/One_critical_filtration.h>
39
- #include <gudhi/Multi_critical_filtration.h>
40
- #include <gudhi/Multi_persistence/Box.h>
41
- #include <gudhi/Multi_persistence/Line.h>
42
- #include <tbb/parallel_for.h>
43
- #include <tensor/tensor.h>
44
-
45
- namespace Gudhi {
46
- namespace multiparameter {
47
- namespace mma {
48
-
49
- using Debug::Timer;
50
- using Gudhi::multi_persistence::Box;
51
- using Gudhi::multi_persistence::Line;
52
- template <typename T>
53
- class Module;
54
- template <typename T>
55
- class Summand;
56
-
57
- void threshold_filters_list(std::vector<value_type> &filtersList, const Box<value_type> &box);
58
-
59
- template <typename value_type>
60
- class Module {
61
- public:
62
- using filtration_type = Gudhi::multi_filtration::One_critical_filtration<value_type>;
63
- using module_type = std::vector<Summand<value_type>>;
64
- using image_type = std::vector<std::vector<value_type>>;
65
- using get_2dpixel_value_function_type = std::function<value_type(const typename module_type::const_iterator,
66
- const typename module_type::const_iterator,
67
- value_type,
68
- value_type)>;
69
- using get_pixel_value_function_type = std::function<value_type(const typename module_type::const_iterator,
70
- const typename module_type::const_iterator,
71
- std::vector<value_type> &)>;
72
-
73
- Module();
74
- Module(Box<value_type> &box);
75
-
76
- void resize(unsigned int size);
77
- Summand<value_type> &at(unsigned int index);
78
- Summand<value_type> &operator[](size_t index);
79
-
80
- const Summand<value_type> &operator[](const size_t index) const;
81
- template <class Barcode>
82
- void add_barcode(const Barcode &barcode);
83
- void add_barcode(const Line<value_type> &line,
84
- const std::vector<std::pair<int, std::pair<value_type, value_type>>> &barcode,
85
- const bool threshold);
86
- void add_barcode(const Line<value_type> &line,
87
- const std::vector<std::pair<value_type, value_type>> &barcode,
88
- const bool threshold);
89
- typename module_type::iterator begin();
90
- typename module_type::iterator end();
91
-
92
- void clean();
93
- void fill(const value_type precision);
94
-
95
- std::vector<image_type> get_vectorization(const value_type delta,
96
- const value_type p,
97
- const bool normalize,
98
- const Gudhi::multi_persistence::Box<value_type> &box,
99
- unsigned int horizontalResolution,
100
- unsigned int verticalResolution);
101
- std::vector<image_type> get_vectorization(unsigned int horizontalResolution,
102
- unsigned int verticalResolution,
103
- get_2dpixel_value_function_type get_pixel_value) const;
104
- image_type get_vectorization_in_dimension(const dimension_type dimension,
105
- const value_type delta,
106
- const value_type p,
107
- const bool normalize,
108
- const Gudhi::multi_persistence::Box<value_type> &box,
109
- unsigned int horizontalResolution,
110
- unsigned int verticalResolution);
111
- image_type get_vectorization_in_dimension(const dimension_type dimension,
112
- unsigned int horizontalResolution,
113
- unsigned int verticalResolution,
114
- get_2dpixel_value_function_type get_pixel_value) const;
115
- std::vector<value_type> get_landscape_values(const std::vector<value_type> &x, const dimension_type dimension) const;
116
- image_type get_landscape(const dimension_type dimension,
117
- const unsigned int k,
118
- const Box<value_type> &box,
119
- const std::vector<unsigned int> &resolution) const;
120
- std::vector<image_type> get_landscapes(const dimension_type dimension,
121
- const std::vector<unsigned int> ks,
122
- const Box<value_type> &box,
123
- const std::vector<unsigned int> &resolution) const;
124
- void add_summand(Summand<value_type> summand, int degree = -1);
125
- Box<value_type> get_box() const;
126
- void set_box(Box<value_type> box);
127
- unsigned int size() const;
128
- void infer_box(std::vector<filtration_type> &filters_list);
129
- dimension_type get_dimension() const;
130
- module_type get_summands_of_dimension(const int dimension) const;
131
- std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>>
132
- get_corners_of_dimension(const int dimension) const;
133
- MultiDiagram<filtration_type, value_type> get_barcode(const Line<value_type> &l,
134
- const dimension_type dimension = -1,
135
- const bool threshold = false) const;
136
- std::vector<std::vector<std::pair<value_type, value_type>>> get_barcode2(const Line<value_type> &l,
137
- const dimension_type dimension) const;
138
- std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> get_barcodes2(
139
- const std::vector<Line<value_type>> &lines,
140
- const dimension_type dimension = -1) const;
141
- MultiDiagrams<filtration_type, value_type> get_barcodes(const std::vector<Line<value_type>> &lines,
142
- const dimension_type dimension = -1,
143
- const bool threshold = false) const;
144
- MultiDiagrams<filtration_type, value_type> get_barcodes(const std::vector<filtration_type> &basepoints,
145
- const dimension_type dimension = -1,
146
- const bool threshold = false) const;
147
- std::vector<int> euler_curve(const std::vector<filtration_type> &points) const;
148
-
149
- inline Box<value_type> get_bounds() const;
150
- inline void rescale(const std::vector<value_type> &rescale_factors, int degree);
151
- inline void translate(const std::vector<value_type> &translation, int degree);
152
-
153
- std::vector<std::vector<value_type>> compute_pixels(const std::vector<std::vector<value_type>> &coordinates,
154
- const std::vector<int> &degrees,
155
- const Box<value_type> &box = {},
156
- const value_type delta = 0.1,
157
- const value_type p = 1,
158
- const bool normalize = true,
159
- const int n_jobs = 0);
160
-
161
- std::vector<value_type> get_interleavings(const Box<value_type> &box);
162
- using distance_to_idx_type = std::vector<std::vector<int>>;
163
- distance_to_idx_type compute_distance_idx_to(const std::vector<value_type> &pt, bool full) const;
164
- std::vector<value_type> compute_distance_to(const std::vector<value_type> &pt, bool negative) const;
165
- std::vector<std::vector<value_type>> compute_distances_to(const std::vector<std::vector<value_type>> &pt,
166
- bool negative,
167
- int n_jobs) const;
168
- template <typename dtype = value_type, typename indices_type = int32_t>
169
- void inline compute_distances_to(dtype *data_ptr,
170
- const std::vector<std::vector<value_type>> &pts,
171
- bool negative,
172
- int n_jobs) const;
173
- using distances_to_idx_type = std::vector<distance_to_idx_type>;
174
- distances_to_idx_type compute_distances_idx_to(const std::vector<std::vector<value_type>> &pt,
175
- bool full,
176
- int n_jobs) const;
177
- std::vector<value_type> compute_pixels_of_degree(const typename module_type::iterator start,
178
- const typename module_type::iterator end,
179
- const value_type delta,
180
- const value_type p,
181
- const bool normalize,
182
- const Box<value_type> &box,
183
- const std::vector<std::vector<value_type>> &coordinates,
184
- const int n_jobs = 0);
185
-
186
- using idx_dump_type =
187
- std::vector<std::vector<std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>>>>;
188
- idx_dump_type to_idx(const std::vector<std::vector<value_type>> &grid) const;
189
- Module<int64_t> grid_squeeze(const std::vector<std::vector<value_type>> &grid) const;
190
-
191
- std::vector<std::vector<std::vector<int>>> to_flat_idx(const std::vector<std::vector<value_type>> &grid) const;
192
-
193
- std::vector<int> inline get_degree_splits() const;
194
-
195
- private:
196
- module_type module_;
197
- Box<value_type> box_;
198
- void _compute_2D_image(image_type &image,
199
- const typename module_type::iterator start,
200
- const typename module_type::iterator end,
201
- const value_type delta = 0.1,
202
- const value_type p = 1,
203
- const bool normalize = true,
204
- const Box<value_type> &box = Box<value_type>(),
205
- const unsigned int horizontalResolution = 100,
206
- const unsigned int verticalResolution = 100);
207
- void _compute_2D_image(image_type &image,
208
- const typename module_type::const_iterator start,
209
- const typename module_type::const_iterator end,
210
- unsigned int horizontalResolution,
211
- unsigned int verticalResolution,
212
- get_2dpixel_value_function_type get_pixel_value) const;
213
-
214
- value_type _get_pixel_value(const typename module_type::iterator start,
215
- const typename module_type::iterator end,
216
- const filtration_type x,
217
- const value_type delta,
218
- const value_type p,
219
- const bool normalize,
220
- const value_type moduleWeight) const;
221
-
222
- void _add_bar_with_threshold(const Line<value_type> &line,
223
- const std::pair<value_type, value_type> &bar,
224
- const bool threshold_in,
225
- Summand<value_type> &summand);
226
- };
227
-
228
- template <typename value_type>
229
- class Summand {
230
- public:
231
- using births_type = Gudhi::multi_filtration::Multi_critical_filtration<value_type, false>;
232
- using deaths_type = Gudhi::multi_filtration::Multi_critical_filtration<value_type, true>;
233
- using filtration_type = typename births_type::Generator; // same for death
234
- using dimension_type = int;
235
- Summand();
236
- Summand(const births_type &birth_corners, const deaths_type &death_corners, dimension_type dimension);
237
- Summand(const std::vector<filtration_type> &birth_corners,
238
- const std::vector<filtration_type> &death_corners,
239
- dimension_type dimension);
240
-
241
- value_type get_interleaving() const;
242
- value_type get_interleaving(const Box<value_type> &box);
243
- value_type get_local_weight(const filtration_type &x, const value_type delta) const;
244
-
245
- value_type distance_to_upper(const filtration_type &x, bool negative) const;
246
- value_type distance_to_lower(const filtration_type &x, bool negative) const;
247
- value_type distance_to(const filtration_type &x, bool negative) const;
248
- std::tuple<int, int> distance_idx_to_upper(const filtration_type &x) const;
249
- std::tuple<int, int> distance_idx_to_lower(const filtration_type &x) const;
250
- std::vector<int> distance_idx_to(const filtration_type &x, bool full) const;
251
- std::pair<filtration_type, filtration_type> get_bar(const Line<value_type> &line) const;
252
- std::pair<value_type, value_type> get_bar2(const Line<value_type> &l) const;
253
- void add_bar(value_type baseBirth,
254
- value_type baseDeath,
255
- const filtration_type &basepoint,
256
- filtration_type &birth,
257
- filtration_type &death,
258
- const bool threshold,
259
- const Box<value_type> &box);
260
- void add_bar(const filtration_type &birth, const filtration_type &death);
261
- void add_bar(const filtration_type &basepoint, value_type birth, value_type death, const Box<value_type> &);
262
-
263
- const std::vector<filtration_type> &get_birth_list() const;
264
- const std::vector<filtration_type> &get_death_list() const;
265
- const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &get_upset() const;
266
- const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &get_downset() const;
267
- void clean();
268
-
269
- void complete_birth(const value_type precision);
270
- void complete_death(const value_type precision);
271
-
272
- dimension_type get_dimension() const;
273
- void set_dimension(dimension_type dimension);
274
-
275
- value_type get_landscape_value(const std::vector<value_type> &x) const;
276
-
277
- friend void swap(Summand &sum1, Summand &sum2) {
278
- std::swap(sum1.birth_corners_, sum2.birth_corners_);
279
- std::swap(sum1.death_corners_, sum2.death_corners_);
280
- std::swap(sum1.distanceTo0_, sum2.distanceTo0_);
281
- // std::swap(sum1.updateDistance_, sum2.updateDistance_);
282
- };
283
-
284
- bool contains(const filtration_type &x) const;
285
-
286
- inline Box<value_type> get_bounds() const {
287
- if (birth_corners_.num_generators() == 0) return Box<value_type>();
288
- auto dimension = birth_corners_.num_parameters();
289
- filtration_type m(dimension, std::numeric_limits<value_type>::infinity());
290
- filtration_type M(dimension, -std::numeric_limits<value_type>::infinity());
291
- for (const auto &corner : birth_corners_) {
292
- for (auto parameter = 0u; parameter < dimension; parameter++) {
293
- m[parameter] = std::min(m[parameter], corner[parameter]);
294
- }
295
- }
296
- for (const auto &corner : death_corners_) {
297
- for (auto parameter = 0u; parameter < dimension; parameter++) {
298
- auto corner_i = corner[parameter];
299
- if (corner_i != std::numeric_limits<value_type>::infinity())
300
- M[parameter] = std::max(M[parameter], corner[parameter]);
301
- }
302
- }
303
- return Box(m, M);
304
- }
305
-
306
- inline void rescale(const std::vector<value_type> &rescale_factors) {
307
- if (birth_corners_.num_generators() == 0) return;
308
- auto dimension = birth_corners_.num_parameters();
309
- for (auto &corner : birth_corners_) {
310
- for (auto parameter = 0u; parameter < dimension; parameter++) {
311
- corner[parameter] *= rescale_factors.at(parameter);
312
- }
313
- }
314
- for (auto &corner : death_corners_) {
315
- for (auto parameter = 0u; parameter < dimension; parameter++) {
316
- corner[parameter] *= rescale_factors.at(parameter);
317
- }
318
- }
319
- }
320
-
321
- inline void translate(const std::vector<value_type> &translation) {
322
- if (birth_corners_.num_generators() == 0) return;
323
- auto dimension = birth_corners_.num_parameters();
324
- for (auto &corner : birth_corners_) {
325
- for (auto parameter = 0u; parameter < dimension; parameter++) {
326
- corner[parameter] += translation.at(parameter);
327
- }
328
- }
329
- for (auto &corner : death_corners_) {
330
- for (auto parameter = 0u; parameter < dimension; parameter++) {
331
- corner[parameter] += translation.at(parameter);
332
- }
333
- }
334
- }
335
-
336
- inline Summand<int64_t> grid_squeeze(const std::vector<std::vector<value_type>> &grid) const;
337
-
338
- private:
339
- Gudhi::multi_filtration::Multi_critical_filtration<value_type, false>
340
- birth_corners_; // TODO : use Multi_critical_filtration
341
- Gudhi::multi_filtration::Multi_critical_filtration<value_type, true> death_corners_;
342
- value_type distanceTo0_;
343
- dimension_type dimension_;
344
-
345
- void _compute_interleaving(const Box<value_type> &box);
346
- void _add_birth(const filtration_type &birth);
347
- void _add_death(const filtration_type &death);
348
- value_type _rectangle_volume(const filtration_type &a, const filtration_type &b) const;
349
- value_type _get_max_diagonal(const filtration_type &a, const filtration_type &b, const Box<value_type> &box) const;
350
- value_type d_inf(const filtration_type &a, const filtration_type &b) const;
351
- void _factorize_min(filtration_type &a, const filtration_type &b);
352
- void _factorize_max(filtration_type &a, const filtration_type &b);
353
- static void _clean(std::vector<filtration_type> &list, bool keep_inf = true);
354
-
355
- static inline void _clean(births_type &list, bool keep_inf = true) { list.remove_empty_generators(keep_inf); }
356
-
357
- static inline void _clean(deaths_type &list, bool keep_inf = true) { list.remove_empty_generators(keep_inf); }
358
- };
359
-
360
- inline void threshold_filters_list(std::vector<filtration_type> &filtersList, const Box<value_type> &box) {
361
- return;
362
- for (unsigned int i = 0; i < filtersList.size(); i++) {
363
- for (value_type &value : filtersList[i]) {
364
- value = std::min(std::max(value, box.get_lower_corner()[i]), box.get_upper_corner()[i]);
365
- }
366
- }
367
- }
368
-
369
- template <class Filtration_value, int axis = 0, bool sign = true>
370
- class LineIterator {
371
- public:
372
- using value_type = typename Filtration_value::value_type;
373
- LineIterator(const Filtration_value &basepoint,
374
- const Filtration_value &direction,
375
- value_type precision,
376
- int num_iterations)
377
- : precision(precision), remaining_iterations(num_iterations), current_line(std::move(basepoint), direction) {};
378
-
379
- inline LineIterator<Filtration_value, axis, sign> &operator++() {
380
- //
381
- auto &basepoint = current_line.base_point();
382
- if (this->is_finished()) return *this;
383
- // If we didn't reached the end, go to the next line
384
- basepoint[axis] += sign ? precision : -precision;
385
- --remaining_iterations;
386
- return *this;
387
- }
388
-
389
- inline const Line<value_type> &operator*() const { return current_line; }
390
-
391
- inline LineIterator<Filtration_value, axis, sign> &next(std::size_t i) {
392
- auto &basepoint = current_line.base_point();
393
- if (this->is_finished()) return *this;
394
- // If we didn't reached the end, go to the next line
395
- basepoint[i] += sign ? precision : -precision;
396
- --remaining_iterations;
397
- return *this;
398
- }
399
-
400
- inline bool is_finished() const { return remaining_iterations <= 0; }
401
-
402
- private:
403
- const value_type precision;
404
- int remaining_iterations;
405
- Line<value_type> current_line;
406
- };
407
-
408
- template <class Filtration_value, int axis_ = 0, bool sign = true, class Slicer>
409
- inline void __add_vineyard_trajectory_to_module(Module<typename Filtration_value::value_type> &module,
410
- Slicer &&slicer,
411
- LineIterator<Filtration_value, axis_, sign> &line_iterator,
412
- const bool threshold,
413
- int axis = 0) {
414
- static_assert(
415
- std::is_same<typename Filtration_value::value_type, typename Slicer::Filtration_value::value_type>::value);
416
- using value_type = typename Filtration_value::value_type;
417
- // Line iterator should be on the biggest axis
418
- constexpr const bool verbose = false;
419
- constexpr const bool verbose2 = false;
420
- while (!line_iterator.is_finished()) {
421
- const Line<value_type> &new_line = (axis_ >= 0) ? *(++line_iterator) : *line_iterator.next(axis);
422
- // if constexpr (axis_ >= 0) {
423
- // new_line = *(++line_iterator); // first line is always a persistence
424
- // } else {
425
- // new_line = *line_iterator.next(axis);
426
- // }
427
- // copy, no need to add it
428
- if constexpr (verbose) std::cout << "----------------------------------------------" << std::endl;
429
- if constexpr (verbose) std::cout << "Line basepoint " << new_line.base_point() << std::endl;
430
- slicer.push_to(new_line);
431
-
432
- slicer.vineyard_update();
433
- if constexpr (verbose2) std::cout << slicer << std::endl;
434
- const auto &diagram = slicer.get_flat_nodim_barcode();
435
- module.add_barcode(new_line, std::move(diagram), threshold);
436
- };
437
- };
438
-
439
- template <class Filtration_value, class Slicer = SimplicialVineMatrixTruc<>>
440
- void _rec_mma(Module<typename Filtration_value::value_type> &module,
441
- Filtration_value &basepoint,
442
- const std::vector<int> &grid_size,
443
- int dim_to_iterate,
444
- Slicer &&current_persistence,
445
- const value_type precision,
446
- bool threshold) {
447
- if (dim_to_iterate <= 0) {
448
- LineIterator<Filtration_value, 0> line_iterator(std::move(basepoint), precision, grid_size[0]);
449
- __add_vineyard_trajectory_to_module<Filtration_value, 0, Slicer>(
450
- module, std::move(current_persistence), line_iterator, threshold);
451
- return;
452
- }
453
- Slicer pers_copy;
454
- Filtration_value basepoint_copy;
455
- for (int i = 0; i < grid_size[dim_to_iterate]; ++i) {
456
- // TODO : multithread, but needs matrix to be thread safe + put mutex on
457
- // module
458
- pers_copy = current_persistence;
459
- basepoint_copy = basepoint;
460
- _rec_mma(module, basepoint_copy, grid_size, dim_to_iterate - 1, pers_copy, precision, threshold);
461
- basepoint[dim_to_iterate] += precision;
462
- // current_persistence.push_to(Line(basepoint));
463
- // current_persistence.vineyard_update();
464
- }
465
- }
466
-
467
- template <int axis, class Filtration_value, class Slicer>
468
- void _rec_mma2(Module<typename Filtration_value::value_type> &module,
469
- Filtration_value &&basepoint,
470
- const Filtration_value &direction,
471
- const std::vector<int> &grid_size,
472
- const std::vector<bool> &signs,
473
- int dim_to_iterate,
474
- Slicer &&current_persistence,
475
- const value_type precision,
476
- bool threshold) {
477
- static_assert(std::is_same<typename Filtration_value::value_type, typename Slicer::value_type>::value);
478
-
479
- if (dim_to_iterate <= axis) {
480
- if (signs[axis]) {
481
- LineIterator<Filtration_value, axis, true> line_iterator(
482
- std::move(basepoint), direction, precision, grid_size[axis]);
483
- __add_vineyard_trajectory_to_module<Filtration_value, axis, true, Slicer>(
484
- module, std::move(current_persistence), line_iterator, threshold);
485
- } else {
486
- LineIterator<Filtration_value, axis, false> line_iterator(
487
- std::move(basepoint), direction, precision, grid_size[axis]);
488
- __add_vineyard_trajectory_to_module<Filtration_value, axis, false, Slicer>(
489
- module, std::move(current_persistence), line_iterator, threshold);
490
- }
491
-
492
- return;
493
- }
494
- if (grid_size[dim_to_iterate] == 0) {
495
- // no need to copy basepoint, we just skip the dim here
496
- _rec_mma2<axis, Filtration_value, Slicer>(module,
497
- std::move(basepoint),
498
- direction,
499
- grid_size,
500
- signs,
501
- dim_to_iterate - 1,
502
- std::move(current_persistence),
503
- precision,
504
- threshold);
505
- return;
506
- }
507
- for (int i = 0; i < grid_size[dim_to_iterate]; ++i) {
508
- // TODO : multithread, but needs matrix to be thread safe + put mutex on
509
- // module
510
- _rec_mma2<axis, Filtration_value, typename Slicer::ThreadSafe>(module,
511
- Filtration_value(basepoint),
512
- direction,
513
- grid_size,
514
- signs,
515
- dim_to_iterate - 1,
516
- current_persistence.weak_copy(),
517
- precision,
518
- threshold);
519
- basepoint[dim_to_iterate] += signs[dim_to_iterate] ? precision : -precision;
520
- // current_persistence.push_to(Line(basepoint));
521
- // current_persistence.vineyard_update();
522
- }
523
- }
524
-
525
- template <class Slicer, typename value_type>
526
- Module<value_type> multiparameter_module_approximation(
527
- Slicer &slicer,
528
- const Gudhi::multi_filtration::One_critical_filtration<value_type> &direction,
529
- const value_type precision,
530
- Box<value_type> &box,
531
- const bool threshold,
532
- const bool complete,
533
- const bool verbose) {
534
- static_assert(std::is_same<typename Slicer::Filtration_value::value_type,
535
- value_type>::value); // Value type can be exposed to python interface.
536
- if (verbose) std::cout << "Starting Module Approximation" << std::endl;
537
- /* using Filtration_value = Slicer::Filtration_value; */
538
-
539
- Gudhi::multi_filtration::One_critical_filtration<value_type> basepoint = box.get_lower_corner();
540
- const std::size_t num_parameters = box.dimension();
541
- std::vector<int> grid_size(num_parameters);
542
- std::vector<bool> signs(num_parameters);
543
- int signs_shifts = 0;
544
- int arg_max_signs_shifts = -1;
545
- for (std::size_t i = 0; i < num_parameters; i++) {
546
- auto &a = box.get_lower_corner()[i];
547
- auto &b = box.get_upper_corner()[i];
548
- grid_size[i] = static_cast<int>(std::ceil((std::fabs(b - a) / precision))) + 1;
549
- signs[i] = b > a;
550
- if (b < a) {
551
- std::swap(a, b);
552
- int local_shift;
553
- if (!direction.num_parameters())
554
- local_shift = grid_size[i];
555
- else {
556
- local_shift = direction[i] > 0 ? static_cast<int>(std::ceil(grid_size[i] / direction[i])) : 0;
557
- }
558
- if (local_shift > signs_shifts) {
559
- signs_shifts = std::max(signs_shifts, local_shift);
560
- arg_max_signs_shifts = i;
561
- }
562
- }
563
-
564
- // fix the box
565
- }
566
- if (signs_shifts > 0) {
567
- for (std::size_t i = 0; i < num_parameters; i++)
568
- grid_size[i] += signs_shifts; // this may be too much for large num_parameters
569
- grid_size[arg_max_signs_shifts] = 1;
570
- if (verbose)
571
- std::cout << "Had to flatten/shift coordinate " << arg_max_signs_shifts << " by " << signs_shifts << std::endl;
572
- }
573
- Module<value_type> out(box);
574
- box.inflate(2 * precision); // for infinte summands
575
-
576
- if (verbose) std::cout << "Num parameters : " << num_parameters << std::endl;
577
- if (verbose) std::cout << "Box : " << box << std::endl;
578
- if (num_parameters < 1) return out;
579
-
580
- // first line to compute
581
- // TODO: change here
582
- // for (auto i = 0u; i < basepoint.size() - 1; i++)
583
- // basepoint[i] -= box.get_upper_corner().back();
584
- // basepoint.back() = 0;
585
- Line<value_type> current_line(basepoint, direction);
586
- if (verbose) std::cout << "First line basepoint " << basepoint << std::endl;
587
-
588
- {
589
- Timer timer("Initializing mma...\n", verbose);
590
- // fills the first barcode
591
- slicer.push_to(current_line);
592
- slicer.compute_persistence();
593
- auto barcode = slicer.get_flat_barcode();
594
- auto num_bars = barcode.size();
595
- out.resize(num_bars);
596
- /* Filtration_value birthContainer(num_parameters), */
597
- /* deathContainer(num_parameters); */
598
- for (std::size_t i = 0; i < num_bars; i++) {
599
- const auto &[dim, bar] = barcode[i];
600
- /* const auto &[birth, death] = bar; */
601
- out[i].set_dimension(dim);
602
- /* out[i].add_bar(birth, death, basepoint, birthContainer, deathContainer,
603
- */
604
- /* threshold, box); */
605
- }
606
-
607
- out.add_barcode(current_line, barcode, threshold);
608
-
609
- if (verbose) std::cout << "Instantiated " << num_bars << " summands" << std::endl;
610
- }
611
- // TODO : change here
612
- // std::vector<int> grid_size(num_parameters - 1);
613
- // auto h = box.get_upper_corner().back() - box.get_lower_corner().back();
614
- // for (int i = 0; i < num_parameters - 1; i++) {
615
- // auto a = box.get_lower_corner()[i];
616
- // auto b = box.get_upper_corner()[i];
617
- // grid_size[i] =
618
- // static_cast<unsigned int>(std::ceil((std::abs(b - a + h) /
619
- // precision)));
620
- // }
621
- // TODO : change here
622
- if (verbose) {
623
- std::cout << "Grid size " << Gudhi::multi_filtration::One_critical_filtration(grid_size) << " Signs ";
624
- if (signs.empty()) {
625
- std::cout << "[]";
626
- } else {
627
- std::cout << "[";
628
- for (std::size_t i = 0; i < signs.size() - 1; i++) {
629
- std::cout << signs[i] << ", ";
630
- }
631
- std::cout << signs.back() << "]";
632
- }
633
- std::cout << std::endl;
634
- std::cout << "Max error " << precision << std::endl;
635
- }
636
-
637
- {
638
- Timer timer("Computing mma...", verbose);
639
- // actual computation. -1 as line grid is n-1 dim, -1 as we start from 0
640
- // _rec_mma(out, basepoint, grid_size, num_parameters - 2, slicer,
641
- // precision,
642
- // threshold);
643
- // TODO : change here
644
-
645
- for (std::size_t i = 1; i < num_parameters; i++) {
646
- // the loop is on the faces of the lower box
647
- // should be parallelizable, up to a mutex on out
648
- if (direction.num_parameters() && direction[i] == 0.0) continue; // skip faces with codim d_i=0
649
- auto temp_grid_size = grid_size;
650
- temp_grid_size[i] = 0;
651
- if (verbose)
652
- std::cout << "Face " << i << "/" << num_parameters << " with grid size "
653
- << Gudhi::multi_filtration::One_critical_filtration(temp_grid_size) << std::endl;
654
- // if (!direction.size() || direction[0] > 0)
655
- _rec_mma2<0>(out,
656
- Gudhi::multi_filtration::One_critical_filtration<value_type>(basepoint),
657
- direction,
658
- temp_grid_size,
659
- signs,
660
- num_parameters - 1,
661
- slicer.weak_copy(),
662
- precision,
663
- threshold);
664
- }
665
- // last one, we can destroy basepoint & cie
666
- if (!direction.num_parameters() || direction[0] > 0) {
667
- grid_size[0] = 0;
668
- if (verbose)
669
- std::cout << "Face " << num_parameters << "/" << num_parameters << " with grid size "
670
- << Gudhi::multi_filtration::One_critical_filtration(grid_size) << std::endl;
671
- _rec_mma2<1>(out,
672
- std::move(basepoint),
673
- direction,
674
- grid_size,
675
- signs,
676
- num_parameters - 1,
677
- std::move(slicer),
678
- precision,
679
- threshold);
680
- }
681
- }
682
-
683
- { // for Timer
684
- Timer timer("Cleaning output ... ", verbose);
685
- out.clean();
686
- if (complete) {
687
- if (verbose) std::cout << "Completing output ...";
688
- for (std::size_t i = 0; i < num_parameters; i++) out.fill(precision);
689
- }
690
- } // Timer death
691
- return out;
692
- };
693
-
694
- template <typename value_type>
695
- template <class Barcode>
696
- inline void Module<value_type>::add_barcode(const Barcode &barcode) {
697
- constexpr const bool verbose = false;
698
- if (barcode.size() != module_.size()) {
699
- std::cerr << "Barcode sizes doesn't match. Module is " << std::to_string(module_.size()) << " and barcode is "
700
- << std::to_string(barcode.size()) << std::endl;
701
- }
702
- unsigned int count = 0;
703
- for (const auto &bar_ : barcode) {
704
- auto &summand = this->operator[](count++);
705
- auto &[dim, bar] = bar_;
706
- auto &[birth_filtration, death_filtration] = bar;
707
- if constexpr (verbose) std::cout << "Birth " << birth_filtration << " Death " << death_filtration << std::endl;
708
- summand.add_bar(birth_filtration, death_filtration);
709
- }
710
- }
711
-
712
- template <typename value_type>
713
- inline void Module<value_type>::add_barcode(
714
- const Line<value_type> &line,
715
- const std::vector<std::pair<int, std::pair<value_type, value_type>>> &barcode,
716
- const bool threshold_in) {
717
- assert(barcode.size() == module_.size() && "Barcode sizes doesn't match.");
718
-
719
- auto count = 0U;
720
- for (const auto &extBar : barcode) {
721
- auto &[dim, bar] = extBar;
722
- _add_bar_with_threshold(line, bar, threshold_in, this->operator[](count++));
723
- }
724
- }
725
-
726
- template <typename value_type>
727
- inline void Module<value_type>::add_barcode(const Line<value_type> &line,
728
- const std::vector<std::pair<value_type, value_type>> &barcode,
729
- const bool threshold_in) {
730
- assert(barcode.size() == module_.size() && "Barcode sizes doesn't match.");
731
-
732
- auto count = 0U;
733
- for (const auto &bar : barcode) {
734
- _add_bar_with_threshold(line, bar, threshold_in, this->operator[](count++));
735
- }
736
- }
737
-
738
- template <typename value_type>
739
- inline void Module<value_type>::_add_bar_with_threshold(const Line<value_type> &line,
740
- const std::pair<value_type, value_type> &bar,
741
- const bool threshold_in,
742
- Summand<value_type> &summand) {
743
- constexpr const bool verbose = false;
744
- auto [birth_filtration, death_filtration] = bar;
745
-
746
- if (birth_filtration >= death_filtration) return;
747
-
748
- if constexpr (verbose) {
749
- std::cout << "--BAR (" << birth_filtration << ", " << death_filtration << ") at basepoint " << line.base_point()
750
- << " direction " << line.direction() << std::endl;
751
- }
752
-
753
- auto birth_container = line[birth_filtration];
754
- if constexpr (verbose) std::cout << " B: " << birth_container << " B*d: " << birth_filtration * line.direction();
755
- if (birth_container.is_minus_inf()) {
756
- if (threshold_in) birth_container = box_.get_lower_corner();
757
- } else {
758
- bool allInf = true;
759
- for (std::size_t i = 0U; i < birth_container.num_parameters(); i++) {
760
- auto t = box_.get_lower_corner()[i];
761
- if (birth_container[i] < t - 1e-10) birth_container[i] = threshold_in ? t : -filtration_type::T_inf;
762
- if (birth_container[i] != -filtration_type::T_inf) allInf = false;
763
- }
764
- if (allInf) birth_container = filtration_type::minus_inf();
765
- }
766
-
767
- auto death_container = line[death_filtration];
768
- if constexpr (verbose) std::cout << " D: " << death_container;
769
- if (death_container.is_plus_inf()) {
770
- if (threshold_in) death_container = box_.get_upper_corner();
771
- } else {
772
- bool allInf = true;
773
- for (std::size_t i = 0U; i < death_container.num_parameters(); i++) {
774
- auto t = box_.get_upper_corner()[i];
775
- if (death_container[i] > t + 1e-10) death_container[i] = threshold_in ? t : filtration_type::T_inf;
776
- if (death_container[i] != filtration_type::T_inf) allInf = false;
777
- }
778
- if (allInf) death_container = filtration_type::inf();
779
- }
780
-
781
- if constexpr (verbose) std::cout << " BT: " << birth_container << " DT: " << death_container << std::endl;
782
- summand.add_bar(birth_container, death_container);
783
- }
784
-
785
- template <typename value_type>
786
- inline Module<value_type>::Module() {}
787
-
788
- template <typename value_type>
789
- inline Module<value_type>::Module(Box<value_type> &box) : box_(box) {}
790
-
791
- template <typename value_type>
792
- inline void Module<value_type>::resize(const unsigned int size) {
793
- module_.resize(size);
794
- }
795
-
796
- template <typename value_type>
797
- inline Summand<value_type> &Module<value_type>::at(const unsigned int index) {
798
- return module_.at(index);
799
- }
800
-
801
- template <typename value_type>
802
- inline Summand<value_type> &Module<value_type>::operator[](const size_t index) {
803
- return this->module_[index];
804
- }
805
-
806
- template <typename value_type>
807
- inline const Summand<value_type> &Module<value_type>::operator[](const size_t index) const {
808
- return this->module_[index];
809
- }
810
-
811
- template <typename value_type>
812
- inline typename Module<value_type>::module_type::iterator Module<value_type>::begin() {
813
- return module_.begin();
814
- }
815
-
816
- template <typename value_type>
817
- inline typename Module<value_type>::module_type::iterator Module<value_type>::end() {
818
- return module_.end();
819
- }
820
-
821
- template <typename value_type>
822
- inline void Module<value_type>::add_summand(Summand<value_type> summand, int degree) {
823
- if (degree >= 0) summand.set_dimension(degree);
824
- module_.push_back(summand);
825
- }
826
-
827
- /**
828
- * @brief Remove the empty summands of the output
829
- *
830
- * @param output p_output:...
831
- * @param keep_order p_keep_order:... Defaults to false.
832
- */
833
-
834
- template <typename value_type>
835
- inline void Module<value_type>::clean() {
836
- module_type tmp;
837
- for (size_t i = 0u; i < module_.size(); i++) {
838
- module_[i].clean();
839
- }
840
- module_.erase(
841
- std::remove_if(
842
- module_.begin(), module_.end(), [](const Summand<value_type> &s) { return s.get_upset().is_plus_inf(); }),
843
- module_.end());
844
- }
845
-
846
- template <typename value_type>
847
- inline void Module<value_type>::fill(const value_type precision) {
848
- if (module_.empty()) return;
849
-
850
- for (Summand<value_type> &sum : module_) {
851
- sum.complete_birth(precision);
852
- sum.complete_death(precision);
853
- }
854
- }
855
-
856
- template <typename value_type>
857
- std::vector<value_type> inline Module<value_type>::get_interleavings(const Box<value_type> &box) {
858
- std::vector<value_type> out(this->size());
859
- for (auto i = 0u; i < out.size(); ++i) {
860
- out[i] = module_[i].get_interleaving(box);
861
- }
862
- return out;
863
- }
864
-
865
- template <typename value_type>
866
- typename Module<value_type>::distance_to_idx_type inline Module<value_type>::compute_distance_idx_to(
867
- const std::vector<value_type> &pt,
868
- bool full) const {
869
- typename Module<value_type>::distance_to_idx_type out(module_.size(), std::vector<int>(full ? 4 : 2));
870
- for (auto i = 0u; i < module_.size(); ++i) {
871
- out[i] = module_[i].distance_idx_to(pt, full);
872
- }
873
- return out;
874
- }
875
-
876
- template <typename value_type>
877
- std::vector<value_type> inline Module<value_type>::compute_distance_to(const std::vector<value_type> &pt,
878
- bool negative) const {
879
- std::vector<value_type> out(this->size());
880
- for (auto i = 0u; i < this->size(); ++i) {
881
- out[i] = module_[i].distance_to(pt, negative);
882
- }
883
- return out;
884
- }
885
-
886
- template <typename value_type>
887
- std::vector<std::vector<value_type>> inline Module<value_type>::compute_distances_to(
888
- const std::vector<std::vector<value_type>> &pts,
889
- bool negative,
890
- int n_jobs) const {
891
- std::vector<std::vector<value_type>> out(pts.size(), std::vector<value_type>(this->size()));
892
- oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
893
- arena.execute([&] {
894
- tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
895
- tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
896
- out[i][j] = module_[j].distance_to(pts[i], negative);
897
- });
898
- });
899
- });
900
- return out;
901
- }
902
-
903
- template <typename value_type>
904
- template <typename dtype, typename indices_type>
905
- void inline Module<value_type>::compute_distances_to(dtype *data_ptr,
906
- const std::vector<std::vector<value_type>> &pts,
907
- bool negative,
908
- int n_jobs) const {
909
- tensor::static_tensor_view<dtype, indices_type> container(
910
- data_ptr, {static_cast<int>(pts.size()), static_cast<int>(this->size())});
911
- oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
912
- arena.execute([&] {
913
- tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
914
- // tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
915
- dtype *current_ptr = &container[{static_cast<int>(i), 0}];
916
- for (std::size_t j = 0u; j < this->size(); ++j) {
917
- *(current_ptr + j) = module_[j].distance_to(pts[i], negative);
918
- }
919
- });
920
- // });
921
- });
922
- }
923
-
924
- template <typename value_type>
925
- typename Module<value_type>::distances_to_idx_type inline Module<value_type>::compute_distances_idx_to(
926
- const std::vector<std::vector<value_type>> &pts,
927
- bool full,
928
- int n_jobs) const {
929
- Module::distances_to_idx_type out(pts.size(),
930
- Module::distance_to_idx_type(module_.size(), std::vector<int>(full ? 4 : 2)));
931
-
932
- oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
933
- arena.execute([&] {
934
- tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
935
- tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
936
- out[i][j] = module_[j].distance_idx_to(pts[i], full);
937
- });
938
- });
939
- });
940
- return out;
941
- }
942
-
943
- template <typename value_type>
944
- inline std::vector<std::vector<value_type>> Module<value_type>::compute_pixels(
945
- const std::vector<std::vector<value_type>> &coordinates,
946
- const std::vector<int> &degrees,
947
- const Box<value_type> &box,
948
- const value_type delta,
949
- const value_type p,
950
- const bool normalize,
951
- const int n_jobs) {
952
- auto num_degrees = degrees.size();
953
- auto num_pts = coordinates.size();
954
- std::vector<std::vector<value_type>> out(num_degrees, std::vector<value_type>(num_pts));
955
-
956
- typename module_type::iterator start;
957
- typename module_type::iterator end = module_.begin();
958
- for (auto degree_idx = 0u; degree_idx < num_degrees; degree_idx++) {
959
- { // for Timer
960
- auto d = degrees[degree_idx];
961
- Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
962
- start = end;
963
- while (start != module_.end() && start->get_dimension() != d) start++;
964
- if (start == module_.end()) break;
965
- end = start;
966
- while (end != module_.end() && end->get_dimension() == d) end++;
967
- out[degree_idx] = compute_pixels_of_degree(start, end, delta, p, normalize, box, coordinates, n_jobs);
968
- } // Timer death
969
- }
970
- return out;
971
- }
972
-
973
- template <typename value_type>
974
- inline typename std::vector<typename Module<value_type>::image_type> Module<value_type>::get_vectorization(
975
- const value_type delta,
976
- const value_type p,
977
- const bool normalize,
978
- const Box<value_type> &box,
979
- unsigned int horizontalResolution,
980
- unsigned int verticalResolution) {
981
- dimension_type maxDim = module_.back().get_dimension();
982
- std::vector<Module::image_type> image_vector(maxDim + 1);
983
- typename module_type::iterator start;
984
- typename module_type::iterator end = module_.begin();
985
- for (dimension_type d = 0; d <= maxDim; d++) {
986
- { // for Timer
987
- Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
988
- start = end;
989
- while (end != module_.end() && end->get_dimension() == d) end++;
990
- _compute_2D_image(
991
- image_vector.at(d), start, end, delta, p, normalize, box, horizontalResolution, verticalResolution);
992
- } // Timer death
993
- }
994
- return image_vector;
995
- }
996
-
997
- template <typename value_type>
998
- inline std::vector<typename Module<value_type>::image_type> Module<value_type>::get_vectorization(
999
- unsigned int horizontalResolution,
1000
- unsigned int verticalResolution,
1001
- get_2dpixel_value_function_type get_pixel_value) const {
1002
- dimension_type maxDim = module_.back().get_dimension();
1003
- std::vector<Module::image_type> image_vector(maxDim + 1);
1004
- typename module_type::const_iterator start;
1005
- typename module_type::const_iterator end = module_.begin();
1006
- for (dimension_type d = 0; d <= maxDim; d++) {
1007
- { // for Timer
1008
- Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
1009
- start = end;
1010
- while (end != module_.end() && end->get_dimension() == d) end++;
1011
- _compute_2D_image(image_vector.at(d), start, end, horizontalResolution, verticalResolution, get_pixel_value);
1012
- } // Timer death
1013
- }
1014
- return image_vector;
1015
- }
1016
-
1017
- template <typename value_type>
1018
- inline typename Module<value_type>::image_type Module<value_type>::get_vectorization_in_dimension(
1019
- const dimension_type dimension,
1020
- const value_type delta,
1021
- const value_type p,
1022
- const bool normalize,
1023
- const Box<value_type> &box,
1024
- unsigned int horizontalResolution,
1025
- unsigned int verticalResolution) {
1026
- Debug::Timer timer("Computing image of dimension " + std::to_string(dimension) + " ...", verbose);
1027
-
1028
- Module::image_type image;
1029
- typename module_type::iterator start = module_.begin();
1030
- while (start != module_.end() && start->get_dimension() < dimension) start++;
1031
- typename module_type::iterator end = start;
1032
- while (end != module_.end() && end->get_dimension() == dimension) end++;
1033
- _compute_2D_image(image, start, end, delta, p, normalize, box, horizontalResolution, verticalResolution);
1034
-
1035
- return image;
1036
- }
1037
-
1038
- template <typename value_type>
1039
- inline typename Module<value_type>::image_type Module<value_type>::get_vectorization_in_dimension(
1040
- const dimension_type dimension,
1041
- unsigned int horizontalResolution,
1042
- unsigned int verticalResolution,
1043
- get_2dpixel_value_function_type get_pixel_value) const {
1044
- Debug::Timer timer("Computing image of dimension " + std::to_string(dimension) + " ...", verbose);
1045
-
1046
- typename Module::image_type image;
1047
- typename module_type::const_iterator start = module_.begin();
1048
- while (start != module_.end() && start->get_dimension() < dimension) start++;
1049
- typename module_type::const_iterator end = start;
1050
- while (end != module_.end() && end->get_dimension() == dimension) end++;
1051
- _compute_2D_image(image, start, end, horizontalResolution, verticalResolution, get_pixel_value);
1052
-
1053
- return image;
1054
- }
1055
-
1056
- template <typename value_type>
1057
- std::vector<value_type> Module<value_type>::get_landscape_values(const std::vector<value_type> &x,
1058
- const dimension_type dimension) const {
1059
- std::vector<value_type> out;
1060
- out.reserve(this->size());
1061
- for (unsigned int i = 0; i < this->size(); i++) {
1062
- const Summand<value_type> &summand = this->module_[i];
1063
- if (summand.get_dimension() == dimension) out.push_back(summand.get_landscape_value(x));
1064
- }
1065
- std::sort(out.begin(), out.end(), [](const value_type x, const value_type y) { return x > y; });
1066
- return out;
1067
- }
1068
-
1069
- template <typename value_type>
1070
- typename Module<value_type>::image_type Module<value_type>::get_landscape(
1071
- const dimension_type dimension,
1072
- const unsigned int k,
1073
- const Box<value_type> &box,
1074
- const std::vector<unsigned int> &resolution) const {
1075
- // TODO extend in higher dimension (ie, change the image type to a template
1076
- // class)
1077
- Module::image_type image;
1078
- image.resize(resolution[0], std::vector<value_type>(resolution[1]));
1079
- value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / resolution[0];
1080
- value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / resolution[1];
1081
- tbb::parallel_for(0U, resolution[0], [&](unsigned int i) {
1082
- tbb::parallel_for(0U, resolution[1], [&](unsigned int j) {
1083
- auto landscape = this->get_landscape_values(
1084
- {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j}, dimension);
1085
- image[i][j] = k < landscape.size() ? landscape[k] : 0;
1086
- });
1087
- });
1088
- return image;
1089
- }
1090
-
1091
- template <typename value_type>
1092
- std::vector<typename Module<value_type>::image_type> Module<value_type>::get_landscapes(
1093
- const dimension_type dimension,
1094
- const std::vector<unsigned int> ks,
1095
- const Box<value_type> &box,
1096
- const std::vector<unsigned int> &resolution) const {
1097
- std::vector<Module::image_type> images(ks.size());
1098
- for (auto &image : images) image.resize(resolution[0], std::vector<value_type>(resolution[1]));
1099
- value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / resolution[0];
1100
- value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / resolution[1];
1101
-
1102
- tbb::parallel_for(0U, resolution[0], [&](unsigned int i) {
1103
- tbb::parallel_for(0U, resolution[1], [&](unsigned int j) {
1104
- std::vector<value_type> landscapes = this->get_landscape_values(
1105
- {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j}, dimension);
1106
- for (const auto k : ks) {
1107
- images[k][i][j] = k < landscapes.size() ? landscapes[k] : 0;
1108
- }
1109
- });
1110
- });
1111
- return images;
1112
- }
1113
-
1114
- template <typename value_type>
1115
- inline Box<value_type> Module<value_type>::get_box() const {
1116
- return this->box_;
1117
- }
1118
-
1119
- template <typename value_type>
1120
- inline void Module<value_type>::set_box(Box<value_type> box) {
1121
- this->box_ = box;
1122
- }
1123
-
1124
- template <typename value_type>
1125
- inline unsigned int Module<value_type>::size() const {
1126
- return this->module_.size();
1127
- }
1128
-
1129
- template <typename value_type>
1130
- inline void Module<value_type>::infer_box(std::vector<filtration_type> &f) {
1131
- this->box_.infer_from_filters(f);
1132
- }
1133
-
1134
- template <typename value_type>
1135
- inline dimension_type Module<value_type>::get_dimension() const {
1136
- return this->module_.empty() ? -1 : this->module_.back().get_dimension();
1137
- }
1138
-
1139
- template <typename value_type>
1140
- inline std::vector<Summand<value_type>> Module<value_type>::get_summands_of_dimension(const int dimension) const {
1141
- std::vector<Summand<value_type>> list;
1142
- for (const Summand<value_type> &summand : this->module_) {
1143
- if (summand.get_dimension() == dimension) list.push_back(summand);
1144
- }
1145
- return list;
1146
- }
1147
-
1148
- template <typename value_type>
1149
- inline std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>>
1150
- Module<value_type>::get_corners_of_dimension(const int dimension) const {
1151
- std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>> list;
1152
- for (const Summand<value_type> &summand : this->module_) {
1153
- if (summand.get_dimension() == dimension)
1154
- list.push_back(std::make_pair(
1155
- std::vector<std::vector<value_type>>(summand.get_birth_list().begin(), summand.get_birth_list().end()),
1156
- std::vector<std::vector<value_type>>(summand.get_death_list().begin(), summand.get_death_list().end())));
1157
- }
1158
- return list;
1159
- }
1160
-
1161
- template <typename value_type>
1162
- std::vector<std::vector<std::pair<value_type, value_type>>> Module<value_type>::get_barcode2(
1163
- const Line<value_type> &l,
1164
- const dimension_type dimension) const {
1165
- constexpr const bool verbose = false;
1166
- std::vector<std::vector<std::pair<value_type, value_type>>> barcode(this->get_dimension() + 1);
1167
- for (auto i = 0; i < this->get_dimension(); ++i) {
1168
- barcode[i].reserve(this->size());
1169
- }
1170
- for (unsigned int i = 0; i < this->size(); i++) {
1171
- const Summand<value_type> &summand = this->module_[i];
1172
- if constexpr (verbose) std::cout << "Summand of dimension " << summand.get_dimension() << std::endl;
1173
-
1174
- if (dimension != -1 && summand.get_dimension() != dimension) continue;
1175
- /* if (dimension != -1 && summand.get_dimension() > dimension) */
1176
- /* break; */
1177
- const auto &pushed_summand = summand.get_bar2(l);
1178
-
1179
- barcode[summand.get_dimension()].push_back(pushed_summand);
1180
- }
1181
- return barcode;
1182
- }
1183
-
1184
- template <typename value_type>
1185
- MultiDiagram<typename Module<value_type>::filtration_type, value_type>
1186
- Module<value_type>::get_barcode(const Line<value_type> &l, const dimension_type dimension, const bool threshold) const {
1187
- constexpr const bool verbose = false;
1188
- if constexpr (verbose)
1189
- std::cout << "Computing barcode of dimension " << dimension << " and threshold " << threshold << std::endl;
1190
- std::vector<MultiDiagram_point<filtration_type>> barcode(this->size());
1191
- std::pair<value_type, value_type> threshold_bounds;
1192
- if (threshold) threshold_bounds = l.get_bounds(this->box_);
1193
- unsigned int summand_idx = 0;
1194
- for (unsigned int i = 0; i < this->size(); i++) {
1195
- const Summand<value_type> &summand = this->module_[i];
1196
- if constexpr (verbose) std::cout << "Summand of dimension " << summand.get_dimension() << std::endl;
1197
-
1198
- if (dimension != -1 && summand.get_dimension() != dimension) continue;
1199
- /* if (dimension != -1 && summand.get_dimension() > dimension) */
1200
- /* break; */
1201
- auto pushed_summand = summand.get_bar(l);
1202
-
1203
- filtration_type &pbirth = pushed_summand.first;
1204
- filtration_type &pdeath = pushed_summand.second;
1205
- if constexpr (verbose) std::cout << "BAR : " << pbirth << " " << pdeath << std::endl;
1206
- if (threshold) {
1207
- auto min = l[threshold_bounds.first];
1208
- auto max = l[threshold_bounds.second];
1209
- if (!(pbirth < max) || !(pdeath > min)) {
1210
- /* continue; */ // We still need summands to be aligned. The price to
1211
- // pay is some memory.
1212
- pbirth = std::numeric_limits<filtration_type>::infinity();
1213
- pdeath = pbirth;
1214
- }
1215
- pbirth.push_to_least_common_upper_bound(min);
1216
- pdeath.pull_to_greatest_common_lower_bound(max);
1217
- }
1218
- barcode[summand_idx++] = MultiDiagram_point(summand.get_dimension(), pbirth, pdeath);
1219
- }
1220
- barcode.resize(summand_idx);
1221
- return MultiDiagram<filtration_type, value_type>(barcode);
1222
- }
1223
-
1224
- template <typename value_type>
1225
- MultiDiagrams<typename Module<value_type>::filtration_type, value_type> Module<value_type>::get_barcodes(
1226
- const std::vector<Line<value_type>> &lines,
1227
- const dimension_type dimension,
1228
- const bool threshold) const {
1229
- unsigned int nlines = lines.size();
1230
- MultiDiagrams<typename Module<value_type>::filtration_type, value_type> out(nlines);
1231
- tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1232
- const Line<value_type> &l = lines[i];
1233
- out[i] = this->get_barcode(l, dimension, threshold);
1234
- });
1235
- return out;
1236
- }
1237
-
1238
- template <typename value_type>
1239
- std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> Module<value_type>::get_barcodes2(
1240
- const std::vector<Line<value_type>> &lines,
1241
- const dimension_type dimension) const {
1242
- unsigned int nlines = lines.size();
1243
- std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> out(
1244
- this->get_dimension() + 1, std::vector<std::vector<std::pair<value_type, value_type>>>(nlines));
1245
- tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1246
- const Line<value_type> &l = lines[i];
1247
- for (const auto &summand : module_) {
1248
- if (dimension != -1 && summand.get_dimension() != dimension) continue;
1249
- const auto &bar = summand.get_bar2(l);
1250
- out[summand.get_dimension()][i].push_back(bar);
1251
- }
1252
- });
1253
- return out;
1254
- }
1255
-
1256
- template <typename value_type>
1257
- MultiDiagrams<typename Module<value_type>::filtration_type, value_type> Module<value_type>::get_barcodes(
1258
- const std::vector<filtration_type> &basepoints,
1259
- const dimension_type dimension,
1260
- const bool threshold) const {
1261
- unsigned int nlines = basepoints.size();
1262
- MultiDiagrams<typename Module<value_type>::filtration_type, value_type> out(nlines);
1263
- // for (unsigned int i = 0; i < nlines; i++){
1264
- tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1265
- const Line<value_type> &l = Line<value_type>(basepoints[i]);
1266
- out[i] = this->get_barcode(l, dimension, threshold);
1267
- });
1268
- return out;
1269
- }
1270
-
1271
- template <typename value_type>
1272
- std::vector<int> Module<value_type>::euler_curve(const std::vector<filtration_type> &points) const {
1273
- unsigned int npts = points.size();
1274
- std::vector<int> out(npts);
1275
- // #pragma omp parallel for
1276
- tbb::parallel_for(0U, static_cast<unsigned int>(out.size()), [&](unsigned int i) {
1277
- auto &euler_char = out[i];
1278
- const filtration_type &point = points[i];
1279
- /* #pragma omp parallel for reduction(+ : euler_char) */
1280
- for (const Summand<value_type> &I : this->module_) {
1281
- if (I.contains(point)) {
1282
- int sign = I.get_dimension() % 2 ? -1 : 1;
1283
- euler_char += sign;
1284
- }
1285
- }
1286
- });
1287
- return out;
1288
- }
1289
-
1290
- template <typename value_type>
1291
- inline Box<value_type> Module<value_type>::get_bounds() const {
1292
- dimension_type num_parameters = box_.get_lower_corner().num_parameters();
1293
- filtration_type lower_bound(num_parameters, std::numeric_limits<value_type>::infinity());
1294
- filtration_type upper_bound(num_parameters, -std::numeric_limits<value_type>::infinity());
1295
- for (const auto &summand : module_) {
1296
- const auto &summand_bounds = summand.get_bounds();
1297
- const auto &[m, M] = summand_bounds.get_bounding_corners();
1298
- for (auto parameter = 0; parameter < num_parameters; parameter++) {
1299
- lower_bound[parameter] = std::min(m[parameter], lower_bound[parameter]);
1300
- upper_bound[parameter] = std::min(M[parameter], upper_bound[parameter]);
1301
- }
1302
- }
1303
- return Box(lower_bound, upper_bound);
1304
- }
1305
-
1306
- template <typename value_type>
1307
- inline void Module<value_type>::rescale(const std::vector<value_type> &rescale_factors, int degree) {
1308
- for (auto &summand : module_) {
1309
- if (degree == -1 or summand.get_dimension() == degree) summand.rescale(rescale_factors);
1310
- }
1311
- }
1312
-
1313
- template <typename value_type>
1314
- inline void Module<value_type>::translate(const std::vector<value_type> &translation, int degree) {
1315
- for (auto &summand : module_) {
1316
- if (degree == -1 or summand.get_dimension() == degree) summand.translate(translation);
1317
- }
1318
- }
1319
-
1320
- template <typename value_type>
1321
- inline std::vector<value_type> Module<value_type>::compute_pixels_of_degree(
1322
- const typename module_type::iterator start,
1323
- const typename module_type::iterator end,
1324
- const value_type delta,
1325
- const value_type p,
1326
- const bool normalize,
1327
- const Box<value_type> &box,
1328
- const std::vector<std::vector<value_type>> &coordinates,
1329
- const int n_jobs) {
1330
- unsigned int num_pixels = coordinates.size();
1331
- std::vector<value_type> out(num_pixels);
1332
- value_type moduleWeight = 0;
1333
- { // for Timer
1334
- Debug::Timer timer("Computing module weight ...", verbose);
1335
- for (auto it = start; it != end; it++) // precomputes interleaving restricted to box for all summands.
1336
- it->get_interleaving(box);
1337
- if (p == 0) {
1338
- // #pragma omp parallel for reduction(+ : moduleWeight)
1339
- for (auto it = start; it != end; it++) {
1340
- moduleWeight += it->get_interleaving() > 0;
1341
- }
1342
- } else if (p != inf) {
1343
- // #pragma omp parallel for reduction(+ : moduleWeight)
1344
- for (auto it = start; it != end; it++) {
1345
- // /!\ TODO deal with inf summands (for the moment, depends on the box
1346
- // ...)
1347
- if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1348
- moduleWeight += std::pow(it->get_interleaving(), p);
1349
- }
1350
- } else {
1351
- // #pragma omp parallel for reduction(std::max : moduleWeight)
1352
- for (auto it = start; it != end; it++) {
1353
- if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1354
- moduleWeight = std::max(moduleWeight, it->get_interleaving());
1355
- }
1356
- }
1357
- } // Timer death
1358
- if (verbose) std::cout << "Module " << start->get_dimension() << " has weight : " << moduleWeight << "\n";
1359
- if (!moduleWeight) return out;
1360
-
1361
- if constexpr (Debug::debug)
1362
- if (moduleWeight < 0) {
1363
- if constexpr (Debug::debug) std::cout << "!! Negative weight !!" << std::endl;
1364
- // image.clear();
1365
- return {};
1366
- }
1367
-
1368
- oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
1369
- arena.execute([&] {
1370
- tbb::parallel_for(0u, num_pixels, [&](unsigned int i) {
1371
- out[i] = _get_pixel_value(start, end, coordinates[i], delta, p, normalize, moduleWeight);
1372
- });
1373
- });
1374
- return out;
1375
- }
1376
-
1377
- template <typename value_type>
1378
- inline Module<int64_t> Module<value_type>::grid_squeeze(const std::vector<std::vector<value_type>> &grid) const {
1379
- auto dimension = this->get_dimension();
1380
- Module<int64_t> out(this->size());
1381
- for (auto i = 0u; i < this->size(); ++i) {
1382
- const auto &interval = this->operator[](i);
1383
- out[i] = interval.grid_squeeze(grid);
1384
- }
1385
- return out;
1386
- }
1387
-
1388
- template <typename value_type>
1389
- inline Summand<int64_t> Summand<value_type>::grid_squeeze(const std::vector<std::vector<value_type>> &grid) const {
1390
- auto dimension = this->get_dimension();
1391
- Summand<int64_t> out(
1392
- compute_coordinates_in_grid(birth_corners_, grid), compute_coordinates_in_grid(death_corners_, grid), dimension_);
1393
- return out;
1394
- }
1395
-
1396
- /**
1397
- * dim, summand, (birth/death), num_pts, num_parameters
1398
- */
1399
- template <typename value_type>
1400
- inline typename Module<value_type>::idx_dump_type Module<value_type>::to_idx(
1401
- const std::vector<std::vector<value_type>> &grid) const {
1402
- unsigned int num_parameters = grid.size();
1403
- auto dimension = this->get_dimension();
1404
- idx_dump_type out(dimension + 1);
1405
- for (auto i = 0u; i < this->size(); ++i) {
1406
- auto &interval = this->operator[](i);
1407
- auto &out_of_dim = out[interval.get_dimension()];
1408
- out_of_dim.reserve(this->size());
1409
- std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>> interval_idx;
1410
-
1411
- auto &birth_idx = interval_idx.first;
1412
- birth_idx.reserve(interval.get_birth_list().size());
1413
- auto &death_idx = interval_idx.second;
1414
- death_idx.reserve(interval.get_death_list().size());
1415
-
1416
- for (const auto &pt : interval.get_birth_list()) {
1417
- std::vector<int> pt_idx(pt.size());
1418
- for (auto i = 0u; i < num_parameters; ++i) {
1419
- pt_idx[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1420
- }
1421
- birth_idx.push_back(pt_idx);
1422
- }
1423
- for (const auto &pt : interval.get_death_list()) {
1424
- std::vector<int> pt_idx(pt.size());
1425
- for (auto i = 0u; i < num_parameters; ++i) {
1426
- pt_idx[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1427
- }
1428
- death_idx.push_back(pt_idx);
1429
- }
1430
- out_of_dim.push_back(interval_idx);
1431
- }
1432
- return out;
1433
- }
1434
-
1435
- template <typename value_type>
1436
- std::vector<int> inline to_grid_coord(const Gudhi::multi_filtration::One_critical_filtration<value_type> &pt,
1437
- const std::vector<std::vector<value_type>> &grid) {
1438
- std::size_t num_parameters = grid.size();
1439
- std::vector<int> out(num_parameters);
1440
- if (pt.is_plus_inf() || pt.is_nan()) [[unlikely]] {
1441
- for (size_t i = 0; i < num_parameters; ++i) out[i] = grid[i].size() - 1;
1442
- return out;
1443
- }
1444
- if (pt.is_minus_inf()) [[unlikely]] {
1445
- for (size_t i = 0; i < num_parameters; ++i) out[i] = 0;
1446
- return out;
1447
- }
1448
- // pt has to be of size num_parameters now
1449
- for (size_t i = 0u; i < num_parameters; ++i) {
1450
- if (pt[i] >= grid[i].back()) [[unlikely]]
1451
- out[i] = grid[i].size() - 1;
1452
- else if (pt[i] <= grid[i][0]) [[unlikely]] {
1453
- out[i] = 0;
1454
- } else
1455
- out[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1456
- }
1457
- return out;
1458
- }
1459
-
1460
- template <typename value_type>
1461
- std::vector<std::vector<std::vector<int>>> inline Module<value_type>::to_flat_idx(
1462
- const std::vector<std::vector<value_type>> &grid) const {
1463
- std::vector<std::vector<std::vector<int>>> out(3);
1464
- auto &idx = out[0];
1465
- auto &births = out[1];
1466
- auto &deaths = out[2];
1467
-
1468
- idx.resize(2);
1469
- idx[0].resize(this->size());
1470
- idx[1].resize(this->size());
1471
-
1472
- // some heuristic: usually
1473
- births.reserve(2 * this->size());
1474
- deaths.reserve(2 * this->size());
1475
- for (auto i = 0u; i < this->size(); ++i) {
1476
- auto &interval = this->operator[](i);
1477
- idx[0][i] = interval.get_birth_list().size();
1478
- for (const auto &pt : interval.get_birth_list()) {
1479
- births.push_back(to_grid_coord(pt, grid));
1480
- }
1481
- idx[1][i] = interval.get_death_list().size();
1482
- for (const auto &pt : interval.get_death_list()) {
1483
- deaths.push_back(to_grid_coord(pt, grid));
1484
- }
1485
- }
1486
- return out;
1487
- }
1488
-
1489
- template <typename value_type>
1490
- std::vector<int> inline Module<value_type>::get_degree_splits() const {
1491
- std::vector<int> splits = {};
1492
- int current_degree = 0;
1493
- for (auto i = 0u; i < this->size(); ++i) {
1494
- const auto &summand = this->operator[](i);
1495
- while (summand.get_dimension() > current_degree) [[unlikely]] {
1496
- current_degree++;
1497
- splits.push_back(i);
1498
- }
1499
- }
1500
- return splits;
1501
- }
1502
-
1503
- template <typename value_type>
1504
- inline void Module<value_type>::_compute_2D_image(Module::image_type &image,
1505
- const typename module_type::iterator start,
1506
- const typename module_type::iterator end,
1507
- const value_type delta,
1508
- const value_type p,
1509
- const bool normalize,
1510
- const Box<value_type> &box,
1511
- const unsigned int horizontalResolution,
1512
- const unsigned int verticalResolution) {
1513
- image.resize(horizontalResolution, std::vector<value_type>(verticalResolution));
1514
- value_type moduleWeight = 0;
1515
- { // for Timer
1516
- Debug::Timer timer("Computing module weight ...", verbose);
1517
- for (auto it = start; it != end; it++) // precomputes interleaving restricted to box for all summands.
1518
- it->get_interleaving(box);
1519
- if (p == 0) {
1520
- /* #pragma omp parallel for reduction(+ : moduleWeight) */
1521
- for (auto it = start; it != end; it++) {
1522
- moduleWeight += it->get_interleaving() > 0;
1523
- }
1524
- } else if (p != inf) {
1525
- /* #pragma omp parallel for reduction(+ : moduleWeight) */
1526
- for (auto it = start; it != end; it++) {
1527
- // /!\ TODO deal with inf summands (for the moment, depends on the box
1528
- // ...)
1529
- if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1530
- moduleWeight += std::pow(it->get_interleaving(), p);
1531
- }
1532
- } else {
1533
- /* #pragma omp parallel for reduction(std::max : moduleWeight) */
1534
- for (auto it = start; it != end; it++) {
1535
- if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1536
- moduleWeight = std::max(moduleWeight, it->get_interleaving());
1537
- }
1538
- }
1539
- } // Timer death
1540
- if (verbose) std::cout << "Module " << start->get_dimension() << " has weight : " << moduleWeight << "\n";
1541
- if (!moduleWeight) return;
1542
-
1543
- if constexpr (Debug::debug)
1544
- if (moduleWeight < 0) {
1545
- if constexpr (Debug::debug) std::cout << "!! Negative weight !!" << std::endl;
1546
- // image.clear();
1547
- return;
1548
- }
1549
-
1550
- value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / horizontalResolution;
1551
- value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / verticalResolution;
1552
-
1553
- { // for Timer
1554
- Debug::Timer timer("Computing pixel values ...", verbose);
1555
-
1556
- tbb::parallel_for(0U, horizontalResolution, [&](unsigned int i) {
1557
- tbb::parallel_for(0U, verticalResolution, [&](unsigned int j) {
1558
- image[i][j] = _get_pixel_value(start,
1559
- end,
1560
- {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j},
1561
- delta,
1562
- p,
1563
- normalize,
1564
- moduleWeight);
1565
- });
1566
- });
1567
- } // Timer death
1568
- }
1569
-
1570
- template <typename value_type>
1571
- inline void Module<value_type>::_compute_2D_image(Module::image_type &image,
1572
- const typename module_type::const_iterator start,
1573
- const typename module_type::const_iterator end,
1574
- unsigned int horizontalResolution,
1575
- unsigned int verticalResolution,
1576
- get_2dpixel_value_function_type get_pixel_value) const {
1577
- image.resize(horizontalResolution, std::vector<value_type>(verticalResolution));
1578
- const Box<value_type> &box = this->box_;
1579
- value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / horizontalResolution;
1580
- value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / verticalResolution;
1581
-
1582
- { // for Timer
1583
- Debug::Timer timer("Computing pixel values ...", verbose);
1584
-
1585
- // #pragma omp parallel for collapse(2)
1586
- // for (unsigned int i = 0; i < horizontalResolution; i++)
1587
- // {
1588
- // for (unsigned int j = 0; j < verticalResolution;
1589
- // j++)
1590
- // {
1591
- // image[i][j] = get_pixel_value(
1592
- // start,
1593
- // end,
1594
- // box.get_lower_corner()[0] +
1595
- // stepX * i,
1596
- // box.get_lower_corner()[1] + stepY * j);
1597
- // }
1598
- // }
1599
- tbb::parallel_for(0U, horizontalResolution, [&](unsigned int i) {
1600
- tbb::parallel_for(0U, verticalResolution, [&](unsigned int j) {
1601
- image[i][j] =
1602
- get_pixel_value(start, end, box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j);
1603
- });
1604
- });
1605
-
1606
- } // Timer death
1607
- }
1608
-
1609
- template <typename value_type>
1610
- inline value_type Module<value_type>::_get_pixel_value(const typename module_type::iterator start,
1611
- const typename module_type::iterator end,
1612
- const filtration_type x,
1613
- const value_type delta,
1614
- const value_type p,
1615
- const bool normalize,
1616
- const value_type moduleWeight) const {
1617
- value_type value = 0;
1618
- if (p == 0) {
1619
- /* #pragma omp parallel for reduction(+ : value) */
1620
- for (auto it = start; it != end; it++) {
1621
- value += it->get_local_weight(x, delta);
1622
- }
1623
- if (normalize) value /= moduleWeight;
1624
- return value;
1625
- }
1626
- if (p != inf) {
1627
- /* #pragma omp parallel for reduction(+ : value) */
1628
- for (auto it = start; it != end; it++) {
1629
- value_type summandWeight = it->get_interleaving();
1630
- value_type summandXWeight = it->get_local_weight(x, delta);
1631
- value += std::pow(summandWeight, p) * summandXWeight;
1632
- }
1633
- if (normalize) value /= moduleWeight;
1634
- return value;
1635
- }
1636
-
1637
- /* #pragma omp parallel for reduction(std::max : value) */
1638
- for (auto it = start; it != end; it++) {
1639
- value = std::max(value, it->get_local_weight(x, delta));
1640
- }
1641
- return value;
1642
- }
1643
-
1644
- /////////////////////////////////////////////////
1645
-
1646
- template <typename value_type>
1647
- inline Summand<value_type>::Summand()
1648
- : birth_corners_(1, births_type::Generator::T_inf),
1649
- death_corners_(1, -births_type::Generator::T_inf),
1650
- distanceTo0_(-1),
1651
- dimension_(-1) {}
1652
-
1653
- template <typename value_type>
1654
- inline Summand<value_type>::Summand(
1655
- const typename std::vector<typename Summand<value_type>::filtration_type> &birth_corners,
1656
- const typename std::vector<typename Summand<value_type>::filtration_type> &death_corners,
1657
- dimension_type dimension)
1658
- : birth_corners_(birth_corners), death_corners_(death_corners), distanceTo0_(-1), dimension_(dimension) {}
1659
-
1660
- template <typename value_type>
1661
- inline bool Summand<value_type>::contains(const filtration_type &x) const {
1662
- bool out = false;
1663
- for (const auto &birth : this->birth_corners_) { // checks if there exists a birth smaller than x
1664
- if (birth <= x) {
1665
- out = true;
1666
- break;
1667
- }
1668
- }
1669
- if (!out) return false;
1670
- out = false;
1671
- for (const auto &death : this->death_corners_) {
1672
- if (x <= death) {
1673
- out = true;
1674
- break;
1675
- }
1676
- }
1677
- return out;
1678
- }
1679
-
1680
- template <typename value_type>
1681
- inline Summand<value_type>::Summand(const typename Summand<value_type>::births_type &birth_corners,
1682
- const typename Summand<value_type>::deaths_type &death_corners,
1683
- dimension_type dimension)
1684
- : birth_corners_(birth_corners), death_corners_(death_corners), distanceTo0_(-1), dimension_(dimension) {}
1685
-
1686
- template <typename value_type>
1687
- inline value_type Summand<value_type>::get_interleaving(const Box<value_type> &box) {
1688
- _compute_interleaving(box);
1689
- return distanceTo0_;
1690
- }
1691
-
1692
- template <typename value_type>
1693
- inline value_type Summand<value_type>::get_interleaving() const {
1694
- return distanceTo0_;
1695
- }
1696
-
1697
- template <typename value_type>
1698
- inline value_type Summand<value_type>::get_local_weight(const filtration_type &x, const value_type delta) const {
1699
- bool rectangle = delta <= 0;
1700
-
1701
- // TODO: add assert to verify that x.size == birth.size/death.size
1702
- // if they are not infinite.
1703
-
1704
- filtration_type mini(x.num_parameters());
1705
- filtration_type maxi(x.num_parameters());
1706
-
1707
- // box on which to compute the local weight
1708
- for (unsigned int i = 0; i < x.size(); i++) {
1709
- mini[i] = delta <= 0 ? x[i] + delta : x[i] - delta;
1710
- maxi[i] = delta <= 0 ? x[i] - delta : x[i] + delta;
1711
- }
1712
-
1713
- // Pre-allocating
1714
- std::vector<filtration_type> birthList(birth_corners_.num_generators());
1715
- std::vector<filtration_type> deathList(death_corners_.num_generators());
1716
- unsigned int lastEntry = 0;
1717
- for (const filtration_type &birth : birth_corners_) {
1718
- if (birth <= maxi) {
1719
- unsigned int dim = std::max(birth.num_parameters(), mini.num_parameters());
1720
- filtration_type tmpBirth(dim);
1721
- for (unsigned int i = 0; i < dim; i++) {
1722
- auto birthi = birth.num_parameters() > i ? birth[i] : birth[0];
1723
- auto minii = mini.num_parameters() > i ? mini[i] : mini[0];
1724
- tmpBirth[i] = std::max(birthi, minii);
1725
- }
1726
-
1727
- birthList[lastEntry].swap(tmpBirth);
1728
- lastEntry++;
1729
- }
1730
- }
1731
- birthList.resize(lastEntry);
1732
-
1733
- // Thresholds birthlist & deathlist to B_inf(x,delta)
1734
- lastEntry = 0;
1735
- for (const filtration_type &death : death_corners_) {
1736
- if (death >= mini) {
1737
- unsigned int dim = std::max(death.num_parameters(), maxi.num_parameters());
1738
- filtration_type tmpDeath(dim);
1739
- for (unsigned int i = 0; i < dim; i++) {
1740
- auto deathi = death.num_parameters() > i ? death[i] : death[0];
1741
- auto maxii = maxi.num_parameters() > i ? maxi[i] : maxi[0];
1742
- tmpDeath[i] = std::min(deathi, maxii);
1743
- }
1744
-
1745
- deathList[lastEntry].swap(tmpDeath);
1746
- lastEntry++;
1747
- }
1748
- }
1749
- deathList.resize(lastEntry);
1750
- value_type local_weight = 0;
1751
- if (!rectangle) {
1752
- // Local weight is inteleaving to 0 of module restricted to the square
1753
- // #pragma omp parallel for reduction(std::max: local_weight)
1754
- Box<value_type> trivial_box;
1755
- for (const filtration_type &birth : birthList) {
1756
- if (birth.num_parameters() == 0) continue;
1757
- for (const filtration_type &death : deathList) {
1758
- if (death.num_parameters() > 0)
1759
- local_weight = std::max(local_weight,
1760
- _get_max_diagonal(birth,
1761
- death,
1762
- trivial_box)); // if box is empty, does not thredhold
1763
- // (already done before).
1764
- }
1765
- }
1766
- return local_weight / (2 * std::abs(delta));
1767
- } else {
1768
- // local weight is the volume of the largest rectangle in the restricted
1769
- // module #pragma omp parallel for reduction(std::max: local_weight)
1770
- for (const filtration_type &birth : birthList) {
1771
- if (birth.num_parameters() == 0) continue;
1772
- for (const filtration_type &death : deathList) {
1773
- if (death.num_parameters() > 0) local_weight = std::max(local_weight, _rectangle_volume(birth, death));
1774
- }
1775
- }
1776
- return local_weight / std::pow(2 * std::abs(delta), x.num_parameters());
1777
- }
1778
- }
1779
-
1780
- template <typename value_type>
1781
- inline std::tuple<int, int> Summand<value_type>::distance_idx_to_lower(const filtration_type &x) const {
1782
- value_type distance_to_lower = std::numeric_limits<value_type>::infinity();
1783
- int b_idx = -1; // argmin_b max_i (b-x)_x
1784
- int param = 0;
1785
- auto count = 0u;
1786
- for (const auto &birth : birth_corners_) {
1787
- value_type temp = -std::numeric_limits<value_type>::infinity(); // max_i(birth - x)_+
1788
- int temp_idx = 0;
1789
- for (auto i = 0u; i < birth.size(); ++i) {
1790
- auto plus = birth[i] - x[i];
1791
- if (plus > temp) {
1792
- temp_idx = i;
1793
- temp = plus;
1794
- }
1795
- }
1796
- if (temp < distance_to_lower) {
1797
- distance_to_lower = temp;
1798
- param = temp_idx;
1799
- b_idx = count;
1800
- }
1801
- ++count;
1802
- }
1803
- return {b_idx, param};
1804
- }
1805
-
1806
- template <typename value_type>
1807
- inline std::tuple<int, int> Summand<value_type>::distance_idx_to_upper(const filtration_type &x) const {
1808
- value_type distance_to_upper = std::numeric_limits<value_type>::infinity();
1809
- int d_idx = -1; // argmin_d max_i (x-death)
1810
- int param = 0;
1811
- auto count = 0u;
1812
- for (const auto &death : death_corners_) {
1813
- value_type temp = -std::numeric_limits<value_type>::infinity(); // max_i(death-x)_+
1814
- int temp_idx = 0;
1815
- for (auto i = 0u; i < death.size(); ++i) {
1816
- auto plus = x[i] - death[i];
1817
- if (plus > temp) {
1818
- temp_idx = i;
1819
- temp = plus;
1820
- }
1821
- }
1822
- if (temp < distance_to_upper) {
1823
- distance_to_upper = temp;
1824
- param = temp_idx;
1825
- d_idx = count;
1826
- }
1827
- ++count;
1828
- }
1829
- return {d_idx, param};
1830
- }
1831
-
1832
- template <typename value_type>
1833
- inline std::vector<int> Summand<value_type>::distance_idx_to(const filtration_type &x, bool full) const {
1834
- const auto &[a, b] = Summand::distance_idx_to_lower(x);
1835
- const auto &[c, d] = Summand::distance_idx_to_upper(x);
1836
- if (full) [[unlikely]]
1837
- return {a, b, c, d};
1838
- else {
1839
- return {a, c};
1840
- }
1841
- }
1842
-
1843
- template <typename value_type>
1844
- inline value_type Summand<value_type>::distance_to_lower(const filtration_type &x, bool negative) const {
1845
- value_type distance_to_lower = std::numeric_limits<value_type>::infinity();
1846
- for (const auto &birth : birth_corners_) {
1847
- value_type temp = negative ? -std::numeric_limits<value_type>::infinity() : 0;
1848
- for (auto i = 0u; i < birth.size(); ++i) {
1849
- temp = std::max(temp, birth[i] - x[i]);
1850
- }
1851
- distance_to_lower = std::min(distance_to_lower, temp);
1852
- }
1853
- return distance_to_lower;
1854
- }
1855
-
1856
- template <typename value_type>
1857
- inline value_type Summand<value_type>::distance_to_upper(const filtration_type &x, bool negative) const {
1858
- value_type distance_to_upper = std::numeric_limits<value_type>::infinity();
1859
- for (const auto &death : death_corners_) {
1860
- value_type temp = negative ? -std::numeric_limits<value_type>::infinity() : 0;
1861
- for (auto i = 0u; i < death.size(); ++i) {
1862
- temp = std::max(temp, x[i] - death[i]);
1863
- }
1864
- distance_to_upper = std::min(distance_to_upper, temp);
1865
- }
1866
- return distance_to_upper;
1867
- }
1868
-
1869
- template <typename value_type>
1870
- inline value_type Summand<value_type>::distance_to(const filtration_type &x, bool negative) const {
1871
- return std::max(Summand::distance_to_lower(x, negative), Summand::distance_to_upper(x, negative));
1872
- }
1873
-
1874
- template <typename value_type>
1875
- inline std::pair<value_type, value_type> Summand<value_type>::get_bar2(const Line<value_type> &l) const {
1876
- constexpr const bool verbose = false;
1877
- if constexpr (verbose)
1878
- std::cout << "Computing bar of this summand of dimension " << this->get_dimension() << std::endl;
1879
- value_type pushed_birth = std::numeric_limits<value_type>::infinity();
1880
- value_type pushed_death = -pushed_birth;
1881
- for (filtration_type birth : this->get_birth_list()) {
1882
- value_type pb = l.compute_forward_intersection(birth);
1883
- pushed_birth = std::min(pb, pushed_birth);
1884
- }
1885
- //
1886
- for (const filtration_type &death : this->get_death_list()) {
1887
- value_type pd = l.compute_backward_intersection(death);
1888
- pushed_death = std::max(pd, pushed_death);
1889
- }
1890
-
1891
- if (!(pushed_birth <= pushed_death)) {
1892
- if constexpr (verbose) std::cout << "Birth <!= Death ! Ignoring this value" << std::endl;
1893
- return {inf, inf};
1894
- }
1895
- if constexpr (verbose) {
1896
- std::cout << "Final values" << pushed_birth << " ----- " << pushed_death << std::endl;
1897
- }
1898
- return {pushed_birth, pushed_death};
1899
- }
1900
-
1901
- template <typename value_type>
1902
- inline std::pair<typename Summand<value_type>::filtration_type, typename Summand<value_type>::filtration_type>
1903
- Summand<value_type>::get_bar(const Line<value_type> &l) const {
1904
- constexpr const bool verbose = false;
1905
- if constexpr (verbose)
1906
- std::cout << "Computing bar of this summand of dimension " << this->get_dimension() << std::endl;
1907
- filtration_type pushed_birth = std::numeric_limits<filtration_type>::infinity();
1908
- filtration_type pushed_death = std::numeric_limits<filtration_type>::minus_infinity();
1909
- for (filtration_type birth : this->get_birth_list()) {
1910
- filtration_type pb = l[l.compute_forward_intersection(birth)];
1911
- if constexpr (verbose)
1912
- std::cout << "Updating birth " << pushed_birth << " with " << pb << " pushed at " << birth << " "
1913
- << pushed_birth.is_plus_inf();
1914
- if ((pb <= pushed_birth) || pushed_birth.is_plus_inf()) {
1915
- pushed_birth.swap(pb);
1916
- if constexpr (verbose) std::cout << " swapped !";
1917
- }
1918
- if constexpr (verbose) std::cout << std::endl;
1919
- }
1920
- //
1921
- for (const filtration_type &death : this->get_death_list()) {
1922
- filtration_type pd = l[l.compute_backward_intersection(death)];
1923
- if constexpr (verbose)
1924
- std::cout << "Updating death " << pushed_death << " with " << pd << " pushed at " << death << " "
1925
- << pushed_death.is_minus_inf() << pushed_death[0];
1926
- if ((pd >= pushed_death) || pushed_death.is_minus_inf()) {
1927
- pushed_death.swap(pd);
1928
- if constexpr (verbose) std::cout << " swapped !";
1929
- }
1930
- if constexpr (verbose) std::cout << std::endl;
1931
- }
1932
-
1933
- if (!(pushed_birth <= pushed_death)) {
1934
- if constexpr (verbose) std::cout << "Birth <!= Death ! Ignoring this value" << std::endl;
1935
- return {std::numeric_limits<filtration_type>::infinity(), std::numeric_limits<filtration_type>::infinity()};
1936
- }
1937
- if constexpr (verbose) {
1938
- std::cout << "Final values" << pushed_birth << " ----- " << pushed_death << std::endl;
1939
- }
1940
- return {pushed_birth, pushed_death};
1941
- }
1942
-
1943
- /**
1944
- * @brief Adds the bar @p bar to the indicator module @p summand if @p bar
1945
- * is non-trivial (ie. not reduced to a point or, if @p threshold is true,
1946
- * its thresholded version should not be reduced to a point) .
1947
- *
1948
- * @param bar p_bar: to add to the support of the summand
1949
- * @param summand p_summand: indicator module which is being completed
1950
- * @param basepoint p_basepoint: basepoint of the line of the bar
1951
- * @param birth p_birth: birth container (for memory optimization purposes).
1952
- * Has to be of the size @p basepoint.size()+1.
1953
- * @param death p_death: death container. Same purpose as @p birth but for
1954
- * deathpoint.
1955
- * @param threshold p_threshold: If true, will threshold the bar with @p box.
1956
- * @param box p_box: Only useful if @p threshold is set to true.
1957
- */
1958
-
1959
- template <typename value_type>
1960
- inline void Summand<value_type>::add_bar(value_type baseBirth,
1961
- value_type baseDeath,
1962
- const filtration_type &basepoint,
1963
- filtration_type &birth,
1964
- filtration_type &death,
1965
- const bool threshold,
1966
- const Box<value_type> &box) {
1967
- // bar is trivial in that case
1968
- if (baseBirth >= baseDeath) return;
1969
- // #pragma omp simd
1970
- // for (unsigned int j = 0; j < birth.size() - 1; j++)
1971
- // {
1972
- // birth[j] = basepoint[j] + baseBirth;
1973
- // death[j] = basepoint[j] + baseDeath;
1974
- // }
1975
- // birth.back() = baseBirth;
1976
- // death.back() = baseDeath;
1977
-
1978
- /* #pragma omp simd */
1979
- for (unsigned int j = 0; j < birth.size() - 1; j++) {
1980
- value_type temp = basepoint[j] + baseBirth;
1981
- // The box is assumed to contain all of the filtration values, if its
1982
- // outside, its inf.
1983
- birth[j] = temp < box.get_lower_corner()[j] ? negInf : temp;
1984
- temp = basepoint[j] + baseDeath;
1985
- death[j] = temp > box.get_upper_corner()[j] ? inf : temp;
1986
- }
1987
- birth.back() = baseBirth < box.get_lower_corner().back() ? negInf : baseBirth;
1988
- death.back() = baseDeath > box.get_upper_corner().back() ? inf : baseDeath;
1989
-
1990
- if (threshold) {
1991
- // std::cout << box;
1992
- threshold_down(birth, box, basepoint);
1993
- threshold_up(death, box, basepoint);
1994
- }
1995
- _add_birth(birth);
1996
- _add_death(death);
1997
- }
1998
-
1999
- template <typename value_type>
2000
- inline void Summand<value_type>::add_bar(const filtration_type &birth, const filtration_type &death) {
2001
- _add_birth(birth);
2002
- _add_death(death);
2003
- }
2004
-
2005
- template <typename value_type>
2006
- inline void Summand<value_type>::add_bar(const filtration_type &basepoint,
2007
- value_type birth,
2008
- value_type death,
2009
- const Box<value_type> &box) {
2010
- constexpr const bool verbose = false;
2011
- if (birth >= death) return;
2012
- if constexpr (verbose) {
2013
- std::cout << "Bar : " << basepoint + birth << "--" << basepoint + death << std::endl;
2014
- }
2015
- auto inf = std::numeric_limits<value_type>::infinity();
2016
- auto container = basepoint + birth;
2017
- for (auto i = 0u; i < container.size(); i++) {
2018
- if (container[i] < box.get_lower_corner()[i]) container[i] = -inf;
2019
- }
2020
- _add_birth(container);
2021
- container = basepoint + death;
2022
- for (auto i = 0u; i < container.size(); i++) {
2023
- if (container[i] > box.get_upper_corner()[i]) container[i] = inf;
2024
- }
2025
- _add_death(container);
2026
- }
2027
-
2028
- template <typename value_type>
2029
- inline const std::vector<typename Summand<value_type>::filtration_type> &Summand<value_type>::get_birth_list() const {
2030
- return birth_corners_.get_underlying_container();
2031
- }
2032
-
2033
- template <typename value_type>
2034
- inline const std::vector<typename Summand<value_type>::filtration_type> &Summand<value_type>::get_death_list() const {
2035
- return death_corners_.get_underlying_container();
2036
- }
2037
-
2038
- template <typename value_type>
2039
- const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &Summand<value_type>::get_upset() const {
2040
- return birth_corners_;
2041
- }
2042
-
2043
- template <typename value_type>
2044
- const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &Summand<value_type>::get_downset() const {
2045
- return death_corners_;
2046
- };
2047
-
2048
- template <typename value_type>
2049
- inline void Summand<value_type>::clean() {
2050
- // birth_corners_.erase(
2051
- // std::remove_if(birth_corners_.begin(), birth_corners_.end(),
2052
- // [](const std::vector<value_type> &bp) {
2053
- // return std::any_of(
2054
- // bp.begin(), bp.end(),
2055
- // [](float value) { return !std::isfinite(value); });
2056
- // }),
2057
- // birth_corners_.end());
2058
- // TODO : clean
2059
- }
2060
-
2061
- template <typename value_type>
2062
- inline void Summand<value_type>::complete_birth(const value_type precision) {
2063
- if (!birth_corners_.is_finite()) return;
2064
-
2065
- for (std::size_t i = 0; i < birth_corners_.num_generators(); i++) {
2066
- for (std::size_t j = i + 1; j < birth_corners_.num_generators(); j++) {
2067
- value_type dinf = d_inf(birth_corners_[i], birth_corners_[j]);
2068
- if (dinf < 1.1 * precision) {
2069
- _factorize_min(birth_corners_[i], birth_corners_[j]);
2070
- birth_corners_[j].clear();
2071
- }
2072
- }
2073
- }
2074
- _clean(birth_corners_);
2075
- }
2076
-
2077
- template <typename value_type>
2078
- inline void Summand<value_type>::complete_death(const value_type precision) {
2079
- if (!death_corners_.is_finite()) return;
2080
-
2081
- for (std::size_t i = 0; i < death_corners_.num_generators(); i++) {
2082
- for (std::size_t j = i + 1; j < death_corners_.num_generators(); j++) {
2083
- value_type d = d_inf(death_corners_[i], death_corners_[j]);
2084
- if (d < 1.1 * precision) {
2085
- _factorize_max(death_corners_[i], death_corners_[j]);
2086
- death_corners_[j].clear();
2087
- }
2088
- }
2089
- }
2090
- _clean(death_corners_);
2091
- }
2092
-
2093
- template <typename value_type>
2094
- inline dimension_type Summand<value_type>::get_dimension() const {
2095
- return dimension_;
2096
- }
2097
-
2098
- template <typename value_type>
2099
- inline value_type Summand<value_type>::get_landscape_value(const std::vector<value_type> &x) const {
2100
- value_type out = 0;
2101
- Box<value_type> trivial_box;
2102
- for (const filtration_type &b : this->birth_corners_) {
2103
- for (const filtration_type &d : this->death_corners_) {
2104
- value_type value =
2105
- std::min(this->_get_max_diagonal(b, x, trivial_box), this->_get_max_diagonal(x, d, trivial_box));
2106
- out = std::max(out, value);
2107
- }
2108
- }
2109
- return out;
2110
- }
2111
-
2112
- template <typename value_type>
2113
- inline void Summand<value_type>::set_dimension(dimension_type dimension) {
2114
- dimension_ = dimension;
2115
- }
2116
-
2117
- template <typename value_type>
2118
- inline void Summand<value_type>::_compute_interleaving(const Box<value_type> &box) {
2119
- distanceTo0_ = 0;
2120
- /* #pragma omp parallel for reduction(max : distanceTo0_) */
2121
- for (const std::vector<value_type> &birth : birth_corners_) {
2122
- for (const std::vector<value_type> &death : death_corners_) {
2123
- distanceTo0_ = std::max(distanceTo0_, _get_max_diagonal(birth, death, box));
2124
- }
2125
- }
2126
- }
2127
-
2128
- /**
2129
- * @brief Adds @p birth to the summand's @p birth_list if it is not induced
2130
- * from the @p birth_list (ie. not comparable or smaller than another birth),
2131
- * and removes unnecessary birthpoints (ie. birthpoints that are induced
2132
- * by @p birth).
2133
- *
2134
- * @param birth_list p_birth_list: birthpoint list of a summand
2135
- * @param birth p_birth: birth to add to the summand
2136
- */
2137
-
2138
- template <typename value_type>
2139
- inline void Summand<value_type>::_add_birth(const filtration_type &birth) {
2140
- birth_corners_.add_generator(birth);
2141
- return;
2142
-
2143
- // // TODO : DEPRECATE THIS OLD CODE
2144
- // if (birth_corners_.empty()) {
2145
- // birth_corners_.push_back(birth);
2146
- // return;
2147
- // }
2148
-
2149
- // for (const auto &current_birth : birth_corners_) {
2150
- // if (birth >= current_birth) {
2151
- // return;
2152
- // }
2153
- // }
2154
- // // this birth value is useful, we can now remove useless other filtrations
2155
- // for (auto &current_birth : birth_corners_) {
2156
- // if ((!current_birth.empty()) && (birth <= current_birth)) {
2157
- // current_birth.clear();
2158
- // }
2159
- // }
2160
-
2161
- // _clean(birth_corners_);
2162
- // birth_corners_.push_back(birth);
2163
- }
2164
-
2165
- /**
2166
- * @brief Adds @p death to the summand's @p death_list if it is not induced
2167
- * from the @p death_list (ie. not comparable or greater than another death),
2168
- * and removes unnecessary deathpoints (ie. deathpoints that are induced
2169
- * by @p death)
2170
- *
2171
- * @param death_list p_death_list: List of deathpoints of a summand
2172
- * @param death p_death: deathpoint to add to this list
2173
- */
2174
-
2175
- template <typename value_type>
2176
- inline void Summand<value_type>::_add_death(const filtration_type &death) {
2177
- death_corners_.add_generator(death);
2178
- return;
2179
- // // TODO: Deprecate this old code
2180
- // if (death_corners_.empty()) {
2181
- // death_corners_.push_back(death);
2182
- // return;
2183
- // }
2184
-
2185
- // for (const auto &current_death : death_corners_) {
2186
- // if (death <= current_death) {
2187
- // return;
2188
- // }
2189
- // }
2190
- // // this death value is useful, we can now remove useless other filtrations
2191
- // for (auto &current_death : death_corners_) {
2192
- // if (!current_death.empty() && (death >= current_death)) {
2193
- // current_death.clear();
2194
- // }
2195
- // }
2196
- // _clean(death_corners_);
2197
- // death_corners_.push_back(death);
2198
- }
2199
-
2200
- template <typename value_type>
2201
- inline value_type Summand<value_type>::_get_max_diagonal(const filtration_type &birth,
2202
- const filtration_type &death,
2203
- const Box<value_type> &box) const {
2204
- // assumes birth and death to be never NaN
2205
- if constexpr (Debug::debug)
2206
- assert(!birth.is_finite || !death.is_finite || birth.size() == death.size() && "Inputs must be of the same size !");
2207
-
2208
- value_type s = inf;
2209
- bool threshold_flag = !box.is_trivial();
2210
- if (threshold_flag) {
2211
- unsigned int dim = std::max(birth.size(), box.dimension());
2212
- for (unsigned int i = 0; i < dim; ++i) {
2213
- value_type max_i = box.get_upper_corner().size() > i ? box.get_upper_corner()[i] : inf;
2214
- value_type min_i = box.get_lower_corner().size() > i ? box.get_lower_corner()[i] : negInf;
2215
- value_type t_death = death.is_plus_inf() ? max_i : (death.is_minus_inf() ? -inf : std::min(death[i], max_i));
2216
- value_type t_birth = birth.is_plus_inf() ? inf : (birth.is_minus_inf() ? min_i : std::max(birth[i], min_i));
2217
- s = std::min(s, t_death - t_birth);
2218
- }
2219
- } else {
2220
- unsigned int dim = std::max(birth.size(), death.size());
2221
- for (unsigned int i = 0; i < dim; i++) {
2222
- // if they don't have the same size, then one of them has to (+/-)infinite.
2223
- value_type t_death = death.size() > i ? death[i] : death[0]; // assumes death is never empty
2224
- value_type t_birth = birth.size() > i ? birth[i] : birth[0]; // assumes birth is never empty
2225
- s = std::min(s, t_death - t_birth);
2226
- }
2227
- }
2228
-
2229
- return s;
2230
- }
2231
-
2232
- template <typename value_type>
2233
- inline value_type Summand<value_type>::_rectangle_volume(const filtration_type &a, const filtration_type &b) const {
2234
- if constexpr (Debug::debug) assert(a.size() == b.size() && "Inputs must be of the same size !");
2235
- value_type s = b[0] - a[0];
2236
- for (unsigned int i = 1; i < a.size(); i++) {
2237
- s = s * (b[i] - a[i]);
2238
- }
2239
- return s;
2240
- }
2241
-
2242
- template <typename value_type>
2243
- inline value_type Summand<value_type>::d_inf(const filtration_type &a, const filtration_type &b) const {
2244
- if (a.empty() || b.empty() || a.size() != b.size()) return inf;
2245
-
2246
- value_type d = std::abs(a[0] - b[0]);
2247
- for (unsigned int i = 1; i < a.size(); i++) d = std::max(d, std::abs(a[i] - b[i]));
2248
-
2249
- return d;
2250
- }
2251
-
2252
- template <typename value_type>
2253
- inline void Summand<value_type>::_factorize_min(filtration_type &a, const filtration_type &b) {
2254
- /* if (Debug::debug && (a.empty() || b.empty())) */
2255
- /* { */
2256
- /* std::cout << "Empty corners ??\n"; */
2257
- /* return; */
2258
- /* } */
2259
-
2260
- for (unsigned int i = 0; i < std::min(b.size(), a.size()); i++) a[i] = std::min(a[i], b[i]);
2261
- }
2262
-
2263
- template <typename value_type>
2264
- inline void Summand<value_type>::_factorize_max(filtration_type &a, const filtration_type &b) {
2265
- /* if (Debug::debug && (a.empty() || b.empty())) */
2266
- /* { */
2267
- /* std::cout << "Empty corners ??\n"; */
2268
- /* return; */
2269
- /* } */
2270
-
2271
- for (unsigned int i = 0; i < std::min(b.size(), a.size()); i++) a[i] = std::max(a[i], b[i]);
2272
- }
2273
-
2274
- /**
2275
- * @brief Cleans empty entries of a corner list
2276
- *
2277
- * @param list corner list to clean
2278
- * @param keep_sort If true, will keep the order of the corners,
2279
- * with a computational overhead. Defaults to false.
2280
- */
2281
- // WARNING Does permute the output.
2282
-
2283
- template <typename value_type>
2284
- inline void Summand<value_type>::_clean(std::vector<filtration_type> &list, bool keep_inf) {
2285
- list.erase(std::remove_if(list.begin(),
2286
- list.end(),
2287
- [keep_inf](filtration_type &a) {
2288
- return a.empty() || ((!keep_inf) && (a.is_plus_inf() || a.is_minus_inf()));
2289
- }),
2290
- list.end());
2291
- }
2292
-
2293
- }}} // namespace Gudhi::multiparameter::mma
2294
-
2295
- #endif // APPR
1
+
2
+ /* This file is part of the MMA Library -
3
+ * https://gitlab.inria.fr/dloiseau/multipers - which is released under MIT. See
4
+ * file LICENSE for full license details. Author(s): David Loiseaux
5
+ *
6
+ * Copyright (C) 2021 Inria
7
+ *
8
+ * Modification(s):
9
+ * - 2022/03 Hannah Schreiber: Integration of the new Vineyard_persistence
10
+ * class, renaming and cleanup.
11
+ * - 2022/05 Hannah Schreiber: Addition of Summand class and Module class.
12
+ */
13
+ /**
14
+ * @file approximation.h
15
+ * @author David Loiseaux, Hannah Schreiber
16
+ * @brief Contains the functions related to the approximation of n-modules.
17
+ */
18
+
19
+ #ifndef APPROXIMATION_H_INCLUDED
20
+ #define APPROXIMATION_H_INCLUDED
21
+
22
+ #include <algorithm>
23
+ #include <cmath>
24
+ #include <cstddef>
25
+ #include <cstdint>
26
+ #include <iostream>
27
+ #include <iterator>
28
+ #include <limits>
29
+ #include <oneapi/tbb/parallel_for.h>
30
+ #include <string>
31
+ #include <utility>
32
+ #include <vector>
33
+
34
+ #include "utilities.h"
35
+
36
+ #include "debug.h"
37
+ #include <Persistence_slices_interface.h>
38
+ #include <gudhi/One_critical_filtration.h>
39
+ #include <gudhi/Multi_critical_filtration.h>
40
+ #include <gudhi/Multi_persistence/Box.h>
41
+ #include <gudhi/Multi_persistence/Line.h>
42
+ #include <tbb/parallel_for.h>
43
+ #include <tensor/tensor.h>
44
+
45
+ namespace Gudhi {
46
+ namespace multiparameter {
47
+ namespace mma {
48
+
49
+ using Debug::Timer;
50
+ using Gudhi::multi_persistence::Box;
51
+ using Gudhi::multi_persistence::Line;
52
+ template <typename T>
53
+ class Module;
54
+ template <typename T>
55
+ class Summand;
56
+
57
+ void threshold_filters_list(std::vector<value_type> &filtersList, const Box<value_type> &box);
58
+
59
+ template <typename value_type>
60
+ class Module {
61
+ public:
62
+ using filtration_type = Gudhi::multi_filtration::One_critical_filtration<value_type>;
63
+ using module_type = std::vector<Summand<value_type>>;
64
+ using image_type = std::vector<std::vector<value_type>>;
65
+ using get_2dpixel_value_function_type = std::function<value_type(const typename module_type::const_iterator,
66
+ const typename module_type::const_iterator,
67
+ value_type,
68
+ value_type)>;
69
+ using get_pixel_value_function_type = std::function<value_type(const typename module_type::const_iterator,
70
+ const typename module_type::const_iterator,
71
+ std::vector<value_type> &)>;
72
+
73
+ Module();
74
+ Module(Box<value_type> &box);
75
+
76
+ void resize(unsigned int size);
77
+ Summand<value_type> &at(unsigned int index);
78
+ Summand<value_type> &operator[](size_t index);
79
+
80
+ const Summand<value_type> &operator[](const size_t index) const;
81
+ template <class Barcode>
82
+ void add_barcode(const Barcode &barcode);
83
+ void add_barcode(const Line<value_type> &line,
84
+ const std::vector<std::pair<int, std::pair<value_type, value_type>>> &barcode,
85
+ const bool threshold);
86
+ void add_barcode(const Line<value_type> &line,
87
+ const std::vector<std::pair<value_type, value_type>> &barcode,
88
+ const bool threshold);
89
+ typename module_type::iterator begin();
90
+ typename module_type::iterator end();
91
+
92
+ void clean();
93
+ void fill(const value_type precision);
94
+
95
+ std::vector<image_type> get_vectorization(const value_type delta,
96
+ const value_type p,
97
+ const bool normalize,
98
+ const Gudhi::multi_persistence::Box<value_type> &box,
99
+ unsigned int horizontalResolution,
100
+ unsigned int verticalResolution);
101
+ std::vector<image_type> get_vectorization(unsigned int horizontalResolution,
102
+ unsigned int verticalResolution,
103
+ get_2dpixel_value_function_type get_pixel_value) const;
104
+ image_type get_vectorization_in_dimension(const dimension_type dimension,
105
+ const value_type delta,
106
+ const value_type p,
107
+ const bool normalize,
108
+ const Gudhi::multi_persistence::Box<value_type> &box,
109
+ unsigned int horizontalResolution,
110
+ unsigned int verticalResolution);
111
+ image_type get_vectorization_in_dimension(const dimension_type dimension,
112
+ unsigned int horizontalResolution,
113
+ unsigned int verticalResolution,
114
+ get_2dpixel_value_function_type get_pixel_value) const;
115
+ std::vector<value_type> get_landscape_values(const std::vector<value_type> &x, const dimension_type dimension) const;
116
+ image_type get_landscape(const dimension_type dimension,
117
+ const unsigned int k,
118
+ const Box<value_type> &box,
119
+ const std::vector<unsigned int> &resolution) const;
120
+ std::vector<image_type> get_landscapes(const dimension_type dimension,
121
+ const std::vector<unsigned int> ks,
122
+ const Box<value_type> &box,
123
+ const std::vector<unsigned int> &resolution) const;
124
+ void add_summand(Summand<value_type> summand, int degree = -1);
125
+ Box<value_type> get_box() const;
126
+ void set_box(Box<value_type> box);
127
+ unsigned int size() const;
128
+ void infer_box(std::vector<filtration_type> &filters_list);
129
+ dimension_type get_dimension() const;
130
+ module_type get_summands_of_dimension(const int dimension) const;
131
+ std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>>
132
+ get_corners_of_dimension(const int dimension) const;
133
+ MultiDiagram<filtration_type, value_type> get_barcode(const Line<value_type> &l,
134
+ const dimension_type dimension = -1,
135
+ const bool threshold = false) const;
136
+ std::vector<std::vector<std::pair<value_type, value_type>>> get_barcode2(const Line<value_type> &l,
137
+ const dimension_type dimension) const;
138
+ std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> get_barcodes2(
139
+ const std::vector<Line<value_type>> &lines,
140
+ const dimension_type dimension = -1) const;
141
+ MultiDiagrams<filtration_type, value_type> get_barcodes(const std::vector<Line<value_type>> &lines,
142
+ const dimension_type dimension = -1,
143
+ const bool threshold = false) const;
144
+ MultiDiagrams<filtration_type, value_type> get_barcodes(const std::vector<filtration_type> &basepoints,
145
+ const dimension_type dimension = -1,
146
+ const bool threshold = false) const;
147
+ std::vector<int> euler_curve(const std::vector<filtration_type> &points) const;
148
+
149
+ inline Box<value_type> get_bounds() const;
150
+ inline void rescale(const std::vector<value_type> &rescale_factors, int degree);
151
+ inline void translate(const std::vector<value_type> &translation, int degree);
152
+
153
+ std::vector<std::vector<value_type>> compute_pixels(const std::vector<std::vector<value_type>> &coordinates,
154
+ const std::vector<int> &degrees,
155
+ const Box<value_type> &box = {},
156
+ const value_type delta = 0.1,
157
+ const value_type p = 1,
158
+ const bool normalize = true,
159
+ const int n_jobs = 0);
160
+
161
+ std::vector<value_type> get_interleavings(const Box<value_type> &box);
162
+ using distance_to_idx_type = std::vector<std::vector<int>>;
163
+ distance_to_idx_type compute_distance_idx_to(const std::vector<value_type> &pt, bool full) const;
164
+ std::vector<value_type> compute_distance_to(const std::vector<value_type> &pt, bool negative) const;
165
+ std::vector<std::vector<value_type>> compute_distances_to(const std::vector<std::vector<value_type>> &pt,
166
+ bool negative,
167
+ int n_jobs) const;
168
+ template <typename dtype = value_type, typename indices_type = int32_t>
169
+ void inline compute_distances_to(dtype *data_ptr,
170
+ const std::vector<std::vector<value_type>> &pts,
171
+ bool negative,
172
+ int n_jobs) const;
173
+ using distances_to_idx_type = std::vector<distance_to_idx_type>;
174
+ distances_to_idx_type compute_distances_idx_to(const std::vector<std::vector<value_type>> &pt,
175
+ bool full,
176
+ int n_jobs) const;
177
+ std::vector<value_type> compute_pixels_of_degree(const typename module_type::iterator start,
178
+ const typename module_type::iterator end,
179
+ const value_type delta,
180
+ const value_type p,
181
+ const bool normalize,
182
+ const Box<value_type> &box,
183
+ const std::vector<std::vector<value_type>> &coordinates,
184
+ const int n_jobs = 0);
185
+
186
+ using idx_dump_type =
187
+ std::vector<std::vector<std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>>>>;
188
+ idx_dump_type to_idx(const std::vector<std::vector<value_type>> &grid) const;
189
+ Module<int64_t> grid_squeeze(const std::vector<std::vector<value_type>> &grid) const;
190
+
191
+ std::vector<std::vector<std::vector<int>>> to_flat_idx(const std::vector<std::vector<value_type>> &grid) const;
192
+
193
+ std::vector<int> inline get_degree_splits() const;
194
+
195
+ private:
196
+ module_type module_;
197
+ Box<value_type> box_;
198
+ void _compute_2D_image(image_type &image,
199
+ const typename module_type::iterator start,
200
+ const typename module_type::iterator end,
201
+ const value_type delta = 0.1,
202
+ const value_type p = 1,
203
+ const bool normalize = true,
204
+ const Box<value_type> &box = Box<value_type>(),
205
+ const unsigned int horizontalResolution = 100,
206
+ const unsigned int verticalResolution = 100);
207
+ void _compute_2D_image(image_type &image,
208
+ const typename module_type::const_iterator start,
209
+ const typename module_type::const_iterator end,
210
+ unsigned int horizontalResolution,
211
+ unsigned int verticalResolution,
212
+ get_2dpixel_value_function_type get_pixel_value) const;
213
+
214
+ value_type _get_pixel_value(const typename module_type::iterator start,
215
+ const typename module_type::iterator end,
216
+ const filtration_type x,
217
+ const value_type delta,
218
+ const value_type p,
219
+ const bool normalize,
220
+ const value_type moduleWeight) const;
221
+
222
+ void _add_bar_with_threshold(const Line<value_type> &line,
223
+ const std::pair<value_type, value_type> &bar,
224
+ const bool threshold_in,
225
+ Summand<value_type> &summand);
226
+ };
227
+
228
+ template <typename value_type>
229
+ class Summand {
230
+ public:
231
+ using births_type = Gudhi::multi_filtration::Multi_critical_filtration<value_type, false>;
232
+ using deaths_type = Gudhi::multi_filtration::Multi_critical_filtration<value_type, true>;
233
+ using filtration_type = typename births_type::Generator; // same for death
234
+ using dimension_type = int;
235
+ Summand();
236
+ Summand(const births_type &birth_corners, const deaths_type &death_corners, dimension_type dimension);
237
+ Summand(const std::vector<filtration_type> &birth_corners,
238
+ const std::vector<filtration_type> &death_corners,
239
+ dimension_type dimension);
240
+
241
+ value_type get_interleaving() const;
242
+ value_type get_interleaving(const Box<value_type> &box);
243
+ value_type get_local_weight(const filtration_type &x, const value_type delta) const;
244
+
245
+ value_type distance_to_upper(const filtration_type &x, bool negative) const;
246
+ value_type distance_to_lower(const filtration_type &x, bool negative) const;
247
+ value_type distance_to(const filtration_type &x, bool negative) const;
248
+ std::tuple<int, int> distance_idx_to_upper(const filtration_type &x) const;
249
+ std::tuple<int, int> distance_idx_to_lower(const filtration_type &x) const;
250
+ std::vector<int> distance_idx_to(const filtration_type &x, bool full) const;
251
+ std::pair<filtration_type, filtration_type> get_bar(const Line<value_type> &line) const;
252
+ std::pair<value_type, value_type> get_bar2(const Line<value_type> &l) const;
253
+ void add_bar(value_type baseBirth,
254
+ value_type baseDeath,
255
+ const filtration_type &basepoint,
256
+ filtration_type &birth,
257
+ filtration_type &death,
258
+ const bool threshold,
259
+ const Box<value_type> &box);
260
+ void add_bar(const filtration_type &birth, const filtration_type &death);
261
+ void add_bar(const filtration_type &basepoint, value_type birth, value_type death, const Box<value_type> &);
262
+
263
+ const std::vector<filtration_type> &get_birth_list() const;
264
+ const std::vector<filtration_type> &get_death_list() const;
265
+ const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &get_upset() const;
266
+ const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &get_downset() const;
267
+ void clean();
268
+
269
+ void complete_birth(const value_type precision);
270
+ void complete_death(const value_type precision);
271
+
272
+ dimension_type get_dimension() const;
273
+ void set_dimension(dimension_type dimension);
274
+
275
+ value_type get_landscape_value(const std::vector<value_type> &x) const;
276
+
277
+ friend void swap(Summand &sum1, Summand &sum2) {
278
+ std::swap(sum1.birth_corners_, sum2.birth_corners_);
279
+ std::swap(sum1.death_corners_, sum2.death_corners_);
280
+ std::swap(sum1.distanceTo0_, sum2.distanceTo0_);
281
+ // std::swap(sum1.updateDistance_, sum2.updateDistance_);
282
+ };
283
+
284
+ bool contains(const filtration_type &x) const;
285
+
286
+ inline Box<value_type> get_bounds() const {
287
+ if (birth_corners_.num_generators() == 0) return Box<value_type>();
288
+ auto dimension = birth_corners_.num_parameters();
289
+ filtration_type m(dimension, std::numeric_limits<value_type>::infinity());
290
+ filtration_type M(dimension, -std::numeric_limits<value_type>::infinity());
291
+ for (const auto &corner : birth_corners_) {
292
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
293
+ m[parameter] = std::min(m[parameter], corner[parameter]);
294
+ }
295
+ }
296
+ for (const auto &corner : death_corners_) {
297
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
298
+ auto corner_i = corner[parameter];
299
+ if (corner_i != std::numeric_limits<value_type>::infinity())
300
+ M[parameter] = std::max(M[parameter], corner[parameter]);
301
+ }
302
+ }
303
+ return Box(m, M);
304
+ }
305
+
306
+ inline void rescale(const std::vector<value_type> &rescale_factors) {
307
+ if (birth_corners_.num_generators() == 0) return;
308
+ auto dimension = birth_corners_.num_parameters();
309
+ for (auto &corner : birth_corners_) {
310
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
311
+ corner[parameter] *= rescale_factors.at(parameter);
312
+ }
313
+ }
314
+ for (auto &corner : death_corners_) {
315
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
316
+ corner[parameter] *= rescale_factors.at(parameter);
317
+ }
318
+ }
319
+ }
320
+
321
+ inline void translate(const std::vector<value_type> &translation) {
322
+ if (birth_corners_.num_generators() == 0) return;
323
+ auto dimension = birth_corners_.num_parameters();
324
+ for (auto &corner : birth_corners_) {
325
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
326
+ corner[parameter] += translation.at(parameter);
327
+ }
328
+ }
329
+ for (auto &corner : death_corners_) {
330
+ for (auto parameter = 0u; parameter < dimension; parameter++) {
331
+ corner[parameter] += translation.at(parameter);
332
+ }
333
+ }
334
+ }
335
+
336
+ inline Summand<int64_t> grid_squeeze(const std::vector<std::vector<value_type>> &grid) const;
337
+
338
+ private:
339
+ Gudhi::multi_filtration::Multi_critical_filtration<value_type, false>
340
+ birth_corners_; // TODO : use Multi_critical_filtration
341
+ Gudhi::multi_filtration::Multi_critical_filtration<value_type, true> death_corners_;
342
+ value_type distanceTo0_;
343
+ dimension_type dimension_;
344
+
345
+ void _compute_interleaving(const Box<value_type> &box);
346
+ void _add_birth(const filtration_type &birth);
347
+ void _add_death(const filtration_type &death);
348
+ value_type _rectangle_volume(const filtration_type &a, const filtration_type &b) const;
349
+ value_type _get_max_diagonal(const filtration_type &a, const filtration_type &b, const Box<value_type> &box) const;
350
+ value_type d_inf(const filtration_type &a, const filtration_type &b) const;
351
+ void _factorize_min(filtration_type &a, const filtration_type &b);
352
+ void _factorize_max(filtration_type &a, const filtration_type &b);
353
+ static void _clean(std::vector<filtration_type> &list, bool keep_inf = true);
354
+
355
+ static inline void _clean(births_type &list, bool keep_inf = true) { list.remove_empty_generators(keep_inf); }
356
+
357
+ static inline void _clean(deaths_type &list, bool keep_inf = true) { list.remove_empty_generators(keep_inf); }
358
+ };
359
+
360
+ inline void threshold_filters_list(std::vector<filtration_type> &filtersList, const Box<value_type> &box) {
361
+ return;
362
+ for (unsigned int i = 0; i < filtersList.size(); i++) {
363
+ for (value_type &value : filtersList[i]) {
364
+ value = std::min(std::max(value, box.get_lower_corner()[i]), box.get_upper_corner()[i]);
365
+ }
366
+ }
367
+ }
368
+
369
+ template <class Filtration_value, int axis = 0, bool sign = true>
370
+ class LineIterator {
371
+ public:
372
+ using value_type = typename Filtration_value::value_type;
373
+ LineIterator(const Filtration_value &basepoint,
374
+ const Filtration_value &direction,
375
+ value_type precision,
376
+ int num_iterations)
377
+ : precision(precision), remaining_iterations(num_iterations), current_line(std::move(basepoint), direction) {};
378
+
379
+ inline LineIterator<Filtration_value, axis, sign> &operator++() {
380
+ //
381
+ auto &basepoint = current_line.base_point();
382
+ if (this->is_finished()) return *this;
383
+ // If we didn't reached the end, go to the next line
384
+ basepoint[axis] += sign ? precision : -precision;
385
+ --remaining_iterations;
386
+ return *this;
387
+ }
388
+
389
+ inline const Line<value_type> &operator*() const { return current_line; }
390
+
391
+ inline LineIterator<Filtration_value, axis, sign> &next(std::size_t i) {
392
+ auto &basepoint = current_line.base_point();
393
+ if (this->is_finished()) return *this;
394
+ // If we didn't reached the end, go to the next line
395
+ basepoint[i] += sign ? precision : -precision;
396
+ --remaining_iterations;
397
+ return *this;
398
+ }
399
+
400
+ inline bool is_finished() const { return remaining_iterations <= 0; }
401
+
402
+ private:
403
+ const value_type precision;
404
+ int remaining_iterations;
405
+ Line<value_type> current_line;
406
+ };
407
+
408
+ template <class Filtration_value, int axis_ = 0, bool sign = true, class Slicer>
409
+ inline void __add_vineyard_trajectory_to_module(Module<typename Filtration_value::value_type> &module,
410
+ Slicer &&slicer,
411
+ LineIterator<Filtration_value, axis_, sign> &line_iterator,
412
+ const bool threshold,
413
+ int axis = 0) {
414
+ static_assert(std::is_same_v<typename Filtration_value::value_type, typename Slicer::Filtration_value::value_type>);
415
+ using value_type = typename Filtration_value::value_type;
416
+ // Line iterator should be on the biggest axis
417
+ constexpr const bool verbose = false;
418
+ constexpr const bool verbose2 = false;
419
+ while (!line_iterator.is_finished()) {
420
+ const Line<value_type> &new_line = (axis_ >= 0) ? *(++line_iterator) : *line_iterator.next(axis);
421
+ // if constexpr (axis_ >= 0) {
422
+ // new_line = *(++line_iterator); // first line is always a persistence
423
+ // } else {
424
+ // new_line = *line_iterator.next(axis);
425
+ // }
426
+ // copy, no need to add it
427
+ if constexpr (verbose) std::cout << "----------------------------------------------" << std::endl;
428
+ if constexpr (verbose) std::cout << "Line basepoint " << new_line.base_point() << std::endl;
429
+ slicer.push_to(new_line);
430
+
431
+ slicer.vineyard_update();
432
+ if constexpr (verbose2) std::cout << slicer << std::endl;
433
+ const auto &diagram = slicer.get_flat_nodim_barcode();
434
+ module.add_barcode(new_line, std::move(diagram), threshold);
435
+ };
436
+ };
437
+
438
+ template <class Filtration_value, class Slicer = SimplicialVineMatrixTruc<>>
439
+ void _rec_mma(Module<typename Filtration_value::value_type> &module,
440
+ Filtration_value &basepoint,
441
+ const std::vector<int> &grid_size,
442
+ int dim_to_iterate,
443
+ Slicer &&current_persistence,
444
+ const value_type precision,
445
+ bool threshold) {
446
+ if (dim_to_iterate <= 0) {
447
+ LineIterator<Filtration_value, 0> line_iterator(std::move(basepoint), precision, grid_size[0]);
448
+ __add_vineyard_trajectory_to_module<Filtration_value, 0, Slicer>(
449
+ module, std::move(current_persistence), line_iterator, threshold);
450
+ return;
451
+ }
452
+ Slicer pers_copy;
453
+ Filtration_value basepoint_copy;
454
+ for (int i = 0; i < grid_size[dim_to_iterate]; ++i) {
455
+ // TODO : multithread, but needs matrix to be thread safe + put mutex on
456
+ // module
457
+ pers_copy = current_persistence;
458
+ basepoint_copy = basepoint;
459
+ _rec_mma(module, basepoint_copy, grid_size, dim_to_iterate - 1, pers_copy, precision, threshold);
460
+ basepoint[dim_to_iterate] += precision;
461
+ // current_persistence.push_to(Line(basepoint));
462
+ // current_persistence.vineyard_update();
463
+ }
464
+ }
465
+
466
+ template <int axis, class Filtration_value, class Slicer>
467
+ void _rec_mma2(Module<typename Filtration_value::value_type> &module,
468
+ Filtration_value &&basepoint,
469
+ const Filtration_value &direction,
470
+ const std::vector<int> &grid_size,
471
+ const std::vector<bool> &signs,
472
+ int dim_to_iterate,
473
+ Slicer &&current_persistence,
474
+ const value_type precision,
475
+ bool threshold) {
476
+ static_assert(std::is_same_v<typename Filtration_value::value_type, typename Slicer::value_type>);
477
+
478
+ if (dim_to_iterate <= axis) {
479
+ if (signs[axis]) {
480
+ LineIterator<Filtration_value, axis, true> line_iterator(
481
+ std::move(basepoint), direction, precision, grid_size[axis]);
482
+ __add_vineyard_trajectory_to_module<Filtration_value, axis, true, Slicer>(
483
+ module, std::move(current_persistence), line_iterator, threshold);
484
+ } else {
485
+ LineIterator<Filtration_value, axis, false> line_iterator(
486
+ std::move(basepoint), direction, precision, grid_size[axis]);
487
+ __add_vineyard_trajectory_to_module<Filtration_value, axis, false, Slicer>(
488
+ module, std::move(current_persistence), line_iterator, threshold);
489
+ }
490
+
491
+ return;
492
+ }
493
+ if (grid_size[dim_to_iterate] == 0) {
494
+ // no need to copy basepoint, we just skip the dim here
495
+ _rec_mma2<axis, Filtration_value, Slicer>(module,
496
+ std::move(basepoint),
497
+ direction,
498
+ grid_size,
499
+ signs,
500
+ dim_to_iterate - 1,
501
+ std::move(current_persistence),
502
+ precision,
503
+ threshold);
504
+ return;
505
+ }
506
+ for (int i = 0; i < grid_size[dim_to_iterate]; ++i) {
507
+ // TODO : multithread, but needs matrix to be thread safe + put mutex on
508
+ // module
509
+ _rec_mma2<axis, Filtration_value, typename Slicer::ThreadSafe>(module,
510
+ Filtration_value(basepoint),
511
+ direction,
512
+ grid_size,
513
+ signs,
514
+ dim_to_iterate - 1,
515
+ current_persistence.weak_copy(),
516
+ precision,
517
+ threshold);
518
+ basepoint[dim_to_iterate] += signs[dim_to_iterate] ? precision : -precision;
519
+ // current_persistence.push_to(Line(basepoint));
520
+ // current_persistence.vineyard_update();
521
+ }
522
+ }
523
+
524
+ template <class Slicer, typename value_type>
525
+ Module<value_type> multiparameter_module_approximation(
526
+ Slicer &slicer,
527
+ const Gudhi::multi_filtration::One_critical_filtration<value_type> &direction,
528
+ const value_type precision,
529
+ Box<value_type> &box,
530
+ const bool threshold,
531
+ const bool complete,
532
+ const bool verbose) {
533
+ static_assert(std::is_same_v<typename Slicer::Filtration_value::value_type,
534
+ value_type>); // Value type can be exposed to python interface.
535
+ if (verbose) std::cout << "Starting Module Approximation" << std::endl;
536
+ /* using Filtration_value = Slicer::Filtration_value; */
537
+
538
+ Gudhi::multi_filtration::One_critical_filtration<value_type> basepoint = box.get_lower_corner();
539
+ const std::size_t num_parameters = box.dimension();
540
+ std::vector<int> grid_size(num_parameters);
541
+ std::vector<bool> signs(num_parameters);
542
+ int signs_shifts = 0;
543
+ int arg_max_signs_shifts = -1;
544
+ for (std::size_t i = 0; i < num_parameters; i++) {
545
+ auto &a = box.get_lower_corner()[i];
546
+ auto &b = box.get_upper_corner()[i];
547
+ grid_size[i] = static_cast<int>(std::ceil((std::fabs(b - a) / precision))) + 1;
548
+ signs[i] = b > a;
549
+ if (b < a) {
550
+ std::swap(a, b);
551
+ int local_shift;
552
+ if (!direction.num_parameters())
553
+ local_shift = grid_size[i];
554
+ else {
555
+ local_shift = direction[i] > 0 ? static_cast<int>(std::ceil(grid_size[i] / direction[i])) : 0;
556
+ }
557
+ if (local_shift > signs_shifts) {
558
+ signs_shifts = std::max(signs_shifts, local_shift);
559
+ arg_max_signs_shifts = i;
560
+ }
561
+ }
562
+
563
+ // fix the box
564
+ }
565
+ if (signs_shifts > 0) {
566
+ for (std::size_t i = 0; i < num_parameters; i++)
567
+ grid_size[i] += signs_shifts; // this may be too much for large num_parameters
568
+ grid_size[arg_max_signs_shifts] = 1;
569
+ if (verbose)
570
+ std::cout << "Had to flatten/shift coordinate " << arg_max_signs_shifts << " by " << signs_shifts << std::endl;
571
+ }
572
+ Module<value_type> out(box);
573
+ box.inflate(2 * precision); // for infinte summands
574
+
575
+ if (verbose) std::cout << "Num parameters : " << num_parameters << std::endl;
576
+ if (verbose) std::cout << "Box : " << box << std::endl;
577
+ if (num_parameters < 1) return out;
578
+
579
+ // first line to compute
580
+ // TODO: change here
581
+ // for (auto i = 0u; i < basepoint.size() - 1; i++)
582
+ // basepoint[i] -= box.get_upper_corner().back();
583
+ // basepoint.back() = 0;
584
+ Line<value_type> current_line(basepoint, direction);
585
+ if (verbose) std::cout << "First line basepoint " << basepoint << std::endl;
586
+
587
+ {
588
+ Timer timer("Initializing mma...\n", verbose);
589
+ // fills the first barcode
590
+ slicer.push_to(current_line);
591
+ slicer.compute_persistence();
592
+ auto barcode = slicer.get_flat_barcode();
593
+ auto num_bars = barcode.size();
594
+ out.resize(num_bars);
595
+ /* Filtration_value birthContainer(num_parameters), */
596
+ /* deathContainer(num_parameters); */
597
+ for (std::size_t i = 0; i < num_bars; i++) {
598
+ const auto &[dim, bar] = barcode[i];
599
+ /* const auto &[birth, death] = bar; */
600
+ out[i].set_dimension(dim);
601
+ /* out[i].add_bar(birth, death, basepoint, birthContainer, deathContainer,
602
+ */
603
+ /* threshold, box); */
604
+ }
605
+
606
+ out.add_barcode(current_line, barcode, threshold);
607
+
608
+ if (verbose) std::cout << "Instantiated " << num_bars << " summands" << std::endl;
609
+ }
610
+ // TODO : change here
611
+ // std::vector<int> grid_size(num_parameters - 1);
612
+ // auto h = box.get_upper_corner().back() - box.get_lower_corner().back();
613
+ // for (int i = 0; i < num_parameters - 1; i++) {
614
+ // auto a = box.get_lower_corner()[i];
615
+ // auto b = box.get_upper_corner()[i];
616
+ // grid_size[i] =
617
+ // static_cast<unsigned int>(std::ceil((std::abs(b - a + h) /
618
+ // precision)));
619
+ // }
620
+ // TODO : change here
621
+ if (verbose) {
622
+ std::cout << "Grid size " << Gudhi::multi_filtration::One_critical_filtration(grid_size) << " Signs ";
623
+ if (signs.empty()) {
624
+ std::cout << "[]";
625
+ } else {
626
+ std::cout << "[";
627
+ for (std::size_t i = 0; i < signs.size() - 1; i++) {
628
+ std::cout << signs[i] << ", ";
629
+ }
630
+ std::cout << signs.back() << "]";
631
+ }
632
+ std::cout << std::endl;
633
+ std::cout << "Max error " << precision << std::endl;
634
+ }
635
+
636
+ {
637
+ Timer timer("Computing mma...", verbose);
638
+ // actual computation. -1 as line grid is n-1 dim, -1 as we start from 0
639
+ // _rec_mma(out, basepoint, grid_size, num_parameters - 2, slicer,
640
+ // precision,
641
+ // threshold);
642
+ // TODO : change here
643
+
644
+ for (std::size_t i = 1; i < num_parameters; i++) {
645
+ // the loop is on the faces of the lower box
646
+ // should be parallelizable, up to a mutex on out
647
+ if (direction.num_parameters() && direction[i] == 0.0) continue; // skip faces with codim d_i=0
648
+ auto temp_grid_size = grid_size;
649
+ temp_grid_size[i] = 0;
650
+ if (verbose)
651
+ std::cout << "Face " << i << "/" << num_parameters << " with grid size "
652
+ << Gudhi::multi_filtration::One_critical_filtration(temp_grid_size) << std::endl;
653
+ // if (!direction.size() || direction[0] > 0)
654
+ _rec_mma2<0>(out,
655
+ Gudhi::multi_filtration::One_critical_filtration<value_type>(basepoint),
656
+ direction,
657
+ temp_grid_size,
658
+ signs,
659
+ num_parameters - 1,
660
+ slicer.weak_copy(),
661
+ precision,
662
+ threshold);
663
+ }
664
+ // last one, we can destroy basepoint & cie
665
+ if (!direction.num_parameters() || direction[0] > 0) {
666
+ grid_size[0] = 0;
667
+ if (verbose)
668
+ std::cout << "Face " << num_parameters << "/" << num_parameters << " with grid size "
669
+ << Gudhi::multi_filtration::One_critical_filtration(grid_size) << std::endl;
670
+ _rec_mma2<1>(out,
671
+ std::move(basepoint),
672
+ direction,
673
+ grid_size,
674
+ signs,
675
+ num_parameters - 1,
676
+ std::move(slicer),
677
+ precision,
678
+ threshold);
679
+ }
680
+ }
681
+
682
+ { // for Timer
683
+ Timer timer("Cleaning output ... ", verbose);
684
+ out.clean();
685
+ if (complete) {
686
+ if (verbose) std::cout << "Completing output ...";
687
+ for (std::size_t i = 0; i < num_parameters; i++) out.fill(precision);
688
+ }
689
+ } // Timer death
690
+ return out;
691
+ };
692
+
693
+ template <typename value_type>
694
+ template <class Barcode>
695
+ inline void Module<value_type>::add_barcode(const Barcode &barcode) {
696
+ constexpr const bool verbose = false;
697
+ if (barcode.size() != module_.size()) {
698
+ std::cerr << "Barcode sizes doesn't match. Module is " << std::to_string(module_.size()) << " and barcode is "
699
+ << std::to_string(barcode.size()) << std::endl;
700
+ }
701
+ unsigned int count = 0;
702
+ for (const auto &bar_ : barcode) {
703
+ auto &summand = this->operator[](count++);
704
+ auto &[dim, bar] = bar_;
705
+ auto &[birth_filtration, death_filtration] = bar;
706
+ if constexpr (verbose) std::cout << "Birth " << birth_filtration << " Death " << death_filtration << std::endl;
707
+ summand.add_bar(birth_filtration, death_filtration);
708
+ }
709
+ }
710
+
711
+ template <typename value_type>
712
+ inline void Module<value_type>::add_barcode(
713
+ const Line<value_type> &line,
714
+ const std::vector<std::pair<int, std::pair<value_type, value_type>>> &barcode,
715
+ const bool threshold_in) {
716
+ assert(barcode.size() == module_.size() && "Barcode sizes doesn't match.");
717
+
718
+ auto count = 0U;
719
+ for (const auto &extBar : barcode) {
720
+ auto &[dim, bar] = extBar;
721
+ _add_bar_with_threshold(line, bar, threshold_in, this->operator[](count++));
722
+ }
723
+ }
724
+
725
+ template <typename value_type>
726
+ inline void Module<value_type>::add_barcode(const Line<value_type> &line,
727
+ const std::vector<std::pair<value_type, value_type>> &barcode,
728
+ const bool threshold_in) {
729
+ assert(barcode.size() == module_.size() && "Barcode sizes doesn't match.");
730
+
731
+ auto count = 0U;
732
+ for (const auto &bar : barcode) {
733
+ _add_bar_with_threshold(line, bar, threshold_in, this->operator[](count++));
734
+ }
735
+ }
736
+
737
+ template <typename value_type>
738
+ inline void Module<value_type>::_add_bar_with_threshold(const Line<value_type> &line,
739
+ const std::pair<value_type, value_type> &bar,
740
+ const bool threshold_in,
741
+ Summand<value_type> &summand) {
742
+ constexpr const bool verbose = false;
743
+ auto [birth_filtration, death_filtration] = bar;
744
+
745
+ if (birth_filtration >= death_filtration) return;
746
+
747
+ if constexpr (verbose) {
748
+ std::cout << "--BAR (" << birth_filtration << ", " << death_filtration << ") at basepoint " << line.base_point()
749
+ << " direction " << line.direction() << std::endl;
750
+ }
751
+
752
+ auto birth_container = line[birth_filtration];
753
+ if constexpr (verbose) std::cout << " B: " << birth_container << " B*d: " << birth_filtration * line.direction();
754
+ if (birth_container.is_minus_inf()) {
755
+ if (threshold_in) birth_container = box_.get_lower_corner();
756
+ } else {
757
+ bool allInf = true;
758
+ for (std::size_t i = 0U; i < birth_container.num_parameters(); i++) {
759
+ auto t = box_.get_lower_corner()[i];
760
+ if (birth_container[i] < t - 1e-10) birth_container[i] = threshold_in ? t : -filtration_type::T_inf;
761
+ if (birth_container[i] != -filtration_type::T_inf) allInf = false;
762
+ }
763
+ if (allInf) birth_container = filtration_type::minus_inf();
764
+ }
765
+
766
+ auto death_container = line[death_filtration];
767
+ if constexpr (verbose) std::cout << " D: " << death_container;
768
+ if (death_container.is_plus_inf()) {
769
+ if (threshold_in) death_container = box_.get_upper_corner();
770
+ } else {
771
+ bool allInf = true;
772
+ for (std::size_t i = 0U; i < death_container.num_parameters(); i++) {
773
+ auto t = box_.get_upper_corner()[i];
774
+ if (death_container[i] > t + 1e-10) death_container[i] = threshold_in ? t : filtration_type::T_inf;
775
+ if (death_container[i] != filtration_type::T_inf) allInf = false;
776
+ }
777
+ if (allInf) death_container = filtration_type::inf();
778
+ }
779
+
780
+ if constexpr (verbose) std::cout << " BT: " << birth_container << " DT: " << death_container << std::endl;
781
+ summand.add_bar(birth_container, death_container);
782
+ }
783
+
784
+ template <typename value_type>
785
+ inline Module<value_type>::Module() {}
786
+
787
+ template <typename value_type>
788
+ inline Module<value_type>::Module(Box<value_type> &box) : box_(box) {}
789
+
790
+ template <typename value_type>
791
+ inline void Module<value_type>::resize(const unsigned int size) {
792
+ module_.resize(size);
793
+ }
794
+
795
+ template <typename value_type>
796
+ inline Summand<value_type> &Module<value_type>::at(const unsigned int index) {
797
+ return module_.at(index);
798
+ }
799
+
800
+ template <typename value_type>
801
+ inline Summand<value_type> &Module<value_type>::operator[](const size_t index) {
802
+ return this->module_[index];
803
+ }
804
+
805
+ template <typename value_type>
806
+ inline const Summand<value_type> &Module<value_type>::operator[](const size_t index) const {
807
+ return this->module_[index];
808
+ }
809
+
810
+ template <typename value_type>
811
+ inline typename Module<value_type>::module_type::iterator Module<value_type>::begin() {
812
+ return module_.begin();
813
+ }
814
+
815
+ template <typename value_type>
816
+ inline typename Module<value_type>::module_type::iterator Module<value_type>::end() {
817
+ return module_.end();
818
+ }
819
+
820
+ template <typename value_type>
821
+ inline void Module<value_type>::add_summand(Summand<value_type> summand, int degree) {
822
+ if (degree >= 0) summand.set_dimension(degree);
823
+ module_.push_back(summand);
824
+ }
825
+
826
+ /**
827
+ * @brief Remove the empty summands of the output
828
+ *
829
+ * @param output p_output:...
830
+ * @param keep_order p_keep_order:... Defaults to false.
831
+ */
832
+
833
+ template <typename value_type>
834
+ inline void Module<value_type>::clean() {
835
+ module_type tmp;
836
+ for (size_t i = 0u; i < module_.size(); i++) {
837
+ module_[i].clean();
838
+ }
839
+ module_.erase(
840
+ std::remove_if(
841
+ module_.begin(), module_.end(), [](const Summand<value_type> &s) { return s.get_upset().is_plus_inf(); }),
842
+ module_.end());
843
+ }
844
+
845
+ template <typename value_type>
846
+ inline void Module<value_type>::fill(const value_type precision) {
847
+ if (module_.empty()) return;
848
+
849
+ for (Summand<value_type> &sum : module_) {
850
+ sum.complete_birth(precision);
851
+ sum.complete_death(precision);
852
+ }
853
+ }
854
+
855
+ template <typename value_type>
856
+ std::vector<value_type> inline Module<value_type>::get_interleavings(const Box<value_type> &box) {
857
+ std::vector<value_type> out(this->size());
858
+ for (auto i = 0u; i < out.size(); ++i) {
859
+ out[i] = module_[i].get_interleaving(box);
860
+ }
861
+ return out;
862
+ }
863
+
864
+ template <typename value_type>
865
+ typename Module<value_type>::distance_to_idx_type inline Module<value_type>::compute_distance_idx_to(
866
+ const std::vector<value_type> &pt,
867
+ bool full) const {
868
+ typename Module<value_type>::distance_to_idx_type out(module_.size(), std::vector<int>(full ? 4 : 2));
869
+ for (auto i = 0u; i < module_.size(); ++i) {
870
+ out[i] = module_[i].distance_idx_to(pt, full);
871
+ }
872
+ return out;
873
+ }
874
+
875
+ template <typename value_type>
876
+ std::vector<value_type> inline Module<value_type>::compute_distance_to(const std::vector<value_type> &pt,
877
+ bool negative) const {
878
+ std::vector<value_type> out(this->size());
879
+ for (auto i = 0u; i < this->size(); ++i) {
880
+ out[i] = module_[i].distance_to(pt, negative);
881
+ }
882
+ return out;
883
+ }
884
+
885
+ template <typename value_type>
886
+ std::vector<std::vector<value_type>> inline Module<value_type>::compute_distances_to(
887
+ const std::vector<std::vector<value_type>> &pts,
888
+ bool negative,
889
+ int n_jobs) const {
890
+ std::vector<std::vector<value_type>> out(pts.size(), std::vector<value_type>(this->size()));
891
+ oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
892
+ arena.execute([&] {
893
+ tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
894
+ tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
895
+ out[i][j] = module_[j].distance_to(pts[i], negative);
896
+ });
897
+ });
898
+ });
899
+ return out;
900
+ }
901
+
902
+ template <typename value_type>
903
+ template <typename dtype, typename indices_type>
904
+ void inline Module<value_type>::compute_distances_to(dtype *data_ptr,
905
+ const std::vector<std::vector<value_type>> &pts,
906
+ bool negative,
907
+ int n_jobs) const {
908
+ tensor::static_tensor_view<dtype, indices_type> container(
909
+ data_ptr, {static_cast<int>(pts.size()), static_cast<int>(this->size())});
910
+ oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
911
+ arena.execute([&] {
912
+ tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
913
+ // tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
914
+ dtype *current_ptr = &container[{static_cast<int>(i), 0}];
915
+ for (std::size_t j = 0u; j < this->size(); ++j) {
916
+ *(current_ptr + j) = module_[j].distance_to(pts[i], negative);
917
+ }
918
+ });
919
+ // });
920
+ });
921
+ }
922
+
923
+ template <typename value_type>
924
+ typename Module<value_type>::distances_to_idx_type inline Module<value_type>::compute_distances_idx_to(
925
+ const std::vector<std::vector<value_type>> &pts,
926
+ bool full,
927
+ int n_jobs) const {
928
+ Module::distances_to_idx_type out(pts.size(),
929
+ Module::distance_to_idx_type(module_.size(), std::vector<int>(full ? 4 : 2)));
930
+
931
+ oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
932
+ arena.execute([&] {
933
+ tbb::parallel_for(std::size_t(0u), pts.size(), [&](std::size_t i) {
934
+ tbb::parallel_for(std::size_t(0u), std::size_t(this->size()), [&](std::size_t j) {
935
+ out[i][j] = module_[j].distance_idx_to(pts[i], full);
936
+ });
937
+ });
938
+ });
939
+ return out;
940
+ }
941
+
942
+ template <typename value_type>
943
+ inline std::vector<std::vector<value_type>> Module<value_type>::compute_pixels(
944
+ const std::vector<std::vector<value_type>> &coordinates,
945
+ const std::vector<int> &degrees,
946
+ const Box<value_type> &box,
947
+ const value_type delta,
948
+ const value_type p,
949
+ const bool normalize,
950
+ const int n_jobs) {
951
+ auto num_degrees = degrees.size();
952
+ auto num_pts = coordinates.size();
953
+ std::vector<std::vector<value_type>> out(num_degrees, std::vector<value_type>(num_pts));
954
+
955
+ typename module_type::iterator start;
956
+ typename module_type::iterator end = module_.begin();
957
+ for (auto degree_idx = 0u; degree_idx < num_degrees; degree_idx++) {
958
+ { // for Timer
959
+ auto d = degrees[degree_idx];
960
+ Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
961
+ start = end;
962
+ while (start != module_.end() && start->get_dimension() != d) start++;
963
+ if (start == module_.end()) break;
964
+ end = start;
965
+ while (end != module_.end() && end->get_dimension() == d) end++;
966
+ out[degree_idx] = compute_pixels_of_degree(start, end, delta, p, normalize, box, coordinates, n_jobs);
967
+ } // Timer death
968
+ }
969
+ return out;
970
+ }
971
+
972
+ template <typename value_type>
973
+ inline typename std::vector<typename Module<value_type>::image_type> Module<value_type>::get_vectorization(
974
+ const value_type delta,
975
+ const value_type p,
976
+ const bool normalize,
977
+ const Box<value_type> &box,
978
+ unsigned int horizontalResolution,
979
+ unsigned int verticalResolution) {
980
+ dimension_type maxDim = module_.back().get_dimension();
981
+ std::vector<Module::image_type> image_vector(maxDim + 1);
982
+ typename module_type::iterator start;
983
+ typename module_type::iterator end = module_.begin();
984
+ for (dimension_type d = 0; d <= maxDim; d++) {
985
+ { // for Timer
986
+ Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
987
+ start = end;
988
+ while (end != module_.end() && end->get_dimension() == d) end++;
989
+ _compute_2D_image(
990
+ image_vector.at(d), start, end, delta, p, normalize, box, horizontalResolution, verticalResolution);
991
+ } // Timer death
992
+ }
993
+ return image_vector;
994
+ }
995
+
996
+ template <typename value_type>
997
+ inline std::vector<typename Module<value_type>::image_type> Module<value_type>::get_vectorization(
998
+ unsigned int horizontalResolution,
999
+ unsigned int verticalResolution,
1000
+ get_2dpixel_value_function_type get_pixel_value) const {
1001
+ dimension_type maxDim = module_.back().get_dimension();
1002
+ std::vector<Module::image_type> image_vector(maxDim + 1);
1003
+ typename module_type::const_iterator start;
1004
+ typename module_type::const_iterator end = module_.begin();
1005
+ for (dimension_type d = 0; d <= maxDim; d++) {
1006
+ { // for Timer
1007
+ Debug::Timer timer("Computing image of dimension " + std::to_string(d) + " ...", verbose);
1008
+ start = end;
1009
+ while (end != module_.end() && end->get_dimension() == d) end++;
1010
+ _compute_2D_image(image_vector.at(d), start, end, horizontalResolution, verticalResolution, get_pixel_value);
1011
+ } // Timer death
1012
+ }
1013
+ return image_vector;
1014
+ }
1015
+
1016
+ template <typename value_type>
1017
+ inline typename Module<value_type>::image_type Module<value_type>::get_vectorization_in_dimension(
1018
+ const dimension_type dimension,
1019
+ const value_type delta,
1020
+ const value_type p,
1021
+ const bool normalize,
1022
+ const Box<value_type> &box,
1023
+ unsigned int horizontalResolution,
1024
+ unsigned int verticalResolution) {
1025
+ Debug::Timer timer("Computing image of dimension " + std::to_string(dimension) + " ...", verbose);
1026
+
1027
+ Module::image_type image;
1028
+ typename module_type::iterator start = module_.begin();
1029
+ while (start != module_.end() && start->get_dimension() < dimension) start++;
1030
+ typename module_type::iterator end = start;
1031
+ while (end != module_.end() && end->get_dimension() == dimension) end++;
1032
+ _compute_2D_image(image, start, end, delta, p, normalize, box, horizontalResolution, verticalResolution);
1033
+
1034
+ return image;
1035
+ }
1036
+
1037
+ template <typename value_type>
1038
+ inline typename Module<value_type>::image_type Module<value_type>::get_vectorization_in_dimension(
1039
+ const dimension_type dimension,
1040
+ unsigned int horizontalResolution,
1041
+ unsigned int verticalResolution,
1042
+ get_2dpixel_value_function_type get_pixel_value) const {
1043
+ Debug::Timer timer("Computing image of dimension " + std::to_string(dimension) + " ...", verbose);
1044
+
1045
+ typename Module::image_type image;
1046
+ typename module_type::const_iterator start = module_.begin();
1047
+ while (start != module_.end() && start->get_dimension() < dimension) start++;
1048
+ typename module_type::const_iterator end = start;
1049
+ while (end != module_.end() && end->get_dimension() == dimension) end++;
1050
+ _compute_2D_image(image, start, end, horizontalResolution, verticalResolution, get_pixel_value);
1051
+
1052
+ return image;
1053
+ }
1054
+
1055
+ template <typename value_type>
1056
+ std::vector<value_type> Module<value_type>::get_landscape_values(const std::vector<value_type> &x,
1057
+ const dimension_type dimension) const {
1058
+ std::vector<value_type> out;
1059
+ out.reserve(this->size());
1060
+ for (unsigned int i = 0; i < this->size(); i++) {
1061
+ const Summand<value_type> &summand = this->module_[i];
1062
+ if (summand.get_dimension() == dimension) out.push_back(summand.get_landscape_value(x));
1063
+ }
1064
+ std::sort(out.begin(), out.end(), [](const value_type x, const value_type y) { return x > y; });
1065
+ return out;
1066
+ }
1067
+
1068
+ template <typename value_type>
1069
+ typename Module<value_type>::image_type Module<value_type>::get_landscape(
1070
+ const dimension_type dimension,
1071
+ const unsigned int k,
1072
+ const Box<value_type> &box,
1073
+ const std::vector<unsigned int> &resolution) const {
1074
+ // TODO extend in higher dimension (ie, change the image type to a template
1075
+ // class)
1076
+ Module::image_type image;
1077
+ image.resize(resolution[0], std::vector<value_type>(resolution[1]));
1078
+ value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / resolution[0];
1079
+ value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / resolution[1];
1080
+ tbb::parallel_for(0U, resolution[0], [&](unsigned int i) {
1081
+ tbb::parallel_for(0U, resolution[1], [&](unsigned int j) {
1082
+ auto landscape = this->get_landscape_values(
1083
+ {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j}, dimension);
1084
+ image[i][j] = k < landscape.size() ? landscape[k] : 0;
1085
+ });
1086
+ });
1087
+ return image;
1088
+ }
1089
+
1090
+ template <typename value_type>
1091
+ std::vector<typename Module<value_type>::image_type> Module<value_type>::get_landscapes(
1092
+ const dimension_type dimension,
1093
+ const std::vector<unsigned int> ks,
1094
+ const Box<value_type> &box,
1095
+ const std::vector<unsigned int> &resolution) const {
1096
+ std::vector<Module::image_type> images(ks.size());
1097
+ for (auto &image : images) image.resize(resolution[0], std::vector<value_type>(resolution[1]));
1098
+ value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / resolution[0];
1099
+ value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / resolution[1];
1100
+
1101
+ tbb::parallel_for(0U, resolution[0], [&](unsigned int i) {
1102
+ tbb::parallel_for(0U, resolution[1], [&](unsigned int j) {
1103
+ std::vector<value_type> landscapes = this->get_landscape_values(
1104
+ {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j}, dimension);
1105
+ for (const auto k : ks) {
1106
+ images[k][i][j] = k < landscapes.size() ? landscapes[k] : 0;
1107
+ }
1108
+ });
1109
+ });
1110
+ return images;
1111
+ }
1112
+
1113
+ template <typename value_type>
1114
+ inline Box<value_type> Module<value_type>::get_box() const {
1115
+ return this->box_;
1116
+ }
1117
+
1118
+ template <typename value_type>
1119
+ inline void Module<value_type>::set_box(Box<value_type> box) {
1120
+ this->box_ = box;
1121
+ }
1122
+
1123
+ template <typename value_type>
1124
+ inline unsigned int Module<value_type>::size() const {
1125
+ return this->module_.size();
1126
+ }
1127
+
1128
+ template <typename value_type>
1129
+ inline void Module<value_type>::infer_box(std::vector<filtration_type> &f) {
1130
+ this->box_.infer_from_filters(f);
1131
+ }
1132
+
1133
+ template <typename value_type>
1134
+ inline dimension_type Module<value_type>::get_dimension() const {
1135
+ return this->module_.empty() ? -1 : this->module_.back().get_dimension();
1136
+ }
1137
+
1138
+ template <typename value_type>
1139
+ inline std::vector<Summand<value_type>> Module<value_type>::get_summands_of_dimension(const int dimension) const {
1140
+ std::vector<Summand<value_type>> list;
1141
+ for (const Summand<value_type> &summand : this->module_) {
1142
+ if (summand.get_dimension() == dimension) list.push_back(summand);
1143
+ }
1144
+ return list;
1145
+ }
1146
+
1147
+ template <typename value_type>
1148
+ inline std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>>
1149
+ Module<value_type>::get_corners_of_dimension(const int dimension) const {
1150
+ std::vector<std::pair<std::vector<std::vector<value_type>>, std::vector<std::vector<value_type>>>> list;
1151
+ for (const Summand<value_type> &summand : this->module_) {
1152
+ if (summand.get_dimension() == dimension)
1153
+ list.push_back(std::make_pair(
1154
+ std::vector<std::vector<value_type>>(summand.get_birth_list().begin(), summand.get_birth_list().end()),
1155
+ std::vector<std::vector<value_type>>(summand.get_death_list().begin(), summand.get_death_list().end())));
1156
+ }
1157
+ return list;
1158
+ }
1159
+
1160
+ template <typename value_type>
1161
+ std::vector<std::vector<std::pair<value_type, value_type>>> Module<value_type>::get_barcode2(
1162
+ const Line<value_type> &l,
1163
+ const dimension_type dimension) const {
1164
+ constexpr const bool verbose = false;
1165
+ std::vector<std::vector<std::pair<value_type, value_type>>> barcode(this->get_dimension() + 1);
1166
+ for (auto i = 0; i < this->get_dimension(); ++i) {
1167
+ barcode[i].reserve(this->size());
1168
+ }
1169
+ for (unsigned int i = 0; i < this->size(); i++) {
1170
+ const Summand<value_type> &summand = this->module_[i];
1171
+ if constexpr (verbose) std::cout << "Summand of dimension " << summand.get_dimension() << std::endl;
1172
+
1173
+ if (dimension != -1 && summand.get_dimension() != dimension) continue;
1174
+ /* if (dimension != -1 && summand.get_dimension() > dimension) */
1175
+ /* break; */
1176
+ const auto &pushed_summand = summand.get_bar2(l);
1177
+
1178
+ barcode[summand.get_dimension()].push_back(pushed_summand);
1179
+ }
1180
+ return barcode;
1181
+ }
1182
+
1183
+ template <typename value_type>
1184
+ MultiDiagram<typename Module<value_type>::filtration_type, value_type>
1185
+ Module<value_type>::get_barcode(const Line<value_type> &l, const dimension_type dimension, const bool threshold) const {
1186
+ constexpr const bool verbose = false;
1187
+ if constexpr (verbose)
1188
+ std::cout << "Computing barcode of dimension " << dimension << " and threshold " << threshold << std::endl;
1189
+ std::vector<MultiDiagram_point<filtration_type>> barcode(this->size());
1190
+ std::pair<value_type, value_type> threshold_bounds;
1191
+ if (threshold) threshold_bounds = l.get_bounds(this->box_);
1192
+ unsigned int summand_idx = 0;
1193
+ for (unsigned int i = 0; i < this->size(); i++) {
1194
+ const Summand<value_type> &summand = this->module_[i];
1195
+ if constexpr (verbose) std::cout << "Summand of dimension " << summand.get_dimension() << std::endl;
1196
+
1197
+ if (dimension != -1 && summand.get_dimension() != dimension) continue;
1198
+ /* if (dimension != -1 && summand.get_dimension() > dimension) */
1199
+ /* break; */
1200
+ auto pushed_summand = summand.get_bar(l);
1201
+
1202
+ filtration_type &pbirth = pushed_summand.first;
1203
+ filtration_type &pdeath = pushed_summand.second;
1204
+ if constexpr (verbose) std::cout << "BAR : " << pbirth << " " << pdeath << std::endl;
1205
+ if (threshold) {
1206
+ auto min = l[threshold_bounds.first];
1207
+ auto max = l[threshold_bounds.second];
1208
+ if (!(pbirth < max) || !(pdeath > min)) {
1209
+ /* continue; */ // We still need summands to be aligned. The price to
1210
+ // pay is some memory.
1211
+ pbirth = std::numeric_limits<filtration_type>::infinity();
1212
+ pdeath = pbirth;
1213
+ }
1214
+ pbirth.push_to_least_common_upper_bound(min);
1215
+ pdeath.pull_to_greatest_common_lower_bound(max);
1216
+ }
1217
+ barcode[summand_idx++] = MultiDiagram_point(summand.get_dimension(), pbirth, pdeath);
1218
+ }
1219
+ barcode.resize(summand_idx);
1220
+ return MultiDiagram<filtration_type, value_type>(barcode);
1221
+ }
1222
+
1223
+ template <typename value_type>
1224
+ MultiDiagrams<typename Module<value_type>::filtration_type, value_type> Module<value_type>::get_barcodes(
1225
+ const std::vector<Line<value_type>> &lines,
1226
+ const dimension_type dimension,
1227
+ const bool threshold) const {
1228
+ unsigned int nlines = lines.size();
1229
+ MultiDiagrams<typename Module<value_type>::filtration_type, value_type> out(nlines);
1230
+ tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1231
+ const Line<value_type> &l = lines[i];
1232
+ out[i] = this->get_barcode(l, dimension, threshold);
1233
+ });
1234
+ return out;
1235
+ }
1236
+
1237
+ template <typename value_type>
1238
+ std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> Module<value_type>::get_barcodes2(
1239
+ const std::vector<Line<value_type>> &lines,
1240
+ const dimension_type dimension) const {
1241
+ unsigned int nlines = lines.size();
1242
+ std::vector<std::vector<std::vector<std::pair<value_type, value_type>>>> out(
1243
+ this->get_dimension() + 1, std::vector<std::vector<std::pair<value_type, value_type>>>(nlines));
1244
+ tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1245
+ const Line<value_type> &l = lines[i];
1246
+ for (const auto &summand : module_) {
1247
+ if (dimension != -1 && summand.get_dimension() != dimension) continue;
1248
+ const auto &bar = summand.get_bar2(l);
1249
+ out[summand.get_dimension()][i].push_back(bar);
1250
+ }
1251
+ });
1252
+ return out;
1253
+ }
1254
+
1255
+ template <typename value_type>
1256
+ MultiDiagrams<typename Module<value_type>::filtration_type, value_type> Module<value_type>::get_barcodes(
1257
+ const std::vector<filtration_type> &basepoints,
1258
+ const dimension_type dimension,
1259
+ const bool threshold) const {
1260
+ unsigned int nlines = basepoints.size();
1261
+ MultiDiagrams<typename Module<value_type>::filtration_type, value_type> out(nlines);
1262
+ // for (unsigned int i = 0; i < nlines; i++){
1263
+ tbb::parallel_for(0U, nlines, [&](unsigned int i) {
1264
+ const Line<value_type> &l = Line<value_type>(basepoints[i]);
1265
+ out[i] = this->get_barcode(l, dimension, threshold);
1266
+ });
1267
+ return out;
1268
+ }
1269
+
1270
+ template <typename value_type>
1271
+ std::vector<int> Module<value_type>::euler_curve(const std::vector<filtration_type> &points) const {
1272
+ unsigned int npts = points.size();
1273
+ std::vector<int> out(npts);
1274
+ // #pragma omp parallel for
1275
+ tbb::parallel_for(0U, static_cast<unsigned int>(out.size()), [&](unsigned int i) {
1276
+ auto &euler_char = out[i];
1277
+ const filtration_type &point = points[i];
1278
+ /* #pragma omp parallel for reduction(+ : euler_char) */
1279
+ for (const Summand<value_type> &I : this->module_) {
1280
+ if (I.contains(point)) {
1281
+ int sign = I.get_dimension() % 2 ? -1 : 1;
1282
+ euler_char += sign;
1283
+ }
1284
+ }
1285
+ });
1286
+ return out;
1287
+ }
1288
+
1289
+ template <typename value_type>
1290
+ inline Box<value_type> Module<value_type>::get_bounds() const {
1291
+ dimension_type num_parameters = box_.get_lower_corner().num_parameters();
1292
+ filtration_type lower_bound(num_parameters, std::numeric_limits<value_type>::infinity());
1293
+ filtration_type upper_bound(num_parameters, -std::numeric_limits<value_type>::infinity());
1294
+ for (const auto &summand : module_) {
1295
+ const auto &summand_bounds = summand.get_bounds();
1296
+ const auto &[m, M] = summand_bounds.get_bounding_corners();
1297
+ for (auto parameter = 0; parameter < num_parameters; parameter++) {
1298
+ lower_bound[parameter] = std::min(m[parameter], lower_bound[parameter]);
1299
+ upper_bound[parameter] = std::min(M[parameter], upper_bound[parameter]);
1300
+ }
1301
+ }
1302
+ return Box(lower_bound, upper_bound);
1303
+ }
1304
+
1305
+ template <typename value_type>
1306
+ inline void Module<value_type>::rescale(const std::vector<value_type> &rescale_factors, int degree) {
1307
+ for (auto &summand : module_) {
1308
+ if (degree == -1 or summand.get_dimension() == degree) summand.rescale(rescale_factors);
1309
+ }
1310
+ }
1311
+
1312
+ template <typename value_type>
1313
+ inline void Module<value_type>::translate(const std::vector<value_type> &translation, int degree) {
1314
+ for (auto &summand : module_) {
1315
+ if (degree == -1 or summand.get_dimension() == degree) summand.translate(translation);
1316
+ }
1317
+ }
1318
+
1319
+ template <typename value_type>
1320
+ inline std::vector<value_type> Module<value_type>::compute_pixels_of_degree(
1321
+ const typename module_type::iterator start,
1322
+ const typename module_type::iterator end,
1323
+ const value_type delta,
1324
+ const value_type p,
1325
+ const bool normalize,
1326
+ const Box<value_type> &box,
1327
+ const std::vector<std::vector<value_type>> &coordinates,
1328
+ const int n_jobs) {
1329
+ unsigned int num_pixels = coordinates.size();
1330
+ std::vector<value_type> out(num_pixels);
1331
+ value_type moduleWeight = 0;
1332
+ { // for Timer
1333
+ Debug::Timer timer("Computing module weight ...", verbose);
1334
+ for (auto it = start; it != end; it++) // precomputes interleaving restricted to box for all summands.
1335
+ it->get_interleaving(box);
1336
+ if (p == 0) {
1337
+ // #pragma omp parallel for reduction(+ : moduleWeight)
1338
+ for (auto it = start; it != end; it++) {
1339
+ moduleWeight += it->get_interleaving() > 0;
1340
+ }
1341
+ } else if (p != inf) {
1342
+ // #pragma omp parallel for reduction(+ : moduleWeight)
1343
+ for (auto it = start; it != end; it++) {
1344
+ // /!\ TODO deal with inf summands (for the moment, depends on the box
1345
+ // ...)
1346
+ if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1347
+ moduleWeight += std::pow(it->get_interleaving(), p);
1348
+ }
1349
+ } else {
1350
+ // #pragma omp parallel for reduction(std::max : moduleWeight)
1351
+ for (auto it = start; it != end; it++) {
1352
+ if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1353
+ moduleWeight = std::max(moduleWeight, it->get_interleaving());
1354
+ }
1355
+ }
1356
+ } // Timer death
1357
+ if (verbose) std::cout << "Module " << start->get_dimension() << " has weight : " << moduleWeight << "\n";
1358
+ if (!moduleWeight) return out;
1359
+
1360
+ if constexpr (Debug::debug)
1361
+ if (moduleWeight < 0) {
1362
+ if constexpr (Debug::debug) std::cout << "!! Negative weight !!" << std::endl;
1363
+ // image.clear();
1364
+ return {};
1365
+ }
1366
+
1367
+ oneapi::tbb::task_arena arena(n_jobs); // limits the number of threads
1368
+ arena.execute([&] {
1369
+ tbb::parallel_for(0u, num_pixels, [&](unsigned int i) {
1370
+ out[i] = _get_pixel_value(start, end, coordinates[i], delta, p, normalize, moduleWeight);
1371
+ });
1372
+ });
1373
+ return out;
1374
+ }
1375
+
1376
+ template <typename value_type>
1377
+ inline Module<int64_t> Module<value_type>::grid_squeeze(const std::vector<std::vector<value_type>> &grid) const {
1378
+ auto dimension = this->get_dimension();
1379
+ Module<int64_t> out(this->size());
1380
+ for (auto i = 0u; i < this->size(); ++i) {
1381
+ const auto &interval = this->operator[](i);
1382
+ out[i] = interval.grid_squeeze(grid);
1383
+ }
1384
+ return out;
1385
+ }
1386
+
1387
+ template <typename value_type>
1388
+ inline Summand<int64_t> Summand<value_type>::grid_squeeze(const std::vector<std::vector<value_type>> &grid) const {
1389
+ auto dimension = this->get_dimension();
1390
+ Summand<int64_t> out(
1391
+ compute_coordinates_in_grid(birth_corners_, grid), compute_coordinates_in_grid(death_corners_, grid), dimension_);
1392
+ return out;
1393
+ }
1394
+
1395
+ /**
1396
+ * dim, summand, (birth/death), num_pts, num_parameters
1397
+ */
1398
+ template <typename value_type>
1399
+ inline typename Module<value_type>::idx_dump_type Module<value_type>::to_idx(
1400
+ const std::vector<std::vector<value_type>> &grid) const {
1401
+ unsigned int num_parameters = grid.size();
1402
+ auto dimension = this->get_dimension();
1403
+ idx_dump_type out(dimension + 1);
1404
+ for (auto i = 0u; i < this->size(); ++i) {
1405
+ auto &interval = this->operator[](i);
1406
+ auto &out_of_dim = out[interval.get_dimension()];
1407
+ out_of_dim.reserve(this->size());
1408
+ std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>> interval_idx;
1409
+
1410
+ auto &birth_idx = interval_idx.first;
1411
+ birth_idx.reserve(interval.get_birth_list().size());
1412
+ auto &death_idx = interval_idx.second;
1413
+ death_idx.reserve(interval.get_death_list().size());
1414
+
1415
+ for (const auto &pt : interval.get_birth_list()) {
1416
+ std::vector<int> pt_idx(pt.size());
1417
+ for (auto i = 0u; i < num_parameters; ++i) {
1418
+ pt_idx[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1419
+ }
1420
+ birth_idx.push_back(pt_idx);
1421
+ }
1422
+ for (const auto &pt : interval.get_death_list()) {
1423
+ std::vector<int> pt_idx(pt.size());
1424
+ for (auto i = 0u; i < num_parameters; ++i) {
1425
+ pt_idx[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1426
+ }
1427
+ death_idx.push_back(pt_idx);
1428
+ }
1429
+ out_of_dim.push_back(interval_idx);
1430
+ }
1431
+ return out;
1432
+ }
1433
+
1434
+ template <typename value_type>
1435
+ std::vector<int> inline to_grid_coord(const Gudhi::multi_filtration::One_critical_filtration<value_type> &pt,
1436
+ const std::vector<std::vector<value_type>> &grid) {
1437
+ std::size_t num_parameters = grid.size();
1438
+ std::vector<int> out(num_parameters);
1439
+ if (pt.is_plus_inf() || pt.is_nan()) [[unlikely]] {
1440
+ for (size_t i = 0; i < num_parameters; ++i) out[i] = grid[i].size() - 1;
1441
+ return out;
1442
+ }
1443
+ if (pt.is_minus_inf()) [[unlikely]] {
1444
+ for (size_t i = 0; i < num_parameters; ++i) out[i] = 0;
1445
+ return out;
1446
+ }
1447
+ // pt has to be of size num_parameters now
1448
+ for (size_t i = 0u; i < num_parameters; ++i) {
1449
+ if (pt[i] >= grid[i].back()) [[unlikely]]
1450
+ out[i] = grid[i].size() - 1;
1451
+ else if (pt[i] <= grid[i][0]) [[unlikely]] {
1452
+ out[i] = 0;
1453
+ } else
1454
+ out[i] = std::distance(grid[i].begin(), std::lower_bound(grid[i].begin(), grid[i].end(), pt[i]));
1455
+ }
1456
+ return out;
1457
+ }
1458
+
1459
+ template <typename value_type>
1460
+ std::vector<std::vector<std::vector<int>>> inline Module<value_type>::to_flat_idx(
1461
+ const std::vector<std::vector<value_type>> &grid) const {
1462
+ std::vector<std::vector<std::vector<int>>> out(3);
1463
+ auto &idx = out[0];
1464
+ auto &births = out[1];
1465
+ auto &deaths = out[2];
1466
+
1467
+ idx.resize(2);
1468
+ idx[0].resize(this->size());
1469
+ idx[1].resize(this->size());
1470
+
1471
+ // some heuristic: usually
1472
+ births.reserve(2 * this->size());
1473
+ deaths.reserve(2 * this->size());
1474
+ for (auto i = 0u; i < this->size(); ++i) {
1475
+ auto &interval = this->operator[](i);
1476
+ idx[0][i] = interval.get_birth_list().size();
1477
+ for (const auto &pt : interval.get_birth_list()) {
1478
+ births.push_back(to_grid_coord(pt, grid));
1479
+ }
1480
+ idx[1][i] = interval.get_death_list().size();
1481
+ for (const auto &pt : interval.get_death_list()) {
1482
+ deaths.push_back(to_grid_coord(pt, grid));
1483
+ }
1484
+ }
1485
+ return out;
1486
+ }
1487
+
1488
+ template <typename value_type>
1489
+ std::vector<int> inline Module<value_type>::get_degree_splits() const {
1490
+ std::vector<int> splits = {};
1491
+ int current_degree = 0;
1492
+ for (auto i = 0u; i < this->size(); ++i) {
1493
+ const auto &summand = this->operator[](i);
1494
+ while (summand.get_dimension() > current_degree) [[unlikely]] {
1495
+ current_degree++;
1496
+ splits.push_back(i);
1497
+ }
1498
+ }
1499
+ return splits;
1500
+ }
1501
+
1502
+ template <typename value_type>
1503
+ inline void Module<value_type>::_compute_2D_image(Module::image_type &image,
1504
+ const typename module_type::iterator start,
1505
+ const typename module_type::iterator end,
1506
+ const value_type delta,
1507
+ const value_type p,
1508
+ const bool normalize,
1509
+ const Box<value_type> &box,
1510
+ const unsigned int horizontalResolution,
1511
+ const unsigned int verticalResolution) {
1512
+ image.resize(horizontalResolution, std::vector<value_type>(verticalResolution));
1513
+ value_type moduleWeight = 0;
1514
+ { // for Timer
1515
+ Debug::Timer timer("Computing module weight ...", verbose);
1516
+ for (auto it = start; it != end; it++) // precomputes interleaving restricted to box for all summands.
1517
+ it->get_interleaving(box);
1518
+ if (p == 0) {
1519
+ /* #pragma omp parallel for reduction(+ : moduleWeight) */
1520
+ for (auto it = start; it != end; it++) {
1521
+ moduleWeight += it->get_interleaving() > 0;
1522
+ }
1523
+ } else if (p != inf) {
1524
+ /* #pragma omp parallel for reduction(+ : moduleWeight) */
1525
+ for (auto it = start; it != end; it++) {
1526
+ // /!\ TODO deal with inf summands (for the moment, depends on the box
1527
+ // ...)
1528
+ if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1529
+ moduleWeight += std::pow(it->get_interleaving(), p);
1530
+ }
1531
+ } else {
1532
+ /* #pragma omp parallel for reduction(std::max : moduleWeight) */
1533
+ for (auto it = start; it != end; it++) {
1534
+ if (it->get_interleaving() > 0 && it->get_interleaving() != inf)
1535
+ moduleWeight = std::max(moduleWeight, it->get_interleaving());
1536
+ }
1537
+ }
1538
+ } // Timer death
1539
+ if (verbose) std::cout << "Module " << start->get_dimension() << " has weight : " << moduleWeight << "\n";
1540
+ if (!moduleWeight) return;
1541
+
1542
+ if constexpr (Debug::debug)
1543
+ if (moduleWeight < 0) {
1544
+ if constexpr (Debug::debug) std::cout << "!! Negative weight !!" << std::endl;
1545
+ // image.clear();
1546
+ return;
1547
+ }
1548
+
1549
+ value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / horizontalResolution;
1550
+ value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / verticalResolution;
1551
+
1552
+ { // for Timer
1553
+ Debug::Timer timer("Computing pixel values ...", verbose);
1554
+
1555
+ tbb::parallel_for(0U, horizontalResolution, [&](unsigned int i) {
1556
+ tbb::parallel_for(0U, verticalResolution, [&](unsigned int j) {
1557
+ image[i][j] = _get_pixel_value(start,
1558
+ end,
1559
+ {box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j},
1560
+ delta,
1561
+ p,
1562
+ normalize,
1563
+ moduleWeight);
1564
+ });
1565
+ });
1566
+ } // Timer death
1567
+ }
1568
+
1569
+ template <typename value_type>
1570
+ inline void Module<value_type>::_compute_2D_image(Module::image_type &image,
1571
+ const typename module_type::const_iterator start,
1572
+ const typename module_type::const_iterator end,
1573
+ unsigned int horizontalResolution,
1574
+ unsigned int verticalResolution,
1575
+ get_2dpixel_value_function_type get_pixel_value) const {
1576
+ image.resize(horizontalResolution, std::vector<value_type>(verticalResolution));
1577
+ const Box<value_type> &box = this->box_;
1578
+ value_type stepX = (box.get_upper_corner()[0] - box.get_lower_corner()[0]) / horizontalResolution;
1579
+ value_type stepY = (box.get_upper_corner()[1] - box.get_lower_corner()[1]) / verticalResolution;
1580
+
1581
+ { // for Timer
1582
+ Debug::Timer timer("Computing pixel values ...", verbose);
1583
+
1584
+ // #pragma omp parallel for collapse(2)
1585
+ // for (unsigned int i = 0; i < horizontalResolution; i++)
1586
+ // {
1587
+ // for (unsigned int j = 0; j < verticalResolution;
1588
+ // j++)
1589
+ // {
1590
+ // image[i][j] = get_pixel_value(
1591
+ // start,
1592
+ // end,
1593
+ // box.get_lower_corner()[0] +
1594
+ // stepX * i,
1595
+ // box.get_lower_corner()[1] + stepY * j);
1596
+ // }
1597
+ // }
1598
+ tbb::parallel_for(0U, horizontalResolution, [&](unsigned int i) {
1599
+ tbb::parallel_for(0U, verticalResolution, [&](unsigned int j) {
1600
+ image[i][j] =
1601
+ get_pixel_value(start, end, box.get_lower_corner()[0] + stepX * i, box.get_lower_corner()[1] + stepY * j);
1602
+ });
1603
+ });
1604
+
1605
+ } // Timer death
1606
+ }
1607
+
1608
+ template <typename value_type>
1609
+ inline value_type Module<value_type>::_get_pixel_value(const typename module_type::iterator start,
1610
+ const typename module_type::iterator end,
1611
+ const filtration_type x,
1612
+ const value_type delta,
1613
+ const value_type p,
1614
+ const bool normalize,
1615
+ const value_type moduleWeight) const {
1616
+ value_type value = 0;
1617
+ if (p == 0) {
1618
+ /* #pragma omp parallel for reduction(+ : value) */
1619
+ for (auto it = start; it != end; it++) {
1620
+ value += it->get_local_weight(x, delta);
1621
+ }
1622
+ if (normalize) value /= moduleWeight;
1623
+ return value;
1624
+ }
1625
+ if (p != inf) {
1626
+ /* #pragma omp parallel for reduction(+ : value) */
1627
+ for (auto it = start; it != end; it++) {
1628
+ value_type summandWeight = it->get_interleaving();
1629
+ value_type summandXWeight = it->get_local_weight(x, delta);
1630
+ value += std::pow(summandWeight, p) * summandXWeight;
1631
+ }
1632
+ if (normalize) value /= moduleWeight;
1633
+ return value;
1634
+ }
1635
+
1636
+ /* #pragma omp parallel for reduction(std::max : value) */
1637
+ for (auto it = start; it != end; it++) {
1638
+ value = std::max(value, it->get_local_weight(x, delta));
1639
+ }
1640
+ return value;
1641
+ }
1642
+
1643
+ /////////////////////////////////////////////////
1644
+
1645
+ template <typename value_type>
1646
+ inline Summand<value_type>::Summand()
1647
+ : birth_corners_(1, births_type::Generator::T_inf),
1648
+ death_corners_(1, -births_type::Generator::T_inf),
1649
+ distanceTo0_(-1),
1650
+ dimension_(-1) {}
1651
+
1652
+ template <typename value_type>
1653
+ inline Summand<value_type>::Summand(
1654
+ const typename std::vector<typename Summand<value_type>::filtration_type> &birth_corners,
1655
+ const typename std::vector<typename Summand<value_type>::filtration_type> &death_corners,
1656
+ dimension_type dimension)
1657
+ : birth_corners_(birth_corners), death_corners_(death_corners), distanceTo0_(-1), dimension_(dimension) {}
1658
+
1659
+ template <typename value_type>
1660
+ inline bool Summand<value_type>::contains(const filtration_type &x) const {
1661
+ bool out = false;
1662
+ for (const auto &birth : this->birth_corners_) { // checks if there exists a birth smaller than x
1663
+ if (birth <= x) {
1664
+ out = true;
1665
+ break;
1666
+ }
1667
+ }
1668
+ if (!out) return false;
1669
+ out = false;
1670
+ for (const auto &death : this->death_corners_) {
1671
+ if (x <= death) {
1672
+ out = true;
1673
+ break;
1674
+ }
1675
+ }
1676
+ return out;
1677
+ }
1678
+
1679
+ template <typename value_type>
1680
+ inline Summand<value_type>::Summand(const typename Summand<value_type>::births_type &birth_corners,
1681
+ const typename Summand<value_type>::deaths_type &death_corners,
1682
+ dimension_type dimension)
1683
+ : birth_corners_(birth_corners), death_corners_(death_corners), distanceTo0_(-1), dimension_(dimension) {}
1684
+
1685
+ template <typename value_type>
1686
+ inline value_type Summand<value_type>::get_interleaving(const Box<value_type> &box) {
1687
+ _compute_interleaving(box);
1688
+ return distanceTo0_;
1689
+ }
1690
+
1691
+ template <typename value_type>
1692
+ inline value_type Summand<value_type>::get_interleaving() const {
1693
+ return distanceTo0_;
1694
+ }
1695
+
1696
+ template <typename value_type>
1697
+ inline value_type Summand<value_type>::get_local_weight(const filtration_type &x, const value_type delta) const {
1698
+ bool rectangle = delta <= 0;
1699
+
1700
+ // TODO: add assert to verify that x.size == birth.size/death.size
1701
+ // if they are not infinite.
1702
+
1703
+ filtration_type mini(x.num_parameters());
1704
+ filtration_type maxi(x.num_parameters());
1705
+
1706
+ // box on which to compute the local weight
1707
+ for (unsigned int i = 0; i < x.size(); i++) {
1708
+ mini[i] = delta <= 0 ? x[i] + delta : x[i] - delta;
1709
+ maxi[i] = delta <= 0 ? x[i] - delta : x[i] + delta;
1710
+ }
1711
+
1712
+ // Pre-allocating
1713
+ std::vector<filtration_type> birthList(birth_corners_.num_generators());
1714
+ std::vector<filtration_type> deathList(death_corners_.num_generators());
1715
+ unsigned int lastEntry = 0;
1716
+ for (const filtration_type &birth : birth_corners_) {
1717
+ if (birth <= maxi) {
1718
+ unsigned int dim = std::max(birth.num_parameters(), mini.num_parameters());
1719
+ filtration_type tmpBirth(dim);
1720
+ for (unsigned int i = 0; i < dim; i++) {
1721
+ auto birthi = birth.num_parameters() > i ? birth[i] : birth[0];
1722
+ auto minii = mini.num_parameters() > i ? mini[i] : mini[0];
1723
+ tmpBirth[i] = std::max(birthi, minii);
1724
+ }
1725
+
1726
+ birthList[lastEntry].swap(tmpBirth);
1727
+ lastEntry++;
1728
+ }
1729
+ }
1730
+ birthList.resize(lastEntry);
1731
+
1732
+ // Thresholds birthlist & deathlist to B_inf(x,delta)
1733
+ lastEntry = 0;
1734
+ for (const filtration_type &death : death_corners_) {
1735
+ if (death >= mini) {
1736
+ unsigned int dim = std::max(death.num_parameters(), maxi.num_parameters());
1737
+ filtration_type tmpDeath(dim);
1738
+ for (unsigned int i = 0; i < dim; i++) {
1739
+ auto deathi = death.num_parameters() > i ? death[i] : death[0];
1740
+ auto maxii = maxi.num_parameters() > i ? maxi[i] : maxi[0];
1741
+ tmpDeath[i] = std::min(deathi, maxii);
1742
+ }
1743
+
1744
+ deathList[lastEntry].swap(tmpDeath);
1745
+ lastEntry++;
1746
+ }
1747
+ }
1748
+ deathList.resize(lastEntry);
1749
+ value_type local_weight = 0;
1750
+ if (!rectangle) {
1751
+ // Local weight is inteleaving to 0 of module restricted to the square
1752
+ // #pragma omp parallel for reduction(std::max: local_weight)
1753
+ Box<value_type> trivial_box;
1754
+ for (const filtration_type &birth : birthList) {
1755
+ if (birth.num_parameters() == 0) continue;
1756
+ for (const filtration_type &death : deathList) {
1757
+ if (death.num_parameters() > 0)
1758
+ local_weight = std::max(local_weight,
1759
+ _get_max_diagonal(birth,
1760
+ death,
1761
+ trivial_box)); // if box is empty, does not thredhold
1762
+ // (already done before).
1763
+ }
1764
+ }
1765
+ return local_weight / (2 * std::abs(delta));
1766
+ } else {
1767
+ // local weight is the volume of the largest rectangle in the restricted
1768
+ // module #pragma omp parallel for reduction(std::max: local_weight)
1769
+ for (const filtration_type &birth : birthList) {
1770
+ if (birth.num_parameters() == 0) continue;
1771
+ for (const filtration_type &death : deathList) {
1772
+ if (death.num_parameters() > 0) local_weight = std::max(local_weight, _rectangle_volume(birth, death));
1773
+ }
1774
+ }
1775
+ return local_weight / std::pow(2 * std::abs(delta), x.num_parameters());
1776
+ }
1777
+ }
1778
+
1779
+ template <typename value_type>
1780
+ inline std::tuple<int, int> Summand<value_type>::distance_idx_to_lower(const filtration_type &x) const {
1781
+ value_type distance_to_lower = std::numeric_limits<value_type>::infinity();
1782
+ int b_idx = -1; // argmin_b max_i (b-x)_x
1783
+ int param = 0;
1784
+ auto count = 0u;
1785
+ for (const auto &birth : birth_corners_) {
1786
+ value_type temp = -std::numeric_limits<value_type>::infinity(); // max_i(birth - x)_+
1787
+ int temp_idx = 0;
1788
+ for (auto i = 0u; i < birth.size(); ++i) {
1789
+ auto plus = birth[i] - x[i];
1790
+ if (plus > temp) {
1791
+ temp_idx = i;
1792
+ temp = plus;
1793
+ }
1794
+ }
1795
+ if (temp < distance_to_lower) {
1796
+ distance_to_lower = temp;
1797
+ param = temp_idx;
1798
+ b_idx = count;
1799
+ }
1800
+ ++count;
1801
+ }
1802
+ return {b_idx, param};
1803
+ }
1804
+
1805
+ template <typename value_type>
1806
+ inline std::tuple<int, int> Summand<value_type>::distance_idx_to_upper(const filtration_type &x) const {
1807
+ value_type distance_to_upper = std::numeric_limits<value_type>::infinity();
1808
+ int d_idx = -1; // argmin_d max_i (x-death)
1809
+ int param = 0;
1810
+ auto count = 0u;
1811
+ for (const auto &death : death_corners_) {
1812
+ value_type temp = -std::numeric_limits<value_type>::infinity(); // max_i(death-x)_+
1813
+ int temp_idx = 0;
1814
+ for (auto i = 0u; i < death.size(); ++i) {
1815
+ auto plus = x[i] - death[i];
1816
+ if (plus > temp) {
1817
+ temp_idx = i;
1818
+ temp = plus;
1819
+ }
1820
+ }
1821
+ if (temp < distance_to_upper) {
1822
+ distance_to_upper = temp;
1823
+ param = temp_idx;
1824
+ d_idx = count;
1825
+ }
1826
+ ++count;
1827
+ }
1828
+ return {d_idx, param};
1829
+ }
1830
+
1831
+ template <typename value_type>
1832
+ inline std::vector<int> Summand<value_type>::distance_idx_to(const filtration_type &x, bool full) const {
1833
+ const auto &[a, b] = Summand::distance_idx_to_lower(x);
1834
+ const auto &[c, d] = Summand::distance_idx_to_upper(x);
1835
+ if (full) [[unlikely]]
1836
+ return {a, b, c, d};
1837
+ else {
1838
+ return {a, c};
1839
+ }
1840
+ }
1841
+
1842
+ template <typename value_type>
1843
+ inline value_type Summand<value_type>::distance_to_lower(const filtration_type &x, bool negative) const {
1844
+ value_type distance_to_lower = std::numeric_limits<value_type>::infinity();
1845
+ for (const auto &birth : birth_corners_) {
1846
+ value_type temp = negative ? -std::numeric_limits<value_type>::infinity() : 0;
1847
+ for (auto i = 0u; i < birth.size(); ++i) {
1848
+ temp = std::max(temp, birth[i] - x[i]);
1849
+ }
1850
+ distance_to_lower = std::min(distance_to_lower, temp);
1851
+ }
1852
+ return distance_to_lower;
1853
+ }
1854
+
1855
+ template <typename value_type>
1856
+ inline value_type Summand<value_type>::distance_to_upper(const filtration_type &x, bool negative) const {
1857
+ value_type distance_to_upper = std::numeric_limits<value_type>::infinity();
1858
+ for (const auto &death : death_corners_) {
1859
+ value_type temp = negative ? -std::numeric_limits<value_type>::infinity() : 0;
1860
+ for (auto i = 0u; i < death.size(); ++i) {
1861
+ temp = std::max(temp, x[i] - death[i]);
1862
+ }
1863
+ distance_to_upper = std::min(distance_to_upper, temp);
1864
+ }
1865
+ return distance_to_upper;
1866
+ }
1867
+
1868
+ template <typename value_type>
1869
+ inline value_type Summand<value_type>::distance_to(const filtration_type &x, bool negative) const {
1870
+ return std::max(Summand::distance_to_lower(x, negative), Summand::distance_to_upper(x, negative));
1871
+ }
1872
+
1873
+ template <typename value_type>
1874
+ inline std::pair<value_type, value_type> Summand<value_type>::get_bar2(const Line<value_type> &l) const {
1875
+ constexpr const bool verbose = false;
1876
+ if constexpr (verbose)
1877
+ std::cout << "Computing bar of this summand of dimension " << this->get_dimension() << std::endl;
1878
+ value_type pushed_birth = std::numeric_limits<value_type>::infinity();
1879
+ value_type pushed_death = -pushed_birth;
1880
+ for (filtration_type birth : this->get_birth_list()) {
1881
+ value_type pb = l.compute_forward_intersection(birth);
1882
+ pushed_birth = std::min(pb, pushed_birth);
1883
+ }
1884
+ //
1885
+ for (const filtration_type &death : this->get_death_list()) {
1886
+ value_type pd = l.compute_backward_intersection(death);
1887
+ pushed_death = std::max(pd, pushed_death);
1888
+ }
1889
+
1890
+ if (!(pushed_birth <= pushed_death)) {
1891
+ if constexpr (verbose) std::cout << "Birth <!= Death ! Ignoring this value" << std::endl;
1892
+ return {inf, inf};
1893
+ }
1894
+ if constexpr (verbose) {
1895
+ std::cout << "Final values" << pushed_birth << " ----- " << pushed_death << std::endl;
1896
+ }
1897
+ return {pushed_birth, pushed_death};
1898
+ }
1899
+
1900
+ template <typename value_type>
1901
+ inline std::pair<typename Summand<value_type>::filtration_type, typename Summand<value_type>::filtration_type>
1902
+ Summand<value_type>::get_bar(const Line<value_type> &l) const {
1903
+ constexpr const bool verbose = false;
1904
+ if constexpr (verbose)
1905
+ std::cout << "Computing bar of this summand of dimension " << this->get_dimension() << std::endl;
1906
+ filtration_type pushed_birth = std::numeric_limits<filtration_type>::infinity();
1907
+ filtration_type pushed_death = std::numeric_limits<filtration_type>::minus_infinity();
1908
+ for (filtration_type birth : this->get_birth_list()) {
1909
+ filtration_type pb = l[l.compute_forward_intersection(birth)];
1910
+ if constexpr (verbose)
1911
+ std::cout << "Updating birth " << pushed_birth << " with " << pb << " pushed at " << birth << " "
1912
+ << pushed_birth.is_plus_inf();
1913
+ if ((pb <= pushed_birth) || pushed_birth.is_plus_inf()) {
1914
+ pushed_birth.swap(pb);
1915
+ if constexpr (verbose) std::cout << " swapped !";
1916
+ }
1917
+ if constexpr (verbose) std::cout << std::endl;
1918
+ }
1919
+ //
1920
+ for (const filtration_type &death : this->get_death_list()) {
1921
+ filtration_type pd = l[l.compute_backward_intersection(death)];
1922
+ if constexpr (verbose)
1923
+ std::cout << "Updating death " << pushed_death << " with " << pd << " pushed at " << death << " "
1924
+ << pushed_death.is_minus_inf() << pushed_death[0];
1925
+ if ((pd >= pushed_death) || pushed_death.is_minus_inf()) {
1926
+ pushed_death.swap(pd);
1927
+ if constexpr (verbose) std::cout << " swapped !";
1928
+ }
1929
+ if constexpr (verbose) std::cout << std::endl;
1930
+ }
1931
+
1932
+ if (!(pushed_birth <= pushed_death)) {
1933
+ if constexpr (verbose) std::cout << "Birth <!= Death ! Ignoring this value" << std::endl;
1934
+ return {std::numeric_limits<filtration_type>::infinity(), std::numeric_limits<filtration_type>::infinity()};
1935
+ }
1936
+ if constexpr (verbose) {
1937
+ std::cout << "Final values" << pushed_birth << " ----- " << pushed_death << std::endl;
1938
+ }
1939
+ return {pushed_birth, pushed_death};
1940
+ }
1941
+
1942
+ /**
1943
+ * @brief Adds the bar @p bar to the indicator module @p summand if @p bar
1944
+ * is non-trivial (ie. not reduced to a point or, if @p threshold is true,
1945
+ * its thresholded version should not be reduced to a point) .
1946
+ *
1947
+ * @param bar p_bar: to add to the support of the summand
1948
+ * @param summand p_summand: indicator module which is being completed
1949
+ * @param basepoint p_basepoint: basepoint of the line of the bar
1950
+ * @param birth p_birth: birth container (for memory optimization purposes).
1951
+ * Has to be of the size @p basepoint.size()+1.
1952
+ * @param death p_death: death container. Same purpose as @p birth but for
1953
+ * deathpoint.
1954
+ * @param threshold p_threshold: If true, will threshold the bar with @p box.
1955
+ * @param box p_box: Only useful if @p threshold is set to true.
1956
+ */
1957
+
1958
+ template <typename value_type>
1959
+ inline void Summand<value_type>::add_bar(value_type baseBirth,
1960
+ value_type baseDeath,
1961
+ const filtration_type &basepoint,
1962
+ filtration_type &birth,
1963
+ filtration_type &death,
1964
+ const bool threshold,
1965
+ const Box<value_type> &box) {
1966
+ // bar is trivial in that case
1967
+ if (baseBirth >= baseDeath) return;
1968
+ // #pragma omp simd
1969
+ // for (unsigned int j = 0; j < birth.size() - 1; j++)
1970
+ // {
1971
+ // birth[j] = basepoint[j] + baseBirth;
1972
+ // death[j] = basepoint[j] + baseDeath;
1973
+ // }
1974
+ // birth.back() = baseBirth;
1975
+ // death.back() = baseDeath;
1976
+
1977
+ /* #pragma omp simd */
1978
+ for (unsigned int j = 0; j < birth.size() - 1; j++) {
1979
+ value_type temp = basepoint[j] + baseBirth;
1980
+ // The box is assumed to contain all of the filtration values, if its
1981
+ // outside, its inf.
1982
+ birth[j] = temp < box.get_lower_corner()[j] ? negInf : temp;
1983
+ temp = basepoint[j] + baseDeath;
1984
+ death[j] = temp > box.get_upper_corner()[j] ? inf : temp;
1985
+ }
1986
+ birth.back() = baseBirth < box.get_lower_corner().back() ? negInf : baseBirth;
1987
+ death.back() = baseDeath > box.get_upper_corner().back() ? inf : baseDeath;
1988
+
1989
+ if (threshold) {
1990
+ // std::cout << box;
1991
+ threshold_down(birth, box, basepoint);
1992
+ threshold_up(death, box, basepoint);
1993
+ }
1994
+ _add_birth(birth);
1995
+ _add_death(death);
1996
+ }
1997
+
1998
+ template <typename value_type>
1999
+ inline void Summand<value_type>::add_bar(const filtration_type &birth, const filtration_type &death) {
2000
+ _add_birth(birth);
2001
+ _add_death(death);
2002
+ }
2003
+
2004
+ template <typename value_type>
2005
+ inline void Summand<value_type>::add_bar(const filtration_type &basepoint,
2006
+ value_type birth,
2007
+ value_type death,
2008
+ const Box<value_type> &box) {
2009
+ constexpr const bool verbose = false;
2010
+ if (birth >= death) return;
2011
+ if constexpr (verbose) {
2012
+ std::cout << "Bar : " << basepoint + birth << "--" << basepoint + death << std::endl;
2013
+ }
2014
+ auto inf = std::numeric_limits<value_type>::infinity();
2015
+ auto container = basepoint + birth;
2016
+ for (auto i = 0u; i < container.size(); i++) {
2017
+ if (container[i] < box.get_lower_corner()[i]) container[i] = -inf;
2018
+ }
2019
+ _add_birth(container);
2020
+ container = basepoint + death;
2021
+ for (auto i = 0u; i < container.size(); i++) {
2022
+ if (container[i] > box.get_upper_corner()[i]) container[i] = inf;
2023
+ }
2024
+ _add_death(container);
2025
+ }
2026
+
2027
+ template <typename value_type>
2028
+ inline const std::vector<typename Summand<value_type>::filtration_type> &Summand<value_type>::get_birth_list() const {
2029
+ return birth_corners_.get_underlying_container();
2030
+ }
2031
+
2032
+ template <typename value_type>
2033
+ inline const std::vector<typename Summand<value_type>::filtration_type> &Summand<value_type>::get_death_list() const {
2034
+ return death_corners_.get_underlying_container();
2035
+ }
2036
+
2037
+ template <typename value_type>
2038
+ const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &Summand<value_type>::get_upset() const {
2039
+ return birth_corners_;
2040
+ }
2041
+
2042
+ template <typename value_type>
2043
+ const Gudhi::multi_filtration::Multi_critical_filtration<value_type> &Summand<value_type>::get_downset() const {
2044
+ return death_corners_;
2045
+ };
2046
+
2047
+ template <typename value_type>
2048
+ inline void Summand<value_type>::clean() {
2049
+ // birth_corners_.erase(
2050
+ // std::remove_if(birth_corners_.begin(), birth_corners_.end(),
2051
+ // [](const std::vector<value_type> &bp) {
2052
+ // return std::any_of(
2053
+ // bp.begin(), bp.end(),
2054
+ // [](float value) { return !std::isfinite(value); });
2055
+ // }),
2056
+ // birth_corners_.end());
2057
+ // TODO : clean
2058
+ }
2059
+
2060
+ template <typename value_type>
2061
+ inline void Summand<value_type>::complete_birth(const value_type precision) {
2062
+ if (!birth_corners_.is_finite()) return;
2063
+
2064
+ for (std::size_t i = 0; i < birth_corners_.num_generators(); i++) {
2065
+ for (std::size_t j = i + 1; j < birth_corners_.num_generators(); j++) {
2066
+ value_type dinf = d_inf(birth_corners_[i], birth_corners_[j]);
2067
+ if (dinf < .99 * precision) { // for machine error ?
2068
+ _factorize_min(birth_corners_[i], birth_corners_[j]);
2069
+ birth_corners_[j].clear();
2070
+ i++;
2071
+ }
2072
+ }
2073
+ }
2074
+ _clean(birth_corners_);
2075
+ }
2076
+
2077
+ template <typename value_type>
2078
+ inline void Summand<value_type>::complete_death(const value_type precision) {
2079
+ if (!death_corners_.is_finite()) return;
2080
+
2081
+ for (std::size_t i = 0; i < death_corners_.num_generators(); i++) {
2082
+ for (std::size_t j = i + 1; j < death_corners_.num_generators(); j++) {
2083
+ value_type d = d_inf(death_corners_[i], death_corners_[j]);
2084
+ if (d < .99 * precision) {
2085
+ _factorize_max(death_corners_[i], death_corners_[j]);
2086
+ death_corners_[j].clear();
2087
+ i++;
2088
+ }
2089
+ }
2090
+ }
2091
+ _clean(death_corners_);
2092
+ }
2093
+
2094
+ template <typename value_type>
2095
+ inline dimension_type Summand<value_type>::get_dimension() const {
2096
+ return dimension_;
2097
+ }
2098
+
2099
+ template <typename value_type>
2100
+ inline value_type Summand<value_type>::get_landscape_value(const std::vector<value_type> &x) const {
2101
+ value_type out = 0;
2102
+ Box<value_type> trivial_box;
2103
+ for (const filtration_type &b : this->birth_corners_) {
2104
+ for (const filtration_type &d : this->death_corners_) {
2105
+ value_type value =
2106
+ std::min(this->_get_max_diagonal(b, x, trivial_box), this->_get_max_diagonal(x, d, trivial_box));
2107
+ out = std::max(out, value);
2108
+ }
2109
+ }
2110
+ return out;
2111
+ }
2112
+
2113
+ template <typename value_type>
2114
+ inline void Summand<value_type>::set_dimension(dimension_type dimension) {
2115
+ dimension_ = dimension;
2116
+ }
2117
+
2118
+ template <typename value_type>
2119
+ inline void Summand<value_type>::_compute_interleaving(const Box<value_type> &box) {
2120
+ distanceTo0_ = 0;
2121
+ /* #pragma omp parallel for reduction(max : distanceTo0_) */
2122
+ for (const std::vector<value_type> &birth : birth_corners_) {
2123
+ for (const std::vector<value_type> &death : death_corners_) {
2124
+ distanceTo0_ = std::max(distanceTo0_, _get_max_diagonal(birth, death, box));
2125
+ }
2126
+ }
2127
+ }
2128
+
2129
+ /**
2130
+ * @brief Adds @p birth to the summand's @p birth_list if it is not induced
2131
+ * from the @p birth_list (ie. not comparable or smaller than another birth),
2132
+ * and removes unnecessary birthpoints (ie. birthpoints that are induced
2133
+ * by @p birth).
2134
+ *
2135
+ * @param birth_list p_birth_list: birthpoint list of a summand
2136
+ * @param birth p_birth: birth to add to the summand
2137
+ */
2138
+
2139
+ template <typename value_type>
2140
+ inline void Summand<value_type>::_add_birth(const filtration_type &birth) {
2141
+ birth_corners_.add_generator(birth);
2142
+ return;
2143
+
2144
+ // // TODO : DEPRECATE THIS OLD CODE
2145
+ // if (birth_corners_.empty()) {
2146
+ // birth_corners_.push_back(birth);
2147
+ // return;
2148
+ // }
2149
+
2150
+ // for (const auto &current_birth : birth_corners_) {
2151
+ // if (birth >= current_birth) {
2152
+ // return;
2153
+ // }
2154
+ // }
2155
+ // // this birth value is useful, we can now remove useless other filtrations
2156
+ // for (auto &current_birth : birth_corners_) {
2157
+ // if ((!current_birth.empty()) && (birth <= current_birth)) {
2158
+ // current_birth.clear();
2159
+ // }
2160
+ // }
2161
+
2162
+ // _clean(birth_corners_);
2163
+ // birth_corners_.push_back(birth);
2164
+ }
2165
+
2166
+ /**
2167
+ * @brief Adds @p death to the summand's @p death_list if it is not induced
2168
+ * from the @p death_list (ie. not comparable or greater than another death),
2169
+ * and removes unnecessary deathpoints (ie. deathpoints that are induced
2170
+ * by @p death)
2171
+ *
2172
+ * @param death_list p_death_list: List of deathpoints of a summand
2173
+ * @param death p_death: deathpoint to add to this list
2174
+ */
2175
+
2176
+ template <typename value_type>
2177
+ inline void Summand<value_type>::_add_death(const filtration_type &death) {
2178
+ death_corners_.add_generator(death);
2179
+ return;
2180
+ // // TODO: Deprecate this old code
2181
+ // if (death_corners_.empty()) {
2182
+ // death_corners_.push_back(death);
2183
+ // return;
2184
+ // }
2185
+
2186
+ // for (const auto &current_death : death_corners_) {
2187
+ // if (death <= current_death) {
2188
+ // return;
2189
+ // }
2190
+ // }
2191
+ // // this death value is useful, we can now remove useless other filtrations
2192
+ // for (auto &current_death : death_corners_) {
2193
+ // if (!current_death.empty() && (death >= current_death)) {
2194
+ // current_death.clear();
2195
+ // }
2196
+ // }
2197
+ // _clean(death_corners_);
2198
+ // death_corners_.push_back(death);
2199
+ }
2200
+
2201
+ template <typename value_type>
2202
+ inline value_type Summand<value_type>::_get_max_diagonal(const filtration_type &birth,
2203
+ const filtration_type &death,
2204
+ const Box<value_type> &box) const {
2205
+ // assumes birth and death to be never NaN
2206
+ if constexpr (Debug::debug)
2207
+ assert(!birth.is_finite || !death.is_finite || birth.size() == death.size() && "Inputs must be of the same size !");
2208
+
2209
+ value_type s = inf;
2210
+ bool threshold_flag = !box.is_trivial();
2211
+ if (threshold_flag) {
2212
+ unsigned int dim = std::max(birth.size(), box.dimension());
2213
+ for (unsigned int i = 0; i < dim; ++i) {
2214
+ value_type max_i = box.get_upper_corner().size() > i ? box.get_upper_corner()[i] : inf;
2215
+ value_type min_i = box.get_lower_corner().size() > i ? box.get_lower_corner()[i] : negInf;
2216
+ value_type t_death = death.is_plus_inf() ? max_i : (death.is_minus_inf() ? -inf : std::min(death[i], max_i));
2217
+ value_type t_birth = birth.is_plus_inf() ? inf : (birth.is_minus_inf() ? min_i : std::max(birth[i], min_i));
2218
+ s = std::min(s, t_death - t_birth);
2219
+ }
2220
+ } else {
2221
+ unsigned int dim = std::max(birth.size(), death.size());
2222
+ for (unsigned int i = 0; i < dim; i++) {
2223
+ // if they don't have the same size, then one of them has to (+/-)infinite.
2224
+ value_type t_death = death.size() > i ? death[i] : death[0]; // assumes death is never empty
2225
+ value_type t_birth = birth.size() > i ? birth[i] : birth[0]; // assumes birth is never empty
2226
+ s = std::min(s, t_death - t_birth);
2227
+ }
2228
+ }
2229
+
2230
+ return s;
2231
+ }
2232
+
2233
+ template <typename value_type>
2234
+ inline value_type Summand<value_type>::_rectangle_volume(const filtration_type &a, const filtration_type &b) const {
2235
+ if constexpr (Debug::debug) assert(a.size() == b.size() && "Inputs must be of the same size !");
2236
+ value_type s = b[0] - a[0];
2237
+ for (unsigned int i = 1; i < a.size(); i++) {
2238
+ s = s * (b[i] - a[i]);
2239
+ }
2240
+ return s;
2241
+ }
2242
+
2243
+ template <typename value_type>
2244
+ inline value_type Summand<value_type>::d_inf(const filtration_type &a, const filtration_type &b) const {
2245
+ if (a.empty() || b.empty() || a.size() != b.size()) return inf;
2246
+
2247
+ value_type d = std::abs(a[0] - b[0]);
2248
+ for (unsigned int i = 1; i < a.size(); i++) d = std::max(d, std::abs(a[i] - b[i]));
2249
+
2250
+ return d;
2251
+ }
2252
+
2253
+ template <typename value_type>
2254
+ inline void Summand<value_type>::_factorize_min(filtration_type &a, const filtration_type &b) {
2255
+ /* if (Debug::debug && (a.empty() || b.empty())) */
2256
+ /* { */
2257
+ /* std::cout << "Empty corners ??\n"; */
2258
+ /* return; */
2259
+ /* } */
2260
+
2261
+ for (unsigned int i = 0; i < std::min(b.size(), a.size()); i++) a[i] = std::min(a[i], b[i]);
2262
+ }
2263
+
2264
+ template <typename value_type>
2265
+ inline void Summand<value_type>::_factorize_max(filtration_type &a, const filtration_type &b) {
2266
+ /* if (Debug::debug && (a.empty() || b.empty())) */
2267
+ /* { */
2268
+ /* std::cout << "Empty corners ??\n"; */
2269
+ /* return; */
2270
+ /* } */
2271
+
2272
+ for (unsigned int i = 0; i < std::min(b.size(), a.size()); i++) a[i] = std::max(a[i], b[i]);
2273
+ }
2274
+
2275
+ /**
2276
+ * @brief Cleans empty entries of a corner list
2277
+ *
2278
+ * @param list corner list to clean
2279
+ * @param keep_sort If true, will keep the order of the corners,
2280
+ * with a computational overhead. Defaults to false.
2281
+ */
2282
+ // WARNING Does permute the output.
2283
+
2284
+ template <typename value_type>
2285
+ inline void Summand<value_type>::_clean(std::vector<filtration_type> &list, bool keep_inf) {
2286
+ list.erase(std::remove_if(list.begin(),
2287
+ list.end(),
2288
+ [keep_inf](filtration_type &a) {
2289
+ return a.empty() || ((!keep_inf) && (a.is_plus_inf() || a.is_minus_inf()));
2290
+ }),
2291
+ list.end());
2292
+ }
2293
+
2294
+ } // namespace mma
2295
+ } // namespace multiparameter
2296
+ } // namespace Gudhi
2297
+
2298
+ #endif // APPR