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,616 +1,616 @@
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): Hannah Schreiber
4
- *
5
- * Copyright (C) 2022-24 Inria
6
- *
7
- * Modification(s):
8
- * - YYYY/MM Author: Description of the modification
9
- */
10
-
11
- /**
12
- * @file chain_vine_swap.h
13
- * @author Hannah Schreiber
14
- * @brief Contains the @ref Gudhi::persistence_matrix::Chain_barcode_swap and
15
- * @ref Gudhi::persistence_matrix::Chain_vine_swap classes, as well as the
16
- * @ref Gudhi::persistence_matrix::Dummy_chain_vine_swap and
17
- * @ref Gudhi::persistence_matrix::Dummy_chain_vine_pairing structures.
18
- */
19
-
20
- #ifndef PM_CHAIN_VINE_SWAP_H
21
- #define PM_CHAIN_VINE_SWAP_H
22
-
23
- #include <utility> //std::swap & std::move
24
- #include <cassert>
25
- #include <functional> //std::function
26
- #include <stdexcept> //std::invalid_argument
27
-
28
- #include "chain_pairing.h"
29
-
30
- namespace Gudhi {
31
- namespace persistence_matrix {
32
-
33
- /**
34
- * @ingroup persistence_matrix
35
- *
36
- * @brief Default death comparator. Simply assumes that two positive paired columns are never swapped. Which is true
37
- * for the use case in zigzag persistence for example.
38
- *
39
- * @param columnIndex1 First column index.
40
- * @param columnIndex2 Second column index.
41
- * @return false
42
- */
43
- constexpr bool _no_G_death_comparator([[maybe_unused]] unsigned int columnIndex1,
44
- [[maybe_unused]] unsigned int columnIndex2)
45
- {
46
- return false;
47
- }
48
-
49
- /**
50
- * @ingroup persistence_matrix
51
- *
52
- * @brief Empty structure.
53
- * Inherited instead of @ref Chain_vine_swap, when vine swaps are not enabled.
54
- */
55
- struct Dummy_chain_vine_swap
56
- {
57
- friend void swap([[maybe_unused]] Dummy_chain_vine_swap& d1, [[maybe_unused]] Dummy_chain_vine_swap& d2) {}
58
-
59
- Dummy_chain_vine_swap() {}
60
- template <typename BirthComparatorFunction, typename DeathComparatorFunction>
61
- Dummy_chain_vine_swap([[maybe_unused]] const BirthComparatorFunction& birthComparator,
62
- [[maybe_unused]] const DeathComparatorFunction& deathComparator) {}
63
- };
64
-
65
- /**
66
- * @ingroup persistence_matrix
67
- *
68
- * @brief Empty structure.
69
- * Inherited instead of @ref Chain_barcode_swap, when the barcode is not stored.
70
- */
71
- struct Dummy_chain_vine_pairing
72
- {
73
- friend void swap([[maybe_unused]] Dummy_chain_vine_pairing& d1, [[maybe_unused]] Dummy_chain_vine_pairing& d2) {}
74
- };
75
-
76
- /**
77
- * @ingroup persistence_matrix
78
- *
79
- * @brief Class managing the barcode for @ref Chain_vine_swap.
80
- *
81
- * @tparam Master_matrix An instantiation of @ref Matrix from which all types and options are deduced.
82
- */
83
- template <typename Master_matrix>
84
- class Chain_barcode_swap : public Chain_pairing<Master_matrix>
85
- {
86
- public:
87
- using ID_index = typename Master_matrix::ID_index; /**< @ref IDIdx index type. */
88
- using Pos_index = typename Master_matrix::Pos_index; /**< @ref PosIdx index type. */
89
- //CP = Chain Pairing
90
- using CP = Chain_pairing<Master_matrix>;
91
-
92
- /**
93
- * @brief Default constructor.
94
- */
95
- Chain_barcode_swap(){};
96
- /**
97
- * @brief Copy constructor.
98
- *
99
- * @param toCopy Matrix to copy.
100
- */
101
- Chain_barcode_swap(const Chain_barcode_swap& toCopy)
102
- : CP(static_cast<const CP&>(toCopy)), pivotToPosition_(toCopy.pivotToPosition_){};
103
- /**
104
- * @brief Move constructor.
105
- *
106
- * @param other Matrix to move.
107
- */
108
- Chain_barcode_swap(Chain_barcode_swap&& other)
109
- : CP(std::move(static_cast<CP&>(other))), pivotToPosition_(std::move(other.pivotToPosition_)){};
110
-
111
- protected:
112
- using Dictionary = typename Master_matrix::template Dictionary<Pos_index>;
113
-
114
- Dictionary pivotToPosition_; // necessary to keep track of the barcode changes
115
-
116
- void swap_positions(ID_index pivot1, ID_index pivot2) {
117
- if constexpr (Master_matrix::Option_list::has_map_column_container) {
118
- std::swap(pivotToPosition_.at(pivot1), pivotToPosition_.at(pivot2));
119
- } else {
120
- std::swap(pivotToPosition_[pivot1], pivotToPosition_[pivot2]);
121
- }
122
- }
123
-
124
- bool is_negative_in_pair(ID_index pivot) const {
125
- Pos_index pos = _get_pivot_position(pivot);
126
- return death(pivot) == pos;
127
- }
128
-
129
- void positive_transpose(ID_index pivot1, ID_index pivot2) {
130
- Pos_index pos1 = _get_pivot_position(pivot1);
131
- Pos_index pos2 = _get_pivot_position(pivot2);
132
-
133
- _birth(pos1) = pos2;
134
- _birth(pos2) = pos1;
135
- std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
136
- }
137
-
138
- void negative_transpose(ID_index pivot1, ID_index pivot2) {
139
- Pos_index pos1 = _get_pivot_position(pivot1);
140
- Pos_index pos2 = _get_pivot_position(pivot2);
141
-
142
- _death(pos1) = pos2;
143
- _death(pos2) = pos1;
144
- std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
145
- }
146
-
147
- void positive_negative_transpose(ID_index pivot1, ID_index pivot2) {
148
- Pos_index pos1 = _get_pivot_position(pivot1);
149
- Pos_index pos2 = _get_pivot_position(pivot2);
150
-
151
- _birth(pos1) = pos2;
152
- _death(pos2) = pos1;
153
- std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
154
- }
155
-
156
- void negative_positive_transpose(ID_index pivot1, ID_index pivot2) {
157
- Pos_index pos1 = _get_pivot_position(pivot1);
158
- Pos_index pos2 = _get_pivot_position(pivot2);
159
-
160
- _death(pos1) = pos2;
161
- _birth(pos2) = pos1;
162
- std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
163
- }
164
-
165
- bool are_adjacent(ID_index pivot1, ID_index pivot2) const {
166
- Pos_index pos1 = _get_pivot_position(pivot1);
167
- Pos_index pos2 = _get_pivot_position(pivot2);
168
- return pos1 < pos2 ? (pos2 - pos1) == 1 : (pos1 - pos2) == 1;
169
- }
170
-
171
- Chain_barcode_swap& operator=(Chain_barcode_swap other) {
172
- Chain_pairing<Master_matrix>::operator=(other);
173
- pivotToPosition_.swap(other.pivotToPosition_);
174
- }
175
- friend void swap(Chain_barcode_swap& swap1, Chain_barcode_swap& swap2) {
176
- swap(static_cast<Chain_pairing<Master_matrix>&>(swap1), static_cast<Chain_pairing<Master_matrix>&>(swap2));
177
- swap1.pivotToPosition_.swap(swap2.pivotToPosition_);
178
- }
179
-
180
- Pos_index death(ID_index pivot) const {
181
- Pos_index simplexIndex = _get_pivot_position(pivot);
182
-
183
- if constexpr (Master_matrix::Option_list::has_removable_columns) {
184
- return CP::indexToBar_.at(simplexIndex)->death;
185
- } else {
186
- return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
187
- }
188
- }
189
-
190
- Pos_index birth(ID_index pivot) const {
191
- Pos_index simplexIndex = _get_pivot_position(pivot);
192
-
193
- if constexpr (Master_matrix::Option_list::has_removable_columns) {
194
- return CP::indexToBar_.at(simplexIndex)->birth;
195
- } else {
196
- return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
197
- }
198
- }
199
-
200
- private:
201
- Pos_index _get_pivot_position(ID_index pivot) const {
202
- if constexpr (Master_matrix::Option_list::has_map_column_container) {
203
- return pivotToPosition_.at(
204
- pivot); // quite often called, make public and pass position instead of pivot to avoid find() every time?
205
- } else {
206
- return pivotToPosition_[pivot];
207
- }
208
- }
209
-
210
- Pos_index& _death(Pos_index simplexIndex) {
211
- if constexpr (Master_matrix::Option_list::has_removable_columns) {
212
- return CP::indexToBar_.at(simplexIndex)->death;
213
- } else {
214
- return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
215
- }
216
- }
217
-
218
- Pos_index& _birth(Pos_index simplexIndex) {
219
- if constexpr (Master_matrix::Option_list::has_removable_columns) {
220
- return CP::indexToBar_.at(simplexIndex)->birth;
221
- } else {
222
- return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
223
- }
224
- }
225
- };
226
-
227
- /**
228
- * @class Chain_vine_swap chain_vine_swap.h gudhi/Persistence_matrix/chain_vine_swap.h
229
- * @ingroup persistence_matrix
230
- *
231
- * @brief Class managing the vine swaps for @ref Chain_matrix.
232
- *
233
- * @tparam Master_matrix An instantiation of @ref Matrix from which all types and options are deduced.
234
- */
235
- template <class Master_matrix>
236
- class Chain_vine_swap : public std::conditional<Master_matrix::Option_list::has_column_pairings,
237
- Chain_barcode_swap<Master_matrix>,
238
- Dummy_chain_vine_pairing
239
- >::type
240
- {
241
- public:
242
- using Index = typename Master_matrix::Index; /**< @ref MatIdx index type. */
243
- using ID_index = typename Master_matrix::ID_index; /**< @ref IDIdx index type. */
244
- using Pos_index = typename Master_matrix::Pos_index; /**< @ref PosIdx index type. */
245
- using Column_container = typename Master_matrix::Column_container; /**< Column container type. */
246
- using Column = typename Master_matrix::Column; /**< Column type. */
247
- typedef bool (*EventCompFuncPointer)(Pos_index, Pos_index); /**< Pointer type for birth/death comparators. */
248
-
249
- /**
250
- * @brief Default constructor. Only available if @ref PersistenceMatrixOptions::has_column_pairings is true.
251
- */
252
- Chain_vine_swap();
253
- /**
254
- * @brief Constructor storing the given comparators.
255
- *
256
- * @param birthComparator Method taking two @ref PosIdx indices as input and returning true if and only if
257
- * the birth associated to the first position is strictly less than birth associated to
258
- * the second one with respect to some self defined order. It is used while swapping two unpaired or
259
- * two negative columns.
260
- * @param deathComparator Method taking two @ref PosIdx indices as input and returning true if and only if
261
- * the death associated to the first position is strictly less than death associated to
262
- * the second one with respect to some self defined order. It is used while swapping two positive but paired
263
- * columns. Default value: @ref _no_G_death_comparator.
264
- */
265
- Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
266
- std::function<bool(Pos_index,Pos_index)> deathComparator = _no_G_death_comparator);
267
- /**
268
- * @brief Copy constructor.
269
- *
270
- * @param matrixToCopy Matrix to copy.
271
- */
272
- Chain_vine_swap(const Chain_vine_swap& matrixToCopy);
273
- /**
274
- * @brief Move constructor.
275
- *
276
- * @param other Matrix to move.
277
- */
278
- Chain_vine_swap(Chain_vine_swap&& other) noexcept;
279
-
280
- /**
281
- * @brief Does the same than @ref vine_swap, but assumes that the swap is non trivial and
282
- * therefore skips a part of the case study.
283
- *
284
- * @param columnIndex1 @ref MatIdx index of the first cell.
285
- * @param columnIndex2 @ref MatIdx index of the second cell.
286
- * @return Let \f$ pos1 \f$ be the @ref PosIdx index of @p columnIndex1 and \f$ pos2 \f$ be the @ref PosIdx index of
287
- * @p columnIndex2. The method returns the @ref MatIdx of the column which has now, after the swap, the @ref PosIdx
288
- * \f$ max(pos1, pos2) \f$.
289
- */
290
- Index vine_swap_with_z_eq_1_case(Index columnIndex1, Index columnIndex2);
291
- /**
292
- * @brief Does a vine swap between two cells which are consecutive in the filtration. Roughly, if \f$ F \f$ is
293
- * the current filtration represented by the matrix, the method modifies the matrix such that the new state
294
- * corresponds to a valid state for the filtration \f$ F' \f$ equal to \f$ F \f$ but with the two given cells
295
- * at swapped positions. Of course, the two cells should not have a face/coface relation which each other ;
296
- * \f$ F' \f$ has to be a valid filtration.
297
- * See @cite vineyards for more information about vine and vineyards.
298
- *
299
- * @param columnIndex1 @ref MatIdx index of the first cell.
300
- * @param columnIndex2 @ref MatIdx index of the second cell. It is assumed that the @ref PosIdx of both only differs
301
- * by one if the barcode is maintained.
302
- * @return Let \f$ pos1 \f$ be the @ref PosIdx index of @p columnIndex1 and \f$ pos2 \f$ be the @ref PosIdx index of
303
- * @p columnIndex2. The method returns the @ref MatIdx of the column which has now, after the swap, the @ref PosIdx
304
- * \f$ max(pos1, pos2) \f$.
305
- */
306
- Index vine_swap(Index columnIndex1, Index columnIndex2);
307
-
308
- /**
309
- * @brief Assign operator.
310
- */
311
- Chain_vine_swap& operator=(Chain_vine_swap other);
312
- /**
313
- * @brief Swap operator.
314
- */
315
- friend void swap(Chain_vine_swap& swap1, Chain_vine_swap& swap2) {
316
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
317
- swap(static_cast<Chain_barcode_swap<Master_matrix>&>(swap1),
318
- static_cast<Chain_barcode_swap<Master_matrix>&>(swap2));
319
- }
320
- std::swap(swap1.birthComp_, swap2.birthComp_);
321
- std::swap(swap1.deathComp_, swap2.deathComp_);
322
- }
323
-
324
- protected:
325
- using CP = typename std::conditional<Master_matrix::Option_list::has_column_pairings,
326
- Chain_barcode_swap<Master_matrix>,
327
- Dummy_chain_vine_pairing
328
- >::type;
329
-
330
- private:
331
- using Master_chain_matrix = typename Master_matrix::Master_chain_matrix;
332
-
333
- std::function<bool(Pos_index,Pos_index)> birthComp_; /**< for F x F & H x H. */
334
- std::function<bool(Pos_index,Pos_index)> deathComp_; /**< for G x G. */
335
-
336
- bool _is_negative_in_pair(Index columnIndex);
337
-
338
- Index _positive_vine_swap(Index columnIndex1, Index columnIndex2);
339
- Index _positive_negative_vine_swap(Index columnIndex1, Index columnIndex2);
340
- Index _negative_positive_vine_swap(Index columnIndex1, Index columnIndex2);
341
- Index _negative_vine_swap(Index columnIndex1, Index columnIndex2);
342
-
343
- constexpr Master_chain_matrix* _matrix() { return static_cast<Master_chain_matrix*>(this); }
344
- constexpr const Master_chain_matrix* _matrix() const { return static_cast<const Master_chain_matrix*>(this); }
345
- };
346
-
347
- template <class Master_matrix>
348
- inline Chain_vine_swap<Master_matrix>::Chain_vine_swap() : CP(), birthComp_(), deathComp_()
349
- {
350
- static_assert(Master_matrix::Option_list::has_column_pairings,
351
- "If barcode is not stored, at least a birth comparator has to be specified.");
352
- }
353
-
354
- template <class Master_matrix>
355
- inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
356
- std::function<bool(Pos_index,Pos_index)> deathComparator)
357
- : CP(), birthComp_(std::move(birthComparator)), deathComp_(std::move(deathComparator))
358
- {}
359
-
360
- template <class Master_matrix>
361
- inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(const Chain_vine_swap& matrixToCopy)
362
- : CP(static_cast<const CP&>(matrixToCopy)),
363
- birthComp_(matrixToCopy.birthComp_),
364
- deathComp_(matrixToCopy.deathComp_)
365
- {}
366
-
367
- template <class Master_matrix>
368
- inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(Chain_vine_swap<Master_matrix>&& other) noexcept
369
- : CP(std::move(static_cast<CP&>(other))),
370
- birthComp_(std::move(other.birthComp_)),
371
- deathComp_(std::move(other.deathComp_))
372
- {}
373
-
374
- template <class Master_matrix>
375
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::vine_swap_with_z_eq_1_case(
376
- Index columnIndex1, Index columnIndex2)
377
- {
378
- const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
379
- const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
380
-
381
- if (col1IsNeg && col2IsNeg) return _negative_vine_swap(columnIndex1, columnIndex2);
382
-
383
- if (col1IsNeg) return _negative_positive_vine_swap(columnIndex1, columnIndex2);
384
-
385
- if (col2IsNeg) return _positive_negative_vine_swap(columnIndex1, columnIndex2);
386
-
387
- return _positive_vine_swap(columnIndex1, columnIndex2);
388
- }
389
-
390
- template <class Master_matrix>
391
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::vine_swap(Index columnIndex1,
392
- Index columnIndex2)
393
- {
394
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
395
- GUDHI_CHECK(CP::are_adjacent(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2)),
396
- std::invalid_argument(
397
- "Chain_vine_swap::vine_swap - Columns to be swapped need to be adjacent in the 'real' matrix."));
398
- }
399
-
400
- const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
401
- const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
402
-
403
- if (col1IsNeg && col2IsNeg) {
404
- if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
405
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
406
- ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
407
- ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
408
-
409
- CP::negative_transpose(pivot1, pivot2);
410
- CP::swap_positions(pivot1, pivot2);
411
- }
412
- return columnIndex1;
413
- }
414
- return _negative_vine_swap(columnIndex1, columnIndex2);
415
- }
416
-
417
- if (col1IsNeg) {
418
- if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
419
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
420
- ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
421
- ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
422
-
423
- CP::negative_positive_transpose(pivot1, pivot2);
424
- CP::swap_positions(pivot1, pivot2);
425
- }
426
- return columnIndex1;
427
- }
428
- return _negative_positive_vine_swap(columnIndex1, columnIndex2);
429
- }
430
-
431
- if (col2IsNeg) {
432
- if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
433
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
434
- ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
435
- ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
436
-
437
- CP::positive_negative_transpose(pivot1, pivot2);
438
- CP::swap_positions(pivot1, pivot2);
439
- }
440
- return columnIndex1;
441
- }
442
- return _positive_negative_vine_swap(columnIndex1, columnIndex2);
443
- }
444
-
445
- if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
446
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
447
- ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
448
- ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
449
-
450
- CP::positive_transpose(pivot1, pivot2);
451
- CP::swap_positions(pivot1, pivot2);
452
- }
453
- return columnIndex1;
454
- }
455
- return _positive_vine_swap(columnIndex1, columnIndex2);
456
- }
457
-
458
- template <class Master_matrix>
459
- inline Chain_vine_swap<Master_matrix>& Chain_vine_swap<Master_matrix>::operator=(Chain_vine_swap<Master_matrix> other)
460
- {
461
- CP::operator=(other);
462
- std::swap(birthComp_, other.birthComp_);
463
- std::swap(deathComp_, other.deathComp_);
464
- return *this;
465
- }
466
-
467
- template <class Master_matrix>
468
- inline bool Chain_vine_swap<Master_matrix>::_is_negative_in_pair(Index columnIndex)
469
- {
470
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
471
- return CP::is_negative_in_pair(_matrix()->get_pivot(columnIndex));
472
- } else {
473
- auto& col = _matrix()->get_column(columnIndex);
474
- if (!col.is_paired()) return false;
475
- return col.get_pivot() > _matrix()->get_pivot(col.get_paired_chain_index());
476
- }
477
- }
478
-
479
- template <class Master_matrix>
480
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_vine_swap(
481
- Index columnIndex1, Index columnIndex2)
482
- {
483
- auto& col1 = _matrix()->get_column(columnIndex1);
484
- auto& col2 = _matrix()->get_column(columnIndex2);
485
-
486
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
487
- CP::swap_positions(col1.get_pivot(), col2.get_pivot());
488
- }
489
- // TODO: factorize the cases. But for debug it is much more easier to understand what is happening splitted like this
490
- if (!col1.is_paired()) { // F x *
491
- bool hasSmallerBirth;
492
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
493
- // this order because position were swapped with CP::swap_positions
494
- hasSmallerBirth = (CP::birth(col2.get_pivot()) < CP::birth(col1.get_pivot()));
495
- } else {
496
- hasSmallerBirth = birthComp_(columnIndex1, columnIndex2);
497
- }
498
-
499
- if (!col2.is_paired() && hasSmallerBirth) {
500
- _matrix()->add_to(columnIndex1, columnIndex2);
501
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
502
- CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
503
- }
504
- return columnIndex1;
505
- }
506
- _matrix()->add_to(columnIndex2, columnIndex1);
507
-
508
- return columnIndex2;
509
- }
510
-
511
- if (!col2.is_paired()) { // G x F
512
- static_cast<Master_chain_matrix*>(this)->add_to(columnIndex1, columnIndex2);
513
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
514
- CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
515
- }
516
- return columnIndex1;
517
- }
518
-
519
- bool hasSmallerDeath;
520
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
521
- // this order because position were swapped with CP::swap_positions
522
- hasSmallerDeath = (CP::death(col2.get_pivot()) < CP::death(col1.get_pivot()));
523
- } else {
524
- hasSmallerDeath = deathComp_(columnIndex1, columnIndex2);
525
- }
526
-
527
- // G x G
528
- if (hasSmallerDeath)
529
- {
530
- _matrix()->add_to(col1.get_paired_chain_index(), col2.get_paired_chain_index());
531
- _matrix()->add_to(columnIndex1, columnIndex2);
532
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
533
- CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
534
- }
535
- return columnIndex1;
536
- }
537
-
538
- _matrix()->add_to(col2.get_paired_chain_index(), col1.get_paired_chain_index());
539
- _matrix()->add_to(columnIndex2, columnIndex1);
540
-
541
- return columnIndex2;
542
- }
543
-
544
- template <class Master_matrix>
545
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_negative_vine_swap(
546
- Index columnIndex1, Index columnIndex2)
547
- {
548
- _matrix()->add_to(columnIndex1, columnIndex2);
549
-
550
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
551
- ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
552
- ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
553
-
554
- CP::positive_negative_transpose(pivot1, pivot2);
555
- CP::swap_positions(pivot1, pivot2);
556
- }
557
-
558
- return columnIndex1;
559
- }
560
-
561
- template <class Master_matrix>
562
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_positive_vine_swap(
563
- Index columnIndex1, Index columnIndex2)
564
- {
565
- _matrix()->add_to(columnIndex2, columnIndex1);
566
-
567
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
568
- CP::swap_positions(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2));
569
- }
570
-
571
- return columnIndex2;
572
- }
573
-
574
- template <class Master_matrix>
575
- inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_vine_swap(
576
- Index columnIndex1, Index columnIndex2)
577
- {
578
- auto& col1 = _matrix()->get_column(columnIndex1);
579
- auto& col2 = _matrix()->get_column(columnIndex2);
580
-
581
- Index pairedIndex1 = col1.get_paired_chain_index();
582
- Index pairedIndex2 = col2.get_paired_chain_index();
583
-
584
- bool hasSmallerBirth;
585
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
586
- hasSmallerBirth = (CP::birth(col1.get_pivot()) < CP::birth(col2.get_pivot()));
587
- } else {
588
- hasSmallerBirth = birthComp_(pairedIndex1, pairedIndex2);
589
- }
590
-
591
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
592
- CP::swap_positions(col1.get_pivot(), col2.get_pivot());
593
- }
594
-
595
- if (hasSmallerBirth)
596
- {
597
- _matrix()->add_to(pairedIndex1, pairedIndex2);
598
- _matrix()->add_to(columnIndex1, columnIndex2);
599
-
600
- if constexpr (Master_matrix::Option_list::has_column_pairings) {
601
- CP::negative_transpose(col1.get_pivot(), col2.get_pivot());
602
- }
603
-
604
- return columnIndex1;
605
- }
606
-
607
- _matrix()->add_to(pairedIndex2, pairedIndex1);
608
- _matrix()->add_to(columnIndex2, columnIndex1);
609
-
610
- return columnIndex2;
611
- }
612
-
613
- } // namespace persistence_matrix
614
- } // namespace Gudhi
615
-
616
- #endif // PM_CHAIN_VINE_SWAP_H
1
+ /* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2
+ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3
+ * Author(s): Hannah Schreiber
4
+ *
5
+ * Copyright (C) 2022-24 Inria
6
+ *
7
+ * Modification(s):
8
+ * - YYYY/MM Author: Description of the modification
9
+ */
10
+
11
+ /**
12
+ * @file chain_vine_swap.h
13
+ * @author Hannah Schreiber
14
+ * @brief Contains the @ref Gudhi::persistence_matrix::Chain_barcode_swap and
15
+ * @ref Gudhi::persistence_matrix::Chain_vine_swap classes, as well as the
16
+ * @ref Gudhi::persistence_matrix::Dummy_chain_vine_swap and
17
+ * @ref Gudhi::persistence_matrix::Dummy_chain_vine_pairing structures.
18
+ */
19
+
20
+ #ifndef PM_CHAIN_VINE_SWAP_H
21
+ #define PM_CHAIN_VINE_SWAP_H
22
+
23
+ #include <utility> //std::swap & std::move
24
+ #include <cassert>
25
+ #include <functional> //std::function
26
+ #include <stdexcept> //std::invalid_argument
27
+
28
+ #include "chain_pairing.h"
29
+
30
+ namespace Gudhi {
31
+ namespace persistence_matrix {
32
+
33
+ /**
34
+ * @ingroup persistence_matrix
35
+ *
36
+ * @brief Default death comparator. Simply assumes that two positive paired columns are never swapped. Which is true
37
+ * for the use case in zigzag persistence for example.
38
+ *
39
+ * @param columnIndex1 First column index.
40
+ * @param columnIndex2 Second column index.
41
+ * @return false
42
+ */
43
+ constexpr bool _no_G_death_comparator([[maybe_unused]] unsigned int columnIndex1,
44
+ [[maybe_unused]] unsigned int columnIndex2)
45
+ {
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * @ingroup persistence_matrix
51
+ *
52
+ * @brief Empty structure.
53
+ * Inherited instead of @ref Chain_vine_swap, when vine swaps are not enabled.
54
+ */
55
+ struct Dummy_chain_vine_swap
56
+ {
57
+ friend void swap([[maybe_unused]] Dummy_chain_vine_swap& d1, [[maybe_unused]] Dummy_chain_vine_swap& d2) {}
58
+
59
+ Dummy_chain_vine_swap() {}
60
+ template <typename BirthComparatorFunction, typename DeathComparatorFunction>
61
+ Dummy_chain_vine_swap([[maybe_unused]] const BirthComparatorFunction& birthComparator,
62
+ [[maybe_unused]] const DeathComparatorFunction& deathComparator) {}
63
+ };
64
+
65
+ /**
66
+ * @ingroup persistence_matrix
67
+ *
68
+ * @brief Empty structure.
69
+ * Inherited instead of @ref Chain_barcode_swap, when the barcode is not stored.
70
+ */
71
+ struct Dummy_chain_vine_pairing
72
+ {
73
+ friend void swap([[maybe_unused]] Dummy_chain_vine_pairing& d1, [[maybe_unused]] Dummy_chain_vine_pairing& d2) {}
74
+ };
75
+
76
+ /**
77
+ * @ingroup persistence_matrix
78
+ *
79
+ * @brief Class managing the barcode for @ref Chain_vine_swap.
80
+ *
81
+ * @tparam Master_matrix An instantiation of @ref Matrix from which all types and options are deduced.
82
+ */
83
+ template <typename Master_matrix>
84
+ class Chain_barcode_swap : public Chain_pairing<Master_matrix>
85
+ {
86
+ public:
87
+ using ID_index = typename Master_matrix::ID_index; /**< @ref IDIdx index type. */
88
+ using Pos_index = typename Master_matrix::Pos_index; /**< @ref PosIdx index type. */
89
+ //CP = Chain Pairing
90
+ using CP = Chain_pairing<Master_matrix>;
91
+
92
+ /**
93
+ * @brief Default constructor.
94
+ */
95
+ Chain_barcode_swap(){};
96
+ /**
97
+ * @brief Copy constructor.
98
+ *
99
+ * @param toCopy Matrix to copy.
100
+ */
101
+ Chain_barcode_swap(const Chain_barcode_swap& toCopy)
102
+ : CP(static_cast<const CP&>(toCopy)), pivotToPosition_(toCopy.pivotToPosition_){};
103
+ /**
104
+ * @brief Move constructor.
105
+ *
106
+ * @param other Matrix to move.
107
+ */
108
+ Chain_barcode_swap(Chain_barcode_swap&& other)
109
+ : CP(std::move(static_cast<CP&>(other))), pivotToPosition_(std::move(other.pivotToPosition_)){};
110
+
111
+ protected:
112
+ using Dictionary = typename Master_matrix::template Dictionary<Pos_index>;
113
+
114
+ Dictionary pivotToPosition_; // necessary to keep track of the barcode changes
115
+
116
+ void swap_positions(ID_index pivot1, ID_index pivot2) {
117
+ if constexpr (Master_matrix::Option_list::has_map_column_container) {
118
+ std::swap(pivotToPosition_.at(pivot1), pivotToPosition_.at(pivot2));
119
+ } else {
120
+ std::swap(pivotToPosition_[pivot1], pivotToPosition_[pivot2]);
121
+ }
122
+ }
123
+
124
+ bool is_negative_in_pair(ID_index pivot) const {
125
+ Pos_index pos = _get_pivot_position(pivot);
126
+ return death(pivot) == pos;
127
+ }
128
+
129
+ void positive_transpose(ID_index pivot1, ID_index pivot2) {
130
+ Pos_index pos1 = _get_pivot_position(pivot1);
131
+ Pos_index pos2 = _get_pivot_position(pivot2);
132
+
133
+ _birth(pos1) = pos2;
134
+ _birth(pos2) = pos1;
135
+ std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
136
+ }
137
+
138
+ void negative_transpose(ID_index pivot1, ID_index pivot2) {
139
+ Pos_index pos1 = _get_pivot_position(pivot1);
140
+ Pos_index pos2 = _get_pivot_position(pivot2);
141
+
142
+ _death(pos1) = pos2;
143
+ _death(pos2) = pos1;
144
+ std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
145
+ }
146
+
147
+ void positive_negative_transpose(ID_index pivot1, ID_index pivot2) {
148
+ Pos_index pos1 = _get_pivot_position(pivot1);
149
+ Pos_index pos2 = _get_pivot_position(pivot2);
150
+
151
+ _birth(pos1) = pos2;
152
+ _death(pos2) = pos1;
153
+ std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
154
+ }
155
+
156
+ void negative_positive_transpose(ID_index pivot1, ID_index pivot2) {
157
+ Pos_index pos1 = _get_pivot_position(pivot1);
158
+ Pos_index pos2 = _get_pivot_position(pivot2);
159
+
160
+ _death(pos1) = pos2;
161
+ _birth(pos2) = pos1;
162
+ std::swap(CP::indexToBar_.at(pos1), CP::indexToBar_.at(pos2));
163
+ }
164
+
165
+ bool are_adjacent(ID_index pivot1, ID_index pivot2) const {
166
+ Pos_index pos1 = _get_pivot_position(pivot1);
167
+ Pos_index pos2 = _get_pivot_position(pivot2);
168
+ return pos1 < pos2 ? (pos2 - pos1) == 1 : (pos1 - pos2) == 1;
169
+ }
170
+
171
+ Chain_barcode_swap& operator=(Chain_barcode_swap other) {
172
+ Chain_pairing<Master_matrix>::operator=(other);
173
+ pivotToPosition_.swap(other.pivotToPosition_);
174
+ }
175
+ friend void swap(Chain_barcode_swap& swap1, Chain_barcode_swap& swap2) {
176
+ swap(static_cast<Chain_pairing<Master_matrix>&>(swap1), static_cast<Chain_pairing<Master_matrix>&>(swap2));
177
+ swap1.pivotToPosition_.swap(swap2.pivotToPosition_);
178
+ }
179
+
180
+ Pos_index death(ID_index pivot) const {
181
+ Pos_index simplexIndex = _get_pivot_position(pivot);
182
+
183
+ if constexpr (Master_matrix::Option_list::has_removable_columns) {
184
+ return CP::indexToBar_.at(simplexIndex)->death;
185
+ } else {
186
+ return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
187
+ }
188
+ }
189
+
190
+ Pos_index birth(ID_index pivot) const {
191
+ Pos_index simplexIndex = _get_pivot_position(pivot);
192
+
193
+ if constexpr (Master_matrix::Option_list::has_removable_columns) {
194
+ return CP::indexToBar_.at(simplexIndex)->birth;
195
+ } else {
196
+ return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
197
+ }
198
+ }
199
+
200
+ private:
201
+ Pos_index _get_pivot_position(ID_index pivot) const {
202
+ if constexpr (Master_matrix::Option_list::has_map_column_container) {
203
+ return pivotToPosition_.at(
204
+ pivot); // quite often called, make public and pass position instead of pivot to avoid find() every time?
205
+ } else {
206
+ return pivotToPosition_[pivot];
207
+ }
208
+ }
209
+
210
+ Pos_index& _death(Pos_index simplexIndex) {
211
+ if constexpr (Master_matrix::Option_list::has_removable_columns) {
212
+ return CP::indexToBar_.at(simplexIndex)->death;
213
+ } else {
214
+ return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).death;
215
+ }
216
+ }
217
+
218
+ Pos_index& _birth(Pos_index simplexIndex) {
219
+ if constexpr (Master_matrix::Option_list::has_removable_columns) {
220
+ return CP::indexToBar_.at(simplexIndex)->birth;
221
+ } else {
222
+ return CP::barcode_.at(CP::indexToBar_.at(simplexIndex)).birth;
223
+ }
224
+ }
225
+ };
226
+
227
+ /**
228
+ * @class Chain_vine_swap chain_vine_swap.h gudhi/Persistence_matrix/chain_vine_swap.h
229
+ * @ingroup persistence_matrix
230
+ *
231
+ * @brief Class managing the vine swaps for @ref Chain_matrix.
232
+ *
233
+ * @tparam Master_matrix An instantiation of @ref Matrix from which all types and options are deduced.
234
+ */
235
+ template <class Master_matrix>
236
+ class Chain_vine_swap : public std::conditional<Master_matrix::Option_list::has_column_pairings,
237
+ Chain_barcode_swap<Master_matrix>,
238
+ Dummy_chain_vine_pairing
239
+ >::type
240
+ {
241
+ public:
242
+ using Index = typename Master_matrix::Index; /**< @ref MatIdx index type. */
243
+ using ID_index = typename Master_matrix::ID_index; /**< @ref IDIdx index type. */
244
+ using Pos_index = typename Master_matrix::Pos_index; /**< @ref PosIdx index type. */
245
+ using Column_container = typename Master_matrix::Column_container; /**< Column container type. */
246
+ using Column = typename Master_matrix::Column; /**< Column type. */
247
+ typedef bool (*EventCompFuncPointer)(Pos_index, Pos_index); /**< Pointer type for birth/death comparators. */
248
+
249
+ /**
250
+ * @brief Default constructor. Only available if @ref PersistenceMatrixOptions::has_column_pairings is true.
251
+ */
252
+ Chain_vine_swap();
253
+ /**
254
+ * @brief Constructor storing the given comparators.
255
+ *
256
+ * @param birthComparator Method taking two @ref PosIdx indices as input and returning true if and only if
257
+ * the birth associated to the first position is strictly less than birth associated to
258
+ * the second one with respect to some self defined order. It is used while swapping two unpaired or
259
+ * two negative columns.
260
+ * @param deathComparator Method taking two @ref PosIdx indices as input and returning true if and only if
261
+ * the death associated to the first position is strictly less than death associated to
262
+ * the second one with respect to some self defined order. It is used while swapping two positive but paired
263
+ * columns. Default value: @ref _no_G_death_comparator.
264
+ */
265
+ Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
266
+ std::function<bool(Pos_index,Pos_index)> deathComparator = _no_G_death_comparator);
267
+ /**
268
+ * @brief Copy constructor.
269
+ *
270
+ * @param matrixToCopy Matrix to copy.
271
+ */
272
+ Chain_vine_swap(const Chain_vine_swap& matrixToCopy);
273
+ /**
274
+ * @brief Move constructor.
275
+ *
276
+ * @param other Matrix to move.
277
+ */
278
+ Chain_vine_swap(Chain_vine_swap&& other) noexcept;
279
+
280
+ /**
281
+ * @brief Does the same than @ref vine_swap, but assumes that the swap is non trivial and
282
+ * therefore skips a part of the case study.
283
+ *
284
+ * @param columnIndex1 @ref MatIdx index of the first cell.
285
+ * @param columnIndex2 @ref MatIdx index of the second cell.
286
+ * @return Let \f$ pos1 \f$ be the @ref PosIdx index of @p columnIndex1 and \f$ pos2 \f$ be the @ref PosIdx index of
287
+ * @p columnIndex2. The method returns the @ref MatIdx of the column which has now, after the swap, the @ref PosIdx
288
+ * \f$ max(pos1, pos2) \f$.
289
+ */
290
+ Index vine_swap_with_z_eq_1_case(Index columnIndex1, Index columnIndex2);
291
+ /**
292
+ * @brief Does a vine swap between two cells which are consecutive in the filtration. Roughly, if \f$ F \f$ is
293
+ * the current filtration represented by the matrix, the method modifies the matrix such that the new state
294
+ * corresponds to a valid state for the filtration \f$ F' \f$ equal to \f$ F \f$ but with the two given cells
295
+ * at swapped positions. Of course, the two cells should not have a face/coface relation which each other ;
296
+ * \f$ F' \f$ has to be a valid filtration.
297
+ * See @cite vineyards for more information about vine and vineyards.
298
+ *
299
+ * @param columnIndex1 @ref MatIdx index of the first cell.
300
+ * @param columnIndex2 @ref MatIdx index of the second cell. It is assumed that the @ref PosIdx of both only differs
301
+ * by one if the barcode is maintained.
302
+ * @return Let \f$ pos1 \f$ be the @ref PosIdx index of @p columnIndex1 and \f$ pos2 \f$ be the @ref PosIdx index of
303
+ * @p columnIndex2. The method returns the @ref MatIdx of the column which has now, after the swap, the @ref PosIdx
304
+ * \f$ max(pos1, pos2) \f$.
305
+ */
306
+ Index vine_swap(Index columnIndex1, Index columnIndex2);
307
+
308
+ /**
309
+ * @brief Assign operator.
310
+ */
311
+ Chain_vine_swap& operator=(Chain_vine_swap other);
312
+ /**
313
+ * @brief Swap operator.
314
+ */
315
+ friend void swap(Chain_vine_swap& swap1, Chain_vine_swap& swap2) {
316
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
317
+ swap(static_cast<Chain_barcode_swap<Master_matrix>&>(swap1),
318
+ static_cast<Chain_barcode_swap<Master_matrix>&>(swap2));
319
+ }
320
+ std::swap(swap1.birthComp_, swap2.birthComp_);
321
+ std::swap(swap1.deathComp_, swap2.deathComp_);
322
+ }
323
+
324
+ protected:
325
+ using CP = typename std::conditional<Master_matrix::Option_list::has_column_pairings,
326
+ Chain_barcode_swap<Master_matrix>,
327
+ Dummy_chain_vine_pairing
328
+ >::type;
329
+
330
+ private:
331
+ using Master_chain_matrix = typename Master_matrix::Master_chain_matrix;
332
+
333
+ std::function<bool(Pos_index,Pos_index)> birthComp_; /**< for F x F & H x H. */
334
+ std::function<bool(Pos_index,Pos_index)> deathComp_; /**< for G x G. */
335
+
336
+ bool _is_negative_in_pair(Index columnIndex);
337
+
338
+ Index _positive_vine_swap(Index columnIndex1, Index columnIndex2);
339
+ Index _positive_negative_vine_swap(Index columnIndex1, Index columnIndex2);
340
+ Index _negative_positive_vine_swap(Index columnIndex1, Index columnIndex2);
341
+ Index _negative_vine_swap(Index columnIndex1, Index columnIndex2);
342
+
343
+ constexpr Master_chain_matrix* _matrix() { return static_cast<Master_chain_matrix*>(this); }
344
+ constexpr const Master_chain_matrix* _matrix() const { return static_cast<const Master_chain_matrix*>(this); }
345
+ };
346
+
347
+ template <class Master_matrix>
348
+ inline Chain_vine_swap<Master_matrix>::Chain_vine_swap() : CP(), birthComp_(), deathComp_()
349
+ {
350
+ static_assert(Master_matrix::Option_list::has_column_pairings,
351
+ "If barcode is not stored, at least a birth comparator has to be specified.");
352
+ }
353
+
354
+ template <class Master_matrix>
355
+ inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(std::function<bool(Pos_index,Pos_index)> birthComparator,
356
+ std::function<bool(Pos_index,Pos_index)> deathComparator)
357
+ : CP(), birthComp_(std::move(birthComparator)), deathComp_(std::move(deathComparator))
358
+ {}
359
+
360
+ template <class Master_matrix>
361
+ inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(const Chain_vine_swap& matrixToCopy)
362
+ : CP(static_cast<const CP&>(matrixToCopy)),
363
+ birthComp_(matrixToCopy.birthComp_),
364
+ deathComp_(matrixToCopy.deathComp_)
365
+ {}
366
+
367
+ template <class Master_matrix>
368
+ inline Chain_vine_swap<Master_matrix>::Chain_vine_swap(Chain_vine_swap<Master_matrix>&& other) noexcept
369
+ : CP(std::move(static_cast<CP&>(other))),
370
+ birthComp_(std::move(other.birthComp_)),
371
+ deathComp_(std::move(other.deathComp_))
372
+ {}
373
+
374
+ template <class Master_matrix>
375
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::vine_swap_with_z_eq_1_case(
376
+ Index columnIndex1, Index columnIndex2)
377
+ {
378
+ const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
379
+ const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
380
+
381
+ if (col1IsNeg && col2IsNeg) return _negative_vine_swap(columnIndex1, columnIndex2);
382
+
383
+ if (col1IsNeg) return _negative_positive_vine_swap(columnIndex1, columnIndex2);
384
+
385
+ if (col2IsNeg) return _positive_negative_vine_swap(columnIndex1, columnIndex2);
386
+
387
+ return _positive_vine_swap(columnIndex1, columnIndex2);
388
+ }
389
+
390
+ template <class Master_matrix>
391
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::vine_swap(Index columnIndex1,
392
+ Index columnIndex2)
393
+ {
394
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
395
+ GUDHI_CHECK(CP::are_adjacent(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2)),
396
+ std::invalid_argument(
397
+ "Chain_vine_swap::vine_swap - Columns to be swapped need to be adjacent in the 'real' matrix."));
398
+ }
399
+
400
+ const bool col1IsNeg = _is_negative_in_pair(columnIndex1);
401
+ const bool col2IsNeg = _is_negative_in_pair(columnIndex2);
402
+
403
+ if (col1IsNeg && col2IsNeg) {
404
+ if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
405
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
406
+ ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
407
+ ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
408
+
409
+ CP::negative_transpose(pivot1, pivot2);
410
+ CP::swap_positions(pivot1, pivot2);
411
+ }
412
+ return columnIndex1;
413
+ }
414
+ return _negative_vine_swap(columnIndex1, columnIndex2);
415
+ }
416
+
417
+ if (col1IsNeg) {
418
+ if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
419
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
420
+ ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
421
+ ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
422
+
423
+ CP::negative_positive_transpose(pivot1, pivot2);
424
+ CP::swap_positions(pivot1, pivot2);
425
+ }
426
+ return columnIndex1;
427
+ }
428
+ return _negative_positive_vine_swap(columnIndex1, columnIndex2);
429
+ }
430
+
431
+ if (col2IsNeg) {
432
+ if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
433
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
434
+ ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
435
+ ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
436
+
437
+ CP::positive_negative_transpose(pivot1, pivot2);
438
+ CP::swap_positions(pivot1, pivot2);
439
+ }
440
+ return columnIndex1;
441
+ }
442
+ return _positive_negative_vine_swap(columnIndex1, columnIndex2);
443
+ }
444
+
445
+ if (_matrix()->is_zero_entry(columnIndex2, _matrix()->get_pivot(columnIndex1))) {
446
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
447
+ ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
448
+ ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
449
+
450
+ CP::positive_transpose(pivot1, pivot2);
451
+ CP::swap_positions(pivot1, pivot2);
452
+ }
453
+ return columnIndex1;
454
+ }
455
+ return _positive_vine_swap(columnIndex1, columnIndex2);
456
+ }
457
+
458
+ template <class Master_matrix>
459
+ inline Chain_vine_swap<Master_matrix>& Chain_vine_swap<Master_matrix>::operator=(Chain_vine_swap<Master_matrix> other)
460
+ {
461
+ CP::operator=(other);
462
+ std::swap(birthComp_, other.birthComp_);
463
+ std::swap(deathComp_, other.deathComp_);
464
+ return *this;
465
+ }
466
+
467
+ template <class Master_matrix>
468
+ inline bool Chain_vine_swap<Master_matrix>::_is_negative_in_pair(Index columnIndex)
469
+ {
470
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
471
+ return CP::is_negative_in_pair(_matrix()->get_pivot(columnIndex));
472
+ } else {
473
+ auto& col = _matrix()->get_column(columnIndex);
474
+ if (!col.is_paired()) return false;
475
+ return col.get_pivot() > _matrix()->get_pivot(col.get_paired_chain_index());
476
+ }
477
+ }
478
+
479
+ template <class Master_matrix>
480
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_vine_swap(
481
+ Index columnIndex1, Index columnIndex2)
482
+ {
483
+ auto& col1 = _matrix()->get_column(columnIndex1);
484
+ auto& col2 = _matrix()->get_column(columnIndex2);
485
+
486
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
487
+ CP::swap_positions(col1.get_pivot(), col2.get_pivot());
488
+ }
489
+ // TODO: factorize the cases. But for debug it is much more easier to understand what is happening splitted like this
490
+ if (!col1.is_paired()) { // F x *
491
+ bool hasSmallerBirth;
492
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
493
+ // this order because position were swapped with CP::swap_positions
494
+ hasSmallerBirth = (CP::birth(col2.get_pivot()) < CP::birth(col1.get_pivot()));
495
+ } else {
496
+ hasSmallerBirth = birthComp_(columnIndex1, columnIndex2);
497
+ }
498
+
499
+ if (!col2.is_paired() && hasSmallerBirth) {
500
+ _matrix()->add_to(columnIndex1, columnIndex2);
501
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
502
+ CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
503
+ }
504
+ return columnIndex1;
505
+ }
506
+ _matrix()->add_to(columnIndex2, columnIndex1);
507
+
508
+ return columnIndex2;
509
+ }
510
+
511
+ if (!col2.is_paired()) { // G x F
512
+ static_cast<Master_chain_matrix*>(this)->add_to(columnIndex1, columnIndex2);
513
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
514
+ CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
515
+ }
516
+ return columnIndex1;
517
+ }
518
+
519
+ bool hasSmallerDeath;
520
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
521
+ // this order because position were swapped with CP::swap_positions
522
+ hasSmallerDeath = (CP::death(col2.get_pivot()) < CP::death(col1.get_pivot()));
523
+ } else {
524
+ hasSmallerDeath = deathComp_(columnIndex1, columnIndex2);
525
+ }
526
+
527
+ // G x G
528
+ if (hasSmallerDeath)
529
+ {
530
+ _matrix()->add_to(col1.get_paired_chain_index(), col2.get_paired_chain_index());
531
+ _matrix()->add_to(columnIndex1, columnIndex2);
532
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
533
+ CP::positive_transpose(col1.get_pivot(), col2.get_pivot());
534
+ }
535
+ return columnIndex1;
536
+ }
537
+
538
+ _matrix()->add_to(col2.get_paired_chain_index(), col1.get_paired_chain_index());
539
+ _matrix()->add_to(columnIndex2, columnIndex1);
540
+
541
+ return columnIndex2;
542
+ }
543
+
544
+ template <class Master_matrix>
545
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_positive_negative_vine_swap(
546
+ Index columnIndex1, Index columnIndex2)
547
+ {
548
+ _matrix()->add_to(columnIndex1, columnIndex2);
549
+
550
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
551
+ ID_index pivot1 = _matrix()->get_pivot(columnIndex1);
552
+ ID_index pivot2 = _matrix()->get_pivot(columnIndex2);
553
+
554
+ CP::positive_negative_transpose(pivot1, pivot2);
555
+ CP::swap_positions(pivot1, pivot2);
556
+ }
557
+
558
+ return columnIndex1;
559
+ }
560
+
561
+ template <class Master_matrix>
562
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_positive_vine_swap(
563
+ Index columnIndex1, Index columnIndex2)
564
+ {
565
+ _matrix()->add_to(columnIndex2, columnIndex1);
566
+
567
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
568
+ CP::swap_positions(_matrix()->get_pivot(columnIndex1), _matrix()->get_pivot(columnIndex2));
569
+ }
570
+
571
+ return columnIndex2;
572
+ }
573
+
574
+ template <class Master_matrix>
575
+ inline typename Chain_vine_swap<Master_matrix>::Index Chain_vine_swap<Master_matrix>::_negative_vine_swap(
576
+ Index columnIndex1, Index columnIndex2)
577
+ {
578
+ auto& col1 = _matrix()->get_column(columnIndex1);
579
+ auto& col2 = _matrix()->get_column(columnIndex2);
580
+
581
+ Index pairedIndex1 = col1.get_paired_chain_index();
582
+ Index pairedIndex2 = col2.get_paired_chain_index();
583
+
584
+ bool hasSmallerBirth;
585
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
586
+ hasSmallerBirth = (CP::birth(col1.get_pivot()) < CP::birth(col2.get_pivot()));
587
+ } else {
588
+ hasSmallerBirth = birthComp_(pairedIndex1, pairedIndex2);
589
+ }
590
+
591
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
592
+ CP::swap_positions(col1.get_pivot(), col2.get_pivot());
593
+ }
594
+
595
+ if (hasSmallerBirth)
596
+ {
597
+ _matrix()->add_to(pairedIndex1, pairedIndex2);
598
+ _matrix()->add_to(columnIndex1, columnIndex2);
599
+
600
+ if constexpr (Master_matrix::Option_list::has_column_pairings) {
601
+ CP::negative_transpose(col1.get_pivot(), col2.get_pivot());
602
+ }
603
+
604
+ return columnIndex1;
605
+ }
606
+
607
+ _matrix()->add_to(pairedIndex2, pairedIndex1);
608
+ _matrix()->add_to(columnIndex2, columnIndex1);
609
+
610
+ return columnIndex2;
611
+ }
612
+
613
+ } // namespace persistence_matrix
614
+ } // namespace Gudhi
615
+
616
+ #endif // PM_CHAIN_VINE_SWAP_H