multipers 2.4.0b1__cp312-cp312-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. multipers/.dylibs/libboost_timer.dylib +0 -0
  2. multipers/.dylibs/libc++.1.0.dylib +0 -0
  3. multipers/.dylibs/libtbb.12.17.dylib +0 -0
  4. multipers/__init__.py +33 -0
  5. multipers/_signed_measure_meta.py +426 -0
  6. multipers/_slicer_meta.py +231 -0
  7. multipers/array_api/__init__.py +62 -0
  8. multipers/array_api/numpy.py +124 -0
  9. multipers/array_api/torch.py +133 -0
  10. multipers/data/MOL2.py +458 -0
  11. multipers/data/UCR.py +18 -0
  12. multipers/data/__init__.py +1 -0
  13. multipers/data/graphs.py +466 -0
  14. multipers/data/immuno_regions.py +27 -0
  15. multipers/data/minimal_presentation_to_st_bf.py +0 -0
  16. multipers/data/pytorch2simplextree.py +91 -0
  17. multipers/data/shape3d.py +101 -0
  18. multipers/data/synthetic.py +113 -0
  19. multipers/distances.py +202 -0
  20. multipers/filtration_conversions.pxd +736 -0
  21. multipers/filtration_conversions.pxd.tp +226 -0
  22. multipers/filtrations/__init__.py +21 -0
  23. multipers/filtrations/density.py +529 -0
  24. multipers/filtrations/filtrations.py +480 -0
  25. multipers/filtrations.pxd +534 -0
  26. multipers/filtrations.pxd.tp +332 -0
  27. multipers/function_rips.cpython-312-darwin.so +0 -0
  28. multipers/function_rips.pyx +104 -0
  29. multipers/grids.cpython-312-darwin.so +0 -0
  30. multipers/grids.pyx +538 -0
  31. multipers/gudhi/Persistence_slices_interface.h +213 -0
  32. multipers/gudhi/Simplex_tree_interface.h +274 -0
  33. multipers/gudhi/Simplex_tree_multi_interface.h +648 -0
  34. multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
  35. multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
  36. multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
  37. multipers/gudhi/gudhi/Debug_utils.h +52 -0
  38. multipers/gudhi/gudhi/Degree_rips_bifiltration.h +2307 -0
  39. multipers/gudhi/gudhi/Dynamic_multi_parameter_filtration.h +2524 -0
  40. multipers/gudhi/gudhi/Fields/Multi_field.h +453 -0
  41. multipers/gudhi/gudhi/Fields/Multi_field_operators.h +460 -0
  42. multipers/gudhi/gudhi/Fields/Multi_field_shared.h +444 -0
  43. multipers/gudhi/gudhi/Fields/Multi_field_small.h +584 -0
  44. multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +490 -0
  45. multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +580 -0
  46. multipers/gudhi/gudhi/Fields/Z2_field.h +391 -0
  47. multipers/gudhi/gudhi/Fields/Z2_field_operators.h +389 -0
  48. multipers/gudhi/gudhi/Fields/Zp_field.h +493 -0
  49. multipers/gudhi/gudhi/Fields/Zp_field_operators.h +384 -0
  50. multipers/gudhi/gudhi/Fields/Zp_field_shared.h +492 -0
  51. multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
  52. multipers/gudhi/gudhi/Matrix.h +2200 -0
  53. multipers/gudhi/gudhi/Multi_filtration/Multi_parameter_generator.h +1712 -0
  54. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_conversions.h +237 -0
  55. multipers/gudhi/gudhi/Multi_filtration/multi_filtration_utils.h +225 -0
  56. multipers/gudhi/gudhi/Multi_parameter_filtered_complex.h +485 -0
  57. multipers/gudhi/gudhi/Multi_parameter_filtration.h +2643 -0
  58. multipers/gudhi/gudhi/Multi_persistence/Box.h +233 -0
  59. multipers/gudhi/gudhi/Multi_persistence/Line.h +309 -0
  60. multipers/gudhi/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h +268 -0
  61. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_cohomology.h +159 -0
  62. multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_matrix.h +463 -0
  63. multipers/gudhi/gudhi/Multi_persistence/Point.h +853 -0
  64. multipers/gudhi/gudhi/Off_reader.h +173 -0
  65. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +834 -0
  66. multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +838 -0
  67. multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +833 -0
  68. multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1367 -0
  69. multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1157 -0
  70. multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +869 -0
  71. multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +905 -0
  72. multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +122 -0
  73. multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +260 -0
  74. multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +288 -0
  75. multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +170 -0
  76. multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +247 -0
  77. multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +571 -0
  78. multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +182 -0
  79. multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +130 -0
  80. multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +235 -0
  81. multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +312 -0
  82. multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1092 -0
  83. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +923 -0
  84. multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +914 -0
  85. multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +930 -0
  86. multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1071 -0
  87. multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +203 -0
  88. multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +886 -0
  89. multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +984 -0
  90. multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1213 -0
  91. multipers/gudhi/gudhi/Persistence_matrix/index_mapper.h +58 -0
  92. multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +227 -0
  93. multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +200 -0
  94. multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +166 -0
  95. multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +319 -0
  96. multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +562 -0
  97. multipers/gudhi/gudhi/Persistence_on_a_line.h +152 -0
  98. multipers/gudhi/gudhi/Persistence_on_rectangle.h +617 -0
  99. multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
  100. multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
  101. multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
  102. multipers/gudhi/gudhi/Persistent_cohomology.h +769 -0
  103. multipers/gudhi/gudhi/Points_off_io.h +171 -0
  104. multipers/gudhi/gudhi/Projective_cover_kernel.h +379 -0
  105. multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
  106. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +559 -0
  107. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
  108. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +121 -0
  109. multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
  110. multipers/gudhi/gudhi/Simplex_tree/filtration_value_utils.h +155 -0
  111. multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
  112. multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
  113. multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +60 -0
  114. multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +105 -0
  115. multipers/gudhi/gudhi/Simplex_tree.h +3170 -0
  116. multipers/gudhi/gudhi/Slicer.h +848 -0
  117. multipers/gudhi/gudhi/Thread_safe_slicer.h +393 -0
  118. multipers/gudhi/gudhi/distance_functions.h +62 -0
  119. multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
  120. multipers/gudhi/gudhi/multi_simplex_tree_helpers.h +147 -0
  121. multipers/gudhi/gudhi/persistence_interval.h +263 -0
  122. multipers/gudhi/gudhi/persistence_matrix_options.h +188 -0
  123. multipers/gudhi/gudhi/reader_utils.h +367 -0
  124. multipers/gudhi/gudhi/simple_mdspan.h +484 -0
  125. multipers/gudhi/gudhi/slicer_helpers.h +779 -0
  126. multipers/gudhi/tmp_h0_pers/mma_interface_h0.h +223 -0
  127. multipers/gudhi/tmp_h0_pers/naive_merge_tree.h +536 -0
  128. multipers/io.cpython-312-darwin.so +0 -0
  129. multipers/io.pyx +472 -0
  130. multipers/ml/__init__.py +0 -0
  131. multipers/ml/accuracies.py +90 -0
  132. multipers/ml/invariants_with_persistable.py +79 -0
  133. multipers/ml/kernels.py +176 -0
  134. multipers/ml/mma.py +713 -0
  135. multipers/ml/one.py +472 -0
  136. multipers/ml/point_clouds.py +352 -0
  137. multipers/ml/signed_measures.py +1667 -0
  138. multipers/ml/sliced_wasserstein.py +461 -0
  139. multipers/ml/tools.py +113 -0
  140. multipers/mma_structures.cpython-312-darwin.so +0 -0
  141. multipers/mma_structures.pxd +134 -0
  142. multipers/mma_structures.pyx +1483 -0
  143. multipers/mma_structures.pyx.tp +1126 -0
  144. multipers/multi_parameter_rank_invariant/diff_helpers.h +85 -0
  145. multipers/multi_parameter_rank_invariant/euler_characteristic.h +95 -0
  146. multipers/multi_parameter_rank_invariant/function_rips.h +317 -0
  147. multipers/multi_parameter_rank_invariant/hilbert_function.h +761 -0
  148. multipers/multi_parameter_rank_invariant/persistence_slices.h +149 -0
  149. multipers/multi_parameter_rank_invariant/rank_invariant.h +350 -0
  150. multipers/multiparameter_edge_collapse.py +41 -0
  151. multipers/multiparameter_module_approximation/approximation.h +2541 -0
  152. multipers/multiparameter_module_approximation/debug.h +107 -0
  153. multipers/multiparameter_module_approximation/format_python-cpp.h +292 -0
  154. multipers/multiparameter_module_approximation/utilities.h +428 -0
  155. multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
  156. multipers/multiparameter_module_approximation.pyx +286 -0
  157. multipers/ops.cpython-312-darwin.so +0 -0
  158. multipers/ops.pyx +231 -0
  159. multipers/pickle.py +89 -0
  160. multipers/plots.py +550 -0
  161. multipers/point_measure.cpython-312-darwin.so +0 -0
  162. multipers/point_measure.pyx +409 -0
  163. multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
  164. multipers/simplex_tree_multi.pxd +136 -0
  165. multipers/simplex_tree_multi.pyx +11719 -0
  166. multipers/simplex_tree_multi.pyx.tp +2102 -0
  167. multipers/slicer.cpython-312-darwin.so +0 -0
  168. multipers/slicer.pxd +2097 -0
  169. multipers/slicer.pxd.tp +263 -0
  170. multipers/slicer.pyx +13042 -0
  171. multipers/slicer.pyx.tp +1259 -0
  172. multipers/tensor/tensor.h +672 -0
  173. multipers/tensor.pxd +13 -0
  174. multipers/test.pyx +44 -0
  175. multipers/tests/__init__.py +70 -0
  176. multipers/torch/__init__.py +1 -0
  177. multipers/torch/diff_grids.py +240 -0
  178. multipers/torch/rips_density.py +310 -0
  179. multipers/vector_interface.pxd +46 -0
  180. multipers-2.4.0b1.dist-info/METADATA +131 -0
  181. multipers-2.4.0b1.dist-info/RECORD +184 -0
  182. multipers-2.4.0b1.dist-info/WHEEL +6 -0
  183. multipers-2.4.0b1.dist-info/licenses/LICENSE +21 -0
  184. multipers-2.4.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,617 @@
1
+ /* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2
+ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3
+ * Author(s): Marc Glisse
4
+ *
5
+ * Copyright (C) 2023 Inria
6
+ *
7
+ * Modification(s):
8
+ * - YYYY/MM Author: Description of the modification
9
+ */
10
+
11
+ // ds_find_set_ is inspired from code in Boost.Graph that is
12
+ //
13
+ // (C) Copyright Jeremy Siek 2004
14
+ // Distributed under the Boost Software License, Version 1.0. (See
15
+ // accompanying file LICENSE_1_0.txt or copy at
16
+ // http://www.boost.org/LICENSE_1_0.txt)
17
+
18
+
19
+ #ifndef PERSISTENCE_ON_RECTANGLE_H
20
+ #define PERSISTENCE_ON_RECTANGLE_H
21
+
22
+ #include <gudhi/Debug_utils.h>
23
+ #ifdef GUDHI_DETAILED_TIMES
24
+ #include <gudhi/Clock.h>
25
+ #endif
26
+
27
+ #include <boost/range/adaptor/reversed.hpp>
28
+
29
+ #ifdef GUDHI_USE_TBB
30
+ #include <tbb/parallel_sort.h>
31
+ #endif
32
+
33
+ #ifdef DEBUG_TRACES
34
+ #include <iostream>
35
+ #endif
36
+ #include <vector>
37
+ #include <memory>
38
+ #include <algorithm>
39
+ #include <stdexcept>
40
+ #include <cstddef>
41
+
42
+ namespace Gudhi::cubical_complex {
43
+
44
+ // When building a cubical complex from top-dimensional cells, there are
45
+ // normally more vertices than input top cells ((x+1)*(y+1) instead of x*y).
46
+ // However, the top cells along the boundary turn out to be collapsible, and we
47
+ // only need to work with (x-1)*(y-1) vertices.
48
+ template <class Filtration_value, class Index = std::size_t, bool output_index = false>
49
+ struct Persistence_on_rectangle {
50
+ // If we want to save space, we don't have to store the redundant 'first'
51
+ // field in T_with_index. However, it would slow down the primal pass.
52
+ struct T_with_index {
53
+ Filtration_value first; Index second;
54
+ T_with_index() = default;
55
+ T_with_index(Filtration_value f, Index i) : first(f), second(i) {}
56
+ bool operator<(T_with_index const& other) const {
57
+ return std::tie(first, second) < std::tie(other.first, other.second);
58
+ }
59
+ Index out() const { return second; }
60
+ };
61
+ // Don't store the index if we don't want to output it.
62
+ struct T_no_index {
63
+ Filtration_value first;
64
+ T_no_index() = default;
65
+ T_no_index(Filtration_value f, Index) : first(f) {}
66
+ bool operator<(T_no_index const& other) const { return first < other.first; }
67
+ Filtration_value out() const { return first; }
68
+ };
69
+ typedef std::conditional_t<output_index, T_with_index, T_no_index> T;
70
+
71
+ Filtration_value const* input_p;
72
+ Filtration_value input(Index i) const { return input_p[i]; }
73
+
74
+ // size_* counts the number of vertices in each direction.
75
+ Index size_x, size_y, input_size;
76
+ // The square i + dy is right above i.
77
+ Index dy;
78
+
79
+ // Squares keep their index from the input.
80
+ // Vertices have the same index as the square at their bottom left (smaller x and y)
81
+ // Store the filtration value of vertices that could be critical. We could store them
82
+ // in some map, or recompute them on demand, but this strongly affects performance.
83
+ std::unique_ptr<T[]> data_v_;
84
+ T& data_vertex(Index i){ return data_v_[i]; }
85
+ T data_vertex(Index i) const { return data_v_[i]; }
86
+
87
+ std::conditional_t<output_index, Index, Filtration_value> global_min;
88
+
89
+ // Information on a cluster
90
+ // We do not use the rank/size heuristics, they do not go well with the pre-pairing and end up slowing things down.
91
+ // We thus use the same representative for disjoint-sets and persistence (the minimum).
92
+ std::unique_ptr<Index[]> ds_parent_v_;
93
+ std::vector<Index> ds_parent_s_;
94
+ Index& ds_parent_vertex(Index n) { return ds_parent_v_[n]; }
95
+ Index& ds_parent_square(Index n) { return ds_parent_s_[n]; }
96
+
97
+ template<class Parent>
98
+ Index ds_find_set_(Index v, Parent&&ds_parent) {
99
+ // Experimentally, path halving is currently the fastest. Note that with a
100
+ // different algorithm, full compression was faster, so make sure to check
101
+ // again if the algorithm changes.
102
+ // (the setting is unusual because we start from a forest with broken ranks)
103
+ #if 0
104
+ // Full compression
105
+ Index old = v;
106
+ Index ancestor = ds_parent(v);
107
+ while (ancestor != v)
108
+ {
109
+ v = ancestor;
110
+ ancestor = ds_parent(v);
111
+ }
112
+ v = ds_parent(old);
113
+ while (ancestor != v)
114
+ {
115
+ ds_parent(old) = ancestor;
116
+ old = v;
117
+ v = ds_parent(old);
118
+ }
119
+ return ancestor;
120
+ #elif 1
121
+ // Path halving
122
+ Index parent = ds_parent(v);
123
+ Index grandparent = ds_parent(parent);
124
+ while (parent != grandparent)
125
+ {
126
+ ds_parent(v) = grandparent;
127
+ v = grandparent;
128
+ parent = ds_parent(v);
129
+ grandparent = ds_parent(parent);
130
+ }
131
+ return parent;
132
+ #elif 1
133
+ // Path splitting
134
+ Index parent = ds_parent(v);
135
+ Index grandparent = ds_parent(parent);
136
+ while (parent != grandparent)
137
+ {
138
+ ds_parent(v) = grandparent;
139
+ v = parent;
140
+ parent = grandparent;
141
+ grandparent = ds_parent(parent);
142
+ }
143
+ return parent;
144
+ #elif 1
145
+ // No compression (just for reference)
146
+ Index parent;
147
+ while (v != (parent = ds_parent(v)))
148
+ v = parent;
149
+ return v;
150
+ #endif
151
+ }
152
+ Index ds_find_set_vertex(Index v) {
153
+ return ds_find_set_(v, [this](Index i) -> Index& { return ds_parent_vertex(i); });
154
+ }
155
+ Index ds_find_set_square(Index v) {
156
+ return ds_find_set_(v, [this](Index i) -> Index& { return ds_parent_square(i); });
157
+ }
158
+
159
+ struct Edge {
160
+ T f;
161
+ Index v1, v2; // v1 < v2
162
+ Edge() = default;
163
+ Edge(T f, Index v1, Index v2) : f(f), v1(v1), v2(v2) {}
164
+ Filtration_value filt() const { return f.first; }
165
+ bool operator<(Edge const& other) const { return filt() < other.filt(); }
166
+ };
167
+ void dualize_edge(Edge& e) const {
168
+ Index new_v2 = e.v1 + (dy + 1);
169
+ e.v1 = e.v2;
170
+ e.v2 = new_v2;
171
+ };
172
+ std::vector<Edge> edges;
173
+
174
+ void init(const Filtration_value* input_, Index n_rows, Index n_cols) {
175
+ input_size = n_rows * n_cols;
176
+ input_p = input_;
177
+ #ifdef DEBUG_TRACES
178
+ std::clog << "Input\n";
179
+ for(Index i = 0; i < input_size; ++i) {
180
+ std::clog << i << '\t' << input(i) << '\n';
181
+ }
182
+ #endif
183
+ dy = n_cols;
184
+ size_x = dy - 1;
185
+ size_y = n_rows - 1;
186
+ // The unique_ptr could be std::vector, but the initialization is useless.
187
+ data_v_.reset(new T[input_size - dy - 1]); // 1 row/column less for vertices than squares
188
+ ds_parent_v_.reset(new Index[input_size - dy - 1]);
189
+ // Initializing the boundary squares to 0 is important, it represents the infinite exterior cell.
190
+ ds_parent_s_.resize(input_size);
191
+ // What is a good estimate here? For a random 1000x1000 input, we get ~311k edges. For a checkerboard, ~498k.
192
+ edges.reserve(input_size / 2);
193
+ }
194
+
195
+ bool has_larger_input(Index a, Index b, Filtration_value fb) const {
196
+ // Is passing fb useful, or would the compiler notice that it already has it available?
197
+ GUDHI_CHECK(a != b, std::logic_error("Bug in Gudhi: comparing a cell to itself"));
198
+ Filtration_value fa = input(a);
199
+ if (fb < fa) return true;
200
+ if (fa < fb) return false;
201
+ return a > b; // Arbitrary, but has to be consistent
202
+ }
203
+ void set_parent_vertex(Index child, Index parent) {
204
+ GUDHI_CHECK(child != parent, std::logic_error("Bug in Gudhi: use mark_*_critical instead of set_parent"));
205
+ ds_parent_vertex(child) = parent;
206
+ }
207
+ void set_parent_square(Index child, Index parent) {
208
+ GUDHI_CHECK(child != parent, std::logic_error("Bug in Gudhi: use mark_*_critical instead of set_parent"));
209
+ ds_parent_square(child) = parent;
210
+ }
211
+
212
+ // Locally pair simplices around each square.
213
+ // Work implicitly from input, only store the filtration value of critical vertices (squares are already in input).
214
+ // Store critical edges for later processing.
215
+ void fill_and_pair() {
216
+ Index i; // Index of the current square
217
+ Filtration_value f; // input(i)
218
+ auto mark_vertex_critical = [&](Index c) {
219
+ ds_parent_vertex(c) = c;
220
+ data_vertex(c) = T(f, i);
221
+ };
222
+ auto mark_square_critical = [&]() {
223
+ ds_parent_square(i) = i;
224
+ };
225
+ auto mark_edge_critical = [&](Index v1, Index v2) {
226
+ edges.emplace_back(T(f, i), v1, v2);
227
+ };
228
+ auto v_up_left = [&](){ return i - 1; };
229
+ auto v_up_right = [&](){ return i; };
230
+ auto v_down_left = [&](){ return i - dy - 1; };
231
+ auto v_down_right = [&](){ return i - dy; };
232
+ auto pair_square_up = [&](){ set_parent_square(i, i + dy); };
233
+ auto pair_square_down = [&](){ set_parent_square(i, i - dy); };
234
+ auto pair_square_left = [&](){ set_parent_square(i, i - 1); };
235
+ auto pair_square_right = [&](){ set_parent_square(i, i + 1); };
236
+
237
+ // Mark the corners as critical, it will be overwritten if not
238
+ i = 0; f = input(i);
239
+ mark_vertex_critical(v_up_right());
240
+ i = size_x; f = input(i);
241
+ mark_vertex_critical(v_up_left());
242
+ i = dy * size_y; f = input(i);
243
+ mark_vertex_critical(v_down_right());
244
+ i = size_x + dy * size_y; f = input(i);
245
+ mark_vertex_critical(v_down_left());
246
+
247
+ // Boundary nodes, 1st row
248
+ for(Index x = 1; x < size_x; ++x) {
249
+ i = x;
250
+ f = input(x);
251
+ if (has_larger_input(i + dy, i, f)) {
252
+ auto up_left = [&](){ return has_larger_input(i - 1, i, f) && has_larger_input(i + dy - 1, i, f); };
253
+ auto up_right = [&](){ return has_larger_input(i + 1, i, f) && has_larger_input(i + dy + 1, i, f); };
254
+ if (up_left()) {
255
+ set_parent_vertex(v_up_left(), v_up_right());
256
+ if (up_right()) mark_vertex_critical(v_up_right());
257
+ } else if (up_right()) {
258
+ set_parent_vertex(v_up_right(), v_up_left());
259
+ } else {
260
+ mark_edge_critical(v_up_left(), v_up_right());
261
+ }
262
+ }
263
+ }
264
+ // Internal rows
265
+ for(Index y = 1; y < size_y; ++y) {
266
+ // First column
267
+ {
268
+ i = y * dy;
269
+ f = input(i);
270
+ if (has_larger_input(i + 1, i, f)) {
271
+ auto down_right = [&](){ return has_larger_input(i - dy, i, f) && has_larger_input(i + 1 - dy, i, f); };
272
+ auto up_right = [&](){ return has_larger_input(i + dy, i, f) && has_larger_input(i + 1 + dy, i, f); };
273
+ if (down_right()) {
274
+ set_parent_vertex(v_down_right(), v_up_right());
275
+ if (up_right()) mark_vertex_critical(v_up_right());
276
+ } else if (up_right()) {
277
+ set_parent_vertex(v_up_right(), v_down_right());
278
+ } else {
279
+ mark_edge_critical(v_down_right(), v_up_right());
280
+ }
281
+ }
282
+ }
283
+ // Internal squares
284
+ for(Index x = 1; x < size_x; ++x) {
285
+ i = x + dy * y;
286
+ f = input(i);
287
+ // See what part of the boundary shares f
288
+ auto left = [&]() { return has_larger_input(i - 1, i, f); };
289
+ auto right = [&]() { return has_larger_input(i + 1, i, f); };
290
+ auto down = [&]() { return has_larger_input(i - dy, i, f); };
291
+ auto up = [&]() { return has_larger_input(i + dy, i, f); };
292
+ auto down_left = [&]() { return has_larger_input(i - dy - 1, i, f); };
293
+ auto up_left = [&]() { return has_larger_input(i + dy - 1, i, f); };
294
+ auto down_right = [&]() { return has_larger_input(i - dy + 1, i, f); };
295
+ auto up_right = [&]() { return has_larger_input(i + dy + 1, i, f); };
296
+ if (up()) { // u
297
+ if (left()) { // u l
298
+ if (up_left()) { // u l ul
299
+ set_parent_vertex(v_up_left(), v_up_right());
300
+ if (down()) { // U l UL d
301
+ if (down_left()) { // U l UL d dl
302
+ set_parent_vertex(v_down_left(), v_up_left());
303
+ if (right()) { // U L UL d DL r
304
+ if (down_right()) { // U L UL d DL r dr
305
+ set_parent_vertex(v_down_right(), v_down_left());
306
+ pair_square_right();
307
+ if (up_right()) { // U L UL D DL R DR ur - cr
308
+ mark_vertex_critical(v_up_right());
309
+ }
310
+ } else { // U L UL d DL r !dr
311
+ pair_square_down();
312
+ if (up_right()) { // U L UL D DL r !dr ur - cd
313
+ set_parent_vertex(v_up_right(), v_down_right());
314
+ } else { // U L UL D DL r !dr !ur - cd
315
+ mark_edge_critical(v_down_right(), v_up_right());
316
+ }
317
+ }
318
+ } else { // U L UL d DL !r
319
+ pair_square_down();
320
+ }
321
+ } else { // U l UL d !dl
322
+ pair_square_left();
323
+ if (right()) { // U L UL d !dl r - cl
324
+ if (down_right()) { // U L UL d !dl r dr - cl
325
+ set_parent_vertex(v_down_right(), v_down_left());
326
+ } else { // U L UL d !dl r !dr - cl
327
+ mark_edge_critical(v_down_left(), v_down_right());
328
+ }
329
+ if (up_right()) { // U L UL D !dl r ur - cl
330
+ set_parent_vertex(v_up_right(), v_down_right());
331
+ } else { // U L UL D !dl r !ur - cl
332
+ mark_edge_critical(v_down_right(), v_up_right());
333
+ }
334
+ } else { // U L UL d !dl !r - cl
335
+ mark_edge_critical(v_down_left(), v_down_right());
336
+ }
337
+ }
338
+ } else { // U l UL !d
339
+ pair_square_left();
340
+ if (right()) { // U L UL !d r - cl
341
+ if (up_right()) { // U L UL !d r ur - cl
342
+ set_parent_vertex(v_up_right(), v_down_right());
343
+ } else { // U L UL !d r !ur - cl
344
+ mark_edge_critical(v_down_right(), v_up_right());
345
+ }
346
+ } else {} // U L UL !d !r - cl
347
+ }
348
+ } else { // u l !ul
349
+ pair_square_up();
350
+ if (down()) { // U l !ul d - cu
351
+ if (down_left()) { // U l !ul d dl - cu
352
+ set_parent_vertex(v_down_left(), v_up_left());
353
+ } else { // U l !ul d !dl - cu
354
+ mark_edge_critical(v_down_left(), v_up_left());
355
+ }
356
+ if (right()) { // U L !ul d r - cu
357
+ if (down_right()) { // U L !ul d r dr - cu
358
+ set_parent_vertex(v_down_right(), v_down_left());
359
+ } else { // U L !ul d r !dr - cu
360
+ mark_edge_critical(v_down_left(), v_down_right());
361
+ }
362
+ if (up_right()) { // U L !ul D r ur - cu
363
+ set_parent_vertex(v_up_right(), v_down_right());
364
+ } else { // U L !ul D r !ur - cu
365
+ mark_edge_critical(v_down_right(), v_up_right());
366
+ }
367
+ } else { // U L !ul d !r - cu
368
+ mark_edge_critical(v_down_left(), v_down_right());
369
+ }
370
+ } else { // U l !ul !d - cu
371
+ mark_edge_critical(v_down_left(), v_up_left());
372
+ if (right()) { // U L !ul !d r - cu
373
+ if (up_right()) { // U L !ul !d r ur - cu
374
+ set_parent_vertex(v_up_right(), v_down_right());
375
+ } else { // U L !ul !d r !ur - cu
376
+ mark_edge_critical(v_down_right(), v_up_right());
377
+ }
378
+ } else {} // U L !ul !d !r - cu
379
+ }
380
+ }
381
+ } else { // u !l
382
+ pair_square_up();
383
+ if (down()) { // U !l d - cu
384
+ if (right()) { // U !l d r - cu
385
+ if (down_right()) { // U !l d r dr - cu
386
+ set_parent_vertex(v_down_right(), v_down_left());
387
+ } else { // U !l d r !dr - cu
388
+ mark_edge_critical(v_down_left(), v_down_right());
389
+ }
390
+ if (up_right()) { // U !l D r ur - cu
391
+ set_parent_vertex(v_up_right(), v_down_right());
392
+ } else { // U !l D r !ur - cu
393
+ mark_edge_critical(v_down_right(), v_up_right());
394
+ }
395
+ } else { // U !l d !r - cu
396
+ mark_edge_critical(v_down_left(), v_down_right());
397
+ }
398
+ } else { // U !l !d - cu
399
+ if (right()) { // U !l !d r - cu
400
+ if (up_right()) { // U !l !d r ur - cu
401
+ set_parent_vertex(v_up_right(), v_down_right());
402
+ } else { // U !l !d r !ur - cu
403
+ mark_edge_critical(v_down_right(), v_up_right());
404
+ }
405
+ } else {} // U !l !d !r - cu
406
+ }
407
+ }
408
+ } else { // !u
409
+ if (left()) { // !u l
410
+ if (down()) { // !u l d
411
+ if (down_left()) { // !u l d dl
412
+ set_parent_vertex(v_down_left(), v_up_left());
413
+ if (right()) { // !u L d DL r
414
+ if (down_right()) { // !u L d DL r dr
415
+ set_parent_vertex(v_down_right(), v_down_left());
416
+ } else { // !u L d DL r !dr
417
+ mark_edge_critical(v_down_left(), v_down_right());
418
+ }
419
+ pair_square_right();
420
+ } else { // !u L d DL !r
421
+ pair_square_down();
422
+ }
423
+ } else { // !u l d !dl
424
+ pair_square_left();
425
+ if (right()) { // !u L d !dl r - cl
426
+ if (down_right()) { // !u L d !dl r dr - cl
427
+ set_parent_vertex(v_down_right(), v_down_left());
428
+ } else { // !u L d !dl r !dr - cl
429
+ mark_edge_critical(v_down_left(), v_down_right());
430
+ }
431
+ mark_edge_critical(v_down_right(), v_up_right());
432
+ } else { // !u L d !dl !r - cl
433
+ mark_edge_critical(v_down_left(), v_down_right());
434
+ }
435
+ }
436
+ } else { // !u l !d
437
+ pair_square_left();
438
+ if (right()) { // !u L !d r - cl
439
+ mark_edge_critical(v_down_right(), v_up_right());
440
+ } else {} // !u L !d !r - cl
441
+ }
442
+ } else { // !u !l
443
+ if (down()) { // !u !l d
444
+ pair_square_down();
445
+ if (right()) { // !u !l D r - cd
446
+ if (down_right()) { // !u !l D r dr - cd
447
+ set_parent_vertex(v_down_right(), v_up_right());
448
+ } else { // !u !l D r !dr - cd
449
+ mark_edge_critical(v_down_right(), v_up_right());
450
+ }
451
+ } else {} // !u !l D !r - cd
452
+ } else { // !u !l !d
453
+ if (right()) { // !u !l !d r
454
+ pair_square_right();
455
+ } else { // !u !l !d !r
456
+ mark_square_critical();
457
+ }
458
+ }
459
+ }
460
+ }
461
+ }
462
+ // Last column
463
+ {
464
+ i = size_x + dy * y;
465
+ f = input(i);
466
+ if (has_larger_input(i - 1, i, f)) {
467
+ auto down_left = [&](){ return has_larger_input(i - dy, i, f) && has_larger_input(i - 1 - dy, i, f); };
468
+ auto up_left = [&](){ return has_larger_input(i + dy, i, f) && has_larger_input(i - 1 + dy, i, f); };
469
+ if (down_left()) {
470
+ set_parent_vertex(v_down_left(), v_up_left());
471
+ if (up_left()) mark_vertex_critical(v_up_left());
472
+ } else if (up_left()) {
473
+ set_parent_vertex(v_up_left(), v_down_left());
474
+ } else {
475
+ mark_edge_critical(v_down_left(), v_up_left());
476
+ }
477
+ }
478
+ }
479
+ }
480
+ // Boundary nodes, last row
481
+ for(Index x = 1; x < size_x; ++x) {
482
+ i = size_y * dy + x;
483
+ f = input(i);
484
+ if (has_larger_input(i - dy, i, f)) {
485
+ auto down_left = [&](){ return has_larger_input(i - 1, i, f) && has_larger_input(i - dy - 1, i, f); };
486
+ auto down_right = [&](){ return has_larger_input(i + 1, i, f) && has_larger_input(i - dy + 1, i, f); };
487
+ if (down_left()) {
488
+ set_parent_vertex(v_down_left(), v_down_right());
489
+ if (down_right()) mark_vertex_critical(v_down_right());
490
+ } else if (down_right()) {
491
+ set_parent_vertex(v_down_right(), v_down_left());
492
+ } else {
493
+ mark_edge_critical(v_down_left(), v_down_right());
494
+ }
495
+ }
496
+ }
497
+ }
498
+
499
+ void sort_edges(){
500
+ #ifdef GUDHI_USE_TBB
501
+ // Parallelizing just this part is a joke. It would be possible to
502
+ // parallelize the pairing (one edge list per thread) and run the dual in
503
+ // parallel with the primal if we were motivated...
504
+ tbb::parallel_sort(edges.begin(), edges.end());
505
+ #else
506
+ std::sort(edges.begin(), edges.end());
507
+ #endif
508
+ #ifdef DEBUG_TRACES
509
+ std::clog << "edges\n";
510
+ for(auto&e : edges){ std::clog << e.v1 << '\t' << e.v2 << '\t' << e.filt() << '\n'; }
511
+ #endif
512
+ }
513
+
514
+ template<class Out>
515
+ void primal(Out&&out){
516
+ auto it = std::remove_if(edges.begin(), edges.end(), [&](Edge& e) {
517
+ assert(e.v1 < e.v2);
518
+ Index a = ds_find_set_vertex(e.v1);
519
+ Index b = ds_find_set_vertex(e.v2);
520
+ if (a == b) return false;
521
+ if (data_vertex(b) < data_vertex(a)) std::swap(a, b);
522
+ ds_parent_vertex(b) = a;
523
+ out(data_vertex(b).out(), e.f.out());
524
+ return true;
525
+ });
526
+ edges.erase(it, edges.end());
527
+ global_min = data_vertex(ds_find_set_vertex(0)).out();
528
+ }
529
+
530
+ // In the dual, squares behave like vertices, and edges are rotated 90° around their middle.
531
+ // To handle boundaries correctly, we imagine a single exterior cell with filtration +inf.
532
+ template<class Out>
533
+ void dual(Out&&out){
534
+ for (auto e : boost::adaptors::reverse(edges)) {
535
+ dualize_edge(e);
536
+ Index a = ds_find_set_square(e.v1);
537
+ Index b = ds_find_set_square(e.v2);
538
+ GUDHI_CHECK(a != b, std::logic_error("Bug in Gudhi"));
539
+ // This is more robust in case the input contains inf? I used to set the filtration of 0 to inf.
540
+ if (b == 0 || (a != 0 && input(a) < input(b))) std::swap(a, b);
541
+ ds_parent_square(b) = a;
542
+ if constexpr (output_index)
543
+ out(e.f.out(), b);
544
+ else
545
+ out(e.f.out(), input(b));
546
+ }
547
+ }
548
+ };
549
+ // Ideas for improvement:
550
+ // * for large hard (many intervals) inputs, primal/dual dominate the running time because of the random reads in
551
+ // find_set and input(a/b). The input load would be cheaper if we stored it with parents, but then find_set would
552
+ // be slower.
553
+ // * to increase memory locality, maybe pairing, which is currently arbitrary, could use some heuristic to favor
554
+ // some pairs over others.
555
+ // * try to loosen tight dependency chains, load values several instructions before they are needed. Performing 2
556
+ // find_set in lock step surprisingly doesn't help.
557
+ // * To handle very big instances, we could remove data_v_ and recompute it on demand as the min of the inputs of i,
558
+ // i+1, i+dy and i+dy+1. On hard instances, it wouldn't save that much memory (and it is a bit slower). On easy
559
+ // instances, if we also remove the calls to reserve(), the saving is less negligible, but we still have ds_parent_*_
560
+ // that take about as much space as the input. We could, on a subarray, fill a dense ds_parent, then reduce it and
561
+ // export only the critical vertices and boundary to some sparse datastructure, but it doesn't seem worth the trouble
562
+ // for now.
563
+
564
+ /**
565
+ * @private
566
+ * Compute the persistence diagram of a function on a 2d cubical complex, defined as a lower-star filtration of the
567
+ * values at the top-dimensional cells.
568
+ *
569
+ * @tparam output_index If false, each argument of the out functors is a filtration value. If true, it is instead the
570
+ * index of this filtration value in the input.
571
+ * @tparam Filtration_value Must be comparable with `operator<`.
572
+ * @tparam Index This is used to index the elements of `input`, so it must be large enough to represent the size
573
+ * of `input`.
574
+ * @param[in] input Pointer to `n_rows*n_cols` filtration values for the square cells. Note that the values are assumed
575
+ * to be stored in C order, unlike `Gudhi::cubical_complex::Bitmap_cubical_complex` (you can exchange `n_rows` and
576
+ * `n_cols` for compatibility).
577
+ * @param[in] n_rows number of rows of `input`.
578
+ * @param[in] n_cols number of columns of `input`.
579
+ * @param[out] out0 For each interval (b, d) in the persistence diagram of dimension 0, the function calls `out0(b, d)`.
580
+ * @param[out] out1 Same as `out0` for persistence in dimension 1.
581
+ * @returns The global minimum, which is not paired and is thus the birth of an infinite persistence interval of
582
+ * dimension 0.
583
+ */
584
+ template <bool output_index = false, typename Filtration_value, typename Index, typename Out0, typename Out1>
585
+ auto persistence_on_rectangle_from_top_cells(Filtration_value const* input, Index n_rows, Index n_cols,
586
+ Out0&&out0, Out1&&out1){
587
+ #ifdef GUDHI_DETAILED_TIMES
588
+ Gudhi::Clock clock;
589
+ #endif
590
+ GUDHI_CHECK(n_rows >= 2 && n_cols >= 2,
591
+ std::domain_error("The complex must truly be 2d, i.e. at least 2 rows and 2 columns"));
592
+ Persistence_on_rectangle<Filtration_value, Index, output_index> X;
593
+ X.init(input, n_rows, n_cols);
594
+ #ifdef GUDHI_DETAILED_TIMES
595
+ std::clog << "init: " << clock; clock.begin();
596
+ #endif
597
+ X.fill_and_pair();
598
+ #ifdef GUDHI_DETAILED_TIMES
599
+ std::clog << "fill and pair: " << clock; clock.begin();
600
+ #endif
601
+ X.sort_edges();
602
+ #ifdef GUDHI_DETAILED_TIMES
603
+ std::clog << "sort: " << clock; clock.begin();
604
+ #endif
605
+ X.primal(out0);
606
+ #ifdef GUDHI_DETAILED_TIMES
607
+ std::clog << "primal pass: " << clock; clock.begin();
608
+ #endif
609
+ X.dual(out1);
610
+ #ifdef GUDHI_DETAILED_TIMES
611
+ std::clog << "dual pass: " << clock;
612
+ #endif
613
+ return X.global_min;
614
+ }
615
+ } // namespace Gudhi::cubical_complex
616
+
617
+ #endif // PERSISTENCE_ON_RECTANGLE_H