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,2307 @@
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, David Loiseaux
4
+ *
5
+ * Copyright (C) 2024-25 Inria
6
+ *
7
+ * Modification(s):
8
+ * - YYYY/MM Author: Description of the modification
9
+ */
10
+
11
+ /**
12
+ * @file Degree_rips_bifiltration.h
13
+ * @author Hannah Schreiber, David Loiseaux
14
+ * @brief Contains the @ref Gudhi::multi_filtration::Degree_rips_bifiltration class.
15
+ */
16
+
17
+ #ifndef MF_DEGREE_RIPS_BIFILTRATION_H_
18
+ #define MF_DEGREE_RIPS_BIFILTRATION_H_
19
+
20
+ #include <algorithm> //std::lower_bound
21
+ #include <cmath> //std::isnan, std::min
22
+ #include <cstddef> //std::size_t
23
+ #include <cstdint> //std::int32_t
24
+ #include <cstring> //memcpy
25
+ #include <iterator> //std::distance
26
+ #include <ostream> //std::ostream
27
+ #include <limits> //std::numerical_limits
28
+ #include <stdexcept> //std::logic_error
29
+ #include <type_traits> //std::is_arithmetic
30
+ #include <utility> //std::swap, std::move
31
+ #include <vector>
32
+ #include <initializer_list>
33
+
34
+ #include <gudhi/Debug_utils.h>
35
+ #include <gudhi/simple_mdspan.h>
36
+ #include <gudhi/Multi_filtration/multi_filtration_utils.h>
37
+ #include <gudhi/Simplex_tree/filtration_value_utils.h>
38
+ #include <gudhi/Multi_parameter_filtration.h>
39
+ #include <gudhi/Dynamic_multi_parameter_filtration.h>
40
+ #include <oneapi/tbb/concurrent_vector.h>
41
+
42
+ namespace Gudhi::multi_filtration {
43
+
44
+ /**
45
+ * @class Degree_rips_bifiltration Degree_rips_bifiltration.h gudhi/Degree_rips_bifiltration.h
46
+ * @ingroup multi_filtration
47
+ *
48
+ * @brief Class encoding the different generators, i.e., apparition times, of a \f$ k \f$-critical
49
+ * \f$\mathbb R^2\f$-filtration value in a degree-Rips filtration. That is, a \f$ k \f$-critical filtration value is
50
+ * always of the form: \f$ [(v_0,0), (v_1,1), (v_2,2), ..., (v_{k-1},k-1)] \f$, where all pairs \f$ (v_i,i) \f$
51
+ * represent a generator with two parameters: the radius and the degree. More precisely: let \f$ d \f$ be the max
52
+ * degree of a vertex in the complex. A vertex will be \f$ k \f$-critical if it has degree \f$ d - k + 1 \f$ and an
53
+ * edge is \f$ k \f$-critical if one of its end vertices is \f$ k \f$-critical and the other one \f$ j \f$-critical,
54
+ * \f$ j \geq k \f$. The first parameter is the more standard radius parameter of a Rips filtration.
55
+ * Note that the set of generators does not have to be minimal (contrary to @ref Multi_parameter_filtration e.g.),
56
+ * neither ordered lexicographically.
57
+ * Implements the concept @ref FiltrationValue of the @ref Gudhi::Simplex_tree and the concept
58
+ * @ref Gudhi::multi_persistence::MultiFiltrationValue.
59
+ *
60
+ * @details Overloads `std::numeric_limits` such that:
61
+ * - `std::numeric_limits<Degree_rips_bifiltration>::has_infinity` returns `true` if and only if `Co` is false,
62
+ * - `std::numeric_limits<Degree_rips_bifiltration>::has_quiet_NaN` returns `true`,
63
+ * - `std::numeric_limits<Degree_rips_bifiltration>::infinity()` returns
64
+ * @ref Degree_rips_bifiltration::inf() "",
65
+ * - `std::numeric_limits<Degree_rips_bifiltration>::minus_infinity()` returns
66
+ * @ref Degree_rips_bifiltration::minus_inf() "",
67
+ * - `std::numeric_limits<Degree_rips_bifiltration>::max(num_param)` throws if `Co` is true and otherwise returns a
68
+ * @ref Degree_rips_bifiltration with 1 generators with first parameter 0 and second parameter
69
+ *`std::numeric_limits<T>::max()`,
70
+ * - `std::numeric_limits<Degree_rips_bifiltration>::quiet_NaN()` returns @ref Degree_rips_bifiltration::nan().
71
+ *
72
+ * @tparam T Arithmetic type of an entry of the second parameter of a filtration value. Has to be **signed** and
73
+ * to implement `std::isnan(T)`, `std::numeric_limits<T>::has_quiet_NaN`, `std::numeric_limits<T>::quiet_NaN()`,
74
+ * `std::numeric_limits<T>::has_infinity`, `std::numeric_limits<T>::infinity()` and `std::numeric_limits<T>::max()`.
75
+ * If `std::numeric_limits<T>::has_infinity` returns `false`, a call to `std::numeric_limits<T>::infinity()`
76
+ * can simply throw. Examples are the native types `double`, `float` and `int`.
77
+ * @tparam Co If `true`, reverses the poset order, i.e., the order \f$ \le \f$ in \f$ \mathbb R^n \f$ becomes
78
+ * \f$ \ge \f$. That is, the positive cones representing a lifetime become all negative instead.
79
+ * @tparam Ensure1Criticality If `true`, the methods ensure that the filtration value is always 1-critical by throwing
80
+ * or refusing to compile if a modification increases the number of generators.
81
+ */
82
+ template <typename T, bool Co = false, bool Ensure1Criticality = false>
83
+ class Degree_rips_bifiltration
84
+ {
85
+ public:
86
+ using Underlying_container = std::vector<T>; /**< Underlying container for values. */
87
+
88
+ // CONSTRUCTORS
89
+
90
+ /**
91
+ * @brief Default constructor. Builds filtration value with one generator `(val, 0)`.
92
+ * If Co is false, `val` is -inf, if Co is true, `val` is at +inf.
93
+ *
94
+ * @param number_of_parameters Ignored, the number of parameters is always 2. For interface purposes only.
95
+ */
96
+ Degree_rips_bifiltration([[maybe_unused]] int number_of_parameters = 2) : generators_(1, _get_default_value()) {}
97
+
98
+ explicit Degree_rips_bifiltration(Gudhi::simplex_tree::empty_filtration_value_t /*e*/) : generators_(0) {}
99
+
100
+ /**
101
+ * @brief Builds a filtration value with one generator `(value, 0)`.
102
+ *
103
+ * @param number_of_parameters Ignored, the number of parameters is always 2. For interface purposes only.
104
+ * @param value Initialization value for the second parameter.
105
+ */
106
+ Degree_rips_bifiltration([[maybe_unused]] int number_of_parameters, T value) : generators_(1, value) {}
107
+
108
+ /**
109
+ * @brief Builds filtration value with one generator `(val, i)`, where `val` and `i` are the two first elements
110
+ * of the given range. Note that `i` has to be 0.
111
+ *
112
+ * @tparam ValueRange Range of types convertible to `T`. Should have a begin() method.
113
+ * @param range Values of the generator. The range has to have at least two elements.
114
+ */
115
+ template <class ValueRange = std::initializer_list<T>, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
116
+ Degree_rips_bifiltration(const ValueRange &range) : generators_(1, *(range.begin()))
117
+ {
118
+ GUDHI_CHECK(*(range.begin() + 1) == 0, std::invalid_argument("Second value of the range has to be 0"));
119
+ }
120
+
121
+ /**
122
+ * @brief Builds filtration value with one generator `(val, i)`, where `val` and `i` are the two first elements
123
+ * of the given range. Note that `i` has to be 0.
124
+ *
125
+ * @tparam Iterator Iterator type that has to satisfy the requirements of standard LegacyInputIterator and
126
+ * dereferenced elements have to be convertible to `T`.
127
+ * @param it_begin Iterator pointing to the start of the range.
128
+ * @param it_end Iterator pointing to the end of the range.
129
+ */
130
+ template <class Iterator, class = std::enable_if_t<!std::is_arithmetic_v<Iterator> > >
131
+ Degree_rips_bifiltration(Iterator it_begin, [[maybe_unused]] Iterator it_end) : generators_(1, *it_begin)
132
+ {
133
+ GUDHI_CHECK(*(it_begin + 1) == 0, std::invalid_argument("Second value of the range has to be 0"));
134
+ }
135
+
136
+ /**
137
+ * @brief Builds a filtration value with given values from the given range. The two first elements of the range have
138
+ * to correspond to the first generator, the two next elements to the second generator and so on... So the length of
139
+ * the range has to be a multiple of 2 and the number of generators will be \f$ k = length / 2 \f$. Note that starting
140
+ * from the second element, every second element has to represent the continuous sequence from 0 to \f$ k \f$.
141
+ * The range is represented by two iterators.
142
+ *
143
+ * @tparam Iterator Iterator type that has to satisfy the requirements of standard LegacyForwardIterator and
144
+ * dereferenced elements have to be convertible to `T`.
145
+ * @param it_begin Iterator pointing to the start of the range.
146
+ * @param it_end Iterator pointing to the end of the range.
147
+ * @param number_of_parameters Ignored, the number of parameters is always 2. For interface purposes only.
148
+ */
149
+ template <class Iterator, class = std::enable_if_t<!std::is_arithmetic_v<Iterator> > >
150
+ Degree_rips_bifiltration(Iterator it_begin, Iterator it_end, [[maybe_unused]] int number_of_parameters)
151
+ : generators_()
152
+ {
153
+ size_type num_gen = std::distance(it_begin, it_end) / 2;
154
+ if constexpr (Ensure1Criticality) {
155
+ if (num_gen > 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
156
+ }
157
+ generators_.resize(num_gen);
158
+ Iterator it = it_begin;
159
+ for (size_type i = 0; i < num_gen; ++i) {
160
+ generators_[i] = *it;
161
+ ++it;
162
+ GUDHI_CHECK(
163
+ static_cast<size_type>(*it) == i,
164
+ std::invalid_argument(
165
+ "Every second value of the range has to correspond to a contiguous sequence of integers starting at 0."));
166
+ ++it;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * @brief Builds filtration value with given values from the given range. The range only represent the values of the
172
+ * first parameter. So `(generators[0], 0)` is the first generator, `(generators[1], 1)` is the second generator and
173
+ * so on... The range is represented by @ref Degree_rips_bifiltration::Underlying_container "" and copied into the
174
+ * underlying container of the class.
175
+ *
176
+ * @param generators Values for the second parameter.
177
+ * @param number_of_parameters Ignored, the number of parameters is always 2. For interface purposes only.
178
+ */
179
+ Degree_rips_bifiltration(const Underlying_container &generators, [[maybe_unused]] int number_of_parameters)
180
+ : generators_(generators)
181
+ {
182
+ if constexpr (Ensure1Criticality) {
183
+ if (generators_.size() > 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
184
+ }
185
+ }
186
+
187
+ /**
188
+ * @brief Builds filtration value with given values from the given range. The range only represent the values of the
189
+ * first parameter. So `(generators[0], 0)` is the first generator, `(generators[1], 1)` is the second generator and
190
+ * so on... The range is represented by @ref Degree_rips_bifiltration::Underlying_container "" and **moved** into
191
+ * the underlying container of the class.
192
+ *
193
+ * @param generators Values to move.
194
+ * @param number_of_parameters Ignored, the number of parameters is always 2. For interface purposes only.
195
+ */
196
+ Degree_rips_bifiltration(Underlying_container &&generators, [[maybe_unused]] int number_of_parameters)
197
+ : generators_(std::move(generators))
198
+ {
199
+ if constexpr (Ensure1Criticality) {
200
+ if (generators_.size() > 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
201
+ }
202
+ }
203
+
204
+ // cannot use = default as it triggers "dummy_g_ may be used uninitialized" compiler warning for nothing
205
+ /**
206
+ * @brief Copy constructor.
207
+ */
208
+ Degree_rips_bifiltration(const Degree_rips_bifiltration &other) : generators_(other.generators_) {}
209
+
210
+ // cannot use = default as it triggers "dummy_g_ may be used uninitialized" compiler warning for nothing
211
+ /**
212
+ * @brief Move constructor.
213
+ */
214
+ Degree_rips_bifiltration(Degree_rips_bifiltration &&other) noexcept : generators_(std::move(other.generators_)) {}
215
+
216
+ /**
217
+ * @brief Copy constructor.
218
+ *
219
+ * @tparam U Type convertible into `T`.
220
+ */
221
+ template <typename U, bool OtherCo, bool OtherEnsure1Criticality>
222
+ Degree_rips_bifiltration(const Degree_rips_bifiltration<U, OtherCo, OtherEnsure1Criticality> &other)
223
+ : generators_(other.begin(), other.end())
224
+ {
225
+ if constexpr (Ensure1Criticality && !OtherEnsure1Criticality) {
226
+ if (generators_.size() > 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
227
+ }
228
+ }
229
+
230
+ ~Degree_rips_bifiltration() = default;
231
+
232
+ // cannot use = default as it triggers "dummy_g_ may be used uninitialized" compiler warning for nothing
233
+ /**
234
+ * @brief Assign operator.
235
+ */
236
+ Degree_rips_bifiltration &operator=(const Degree_rips_bifiltration &other) {
237
+ generators_ = other.generators_;
238
+ return *this;
239
+ }
240
+
241
+ // cannot use = default as it triggers "dummy_g_ may be used uninitialized" compiler warning for nothing
242
+ /**
243
+ * @brief Move assign operator.
244
+ */
245
+ Degree_rips_bifiltration &operator=(Degree_rips_bifiltration &&other) noexcept {
246
+ generators_ = std::move(other.generators_);
247
+ return *this;
248
+ }
249
+
250
+ /**
251
+ * @brief Assign operator.
252
+ *
253
+ * @tparam U Type convertible into `T`.
254
+ */
255
+ template <typename U, bool OtherCo, bool OtherEnsure1Criticality>
256
+ Degree_rips_bifiltration &operator=(const Degree_rips_bifiltration<U, OtherCo, OtherEnsure1Criticality> &other)
257
+ {
258
+ if constexpr (Ensure1Criticality && !OtherEnsure1Criticality) {
259
+ if (other.num_generators() > 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
260
+ }
261
+ generators_ = Underlying_container(other.begin(), other.end());
262
+ return *this;
263
+ }
264
+
265
+ /**
266
+ * @brief Swap operator.
267
+ */
268
+ friend void swap(Degree_rips_bifiltration &f1, Degree_rips_bifiltration &f2) noexcept
269
+ {
270
+ f1.generators_.swap(f2.generators_);
271
+ }
272
+
273
+ // VECTOR-LIKE
274
+
275
+ using value_type = T; /**< Value type. */
276
+ using size_type = typename Underlying_container::size_type; /**< Size type. */
277
+ using difference_type = typename Underlying_container::difference_type; /**< Difference type. */
278
+ using reference = value_type &; /**< Reference type. */
279
+ using const_reference = const value_type &; /**< Const reference type. */
280
+ using pointer = typename Underlying_container::pointer; /**< Pointer type. */
281
+ using const_pointer = typename Underlying_container::const_pointer; /**< Const pointer type. */
282
+ using iterator = typename Underlying_container::iterator; /**< Iterator type. */
283
+ using const_iterator = typename Underlying_container::const_iterator; /**< Const iterator type. */
284
+ using reverse_iterator = typename Underlying_container::reverse_iterator; /**< Reverse iterator type. */
285
+ using const_reverse_iterator = typename Underlying_container::const_reverse_iterator; /**< Const reverse iterator. */
286
+
287
+ /**
288
+ * @brief Returns reference to value of parameter `p` of generator `g`.
289
+ *
290
+ * The reference returned when `p` is 1 can be modified but will have no impact on the value of the second parameter
291
+ * represented on the class and is shared by the second parameter of all generators. If there is a need to store this
292
+ * value, a copy should be stored instead.
293
+ */
294
+ reference operator()(size_type g, size_type p)
295
+ {
296
+ GUDHI_CHECK(g < generators_.size() && p < 2, std::out_of_range("Out of bound index."));
297
+ if (p == 1) {
298
+ dummy_g_ = g;
299
+ return dummy_g_;
300
+ }
301
+ return generators_[g];
302
+ }
303
+
304
+ /**
305
+ * @brief Returns const reference to value of parameter `p` of generator `g`.
306
+ */
307
+ const_reference operator()(size_type g, size_type p) const
308
+ {
309
+ GUDHI_CHECK(g < generators_.size() && p < 2, std::out_of_range("Out of bound index."));
310
+ if (p == 1) {
311
+ dummy_g_ = g;
312
+ return dummy_g_;
313
+ }
314
+ return generators_[g];
315
+ }
316
+
317
+ /**
318
+ * @brief Let \f$ g \f$ be the first value in `indices` and \f$ p \f$ the second value.
319
+ * Returns reference to value of parameter \f$ p \f$ of generator \f$ g \f$.
320
+ *
321
+ * The reference returned when `p` is 1 can be modified but will have no impact on the value of the second parameter
322
+ * represented on the class and is shared by the second parameter of all generators. If there is a need to store this
323
+ * value, a copy should be stored instead.
324
+ *
325
+ * @tparam IndexRange Range with a begin() and size() method.
326
+ * @param indices Range with at least two elements. The first element should correspond to the generator number and
327
+ * the second element to the parameter number.
328
+ */
329
+ template <class IndexRange = std::initializer_list<size_type>,
330
+ class = std::enable_if_t<RangeTraits<IndexRange>::has_begin> >
331
+ reference operator[](const IndexRange &indices)
332
+ {
333
+ GUDHI_CHECK(indices.size() == 2,
334
+ std::invalid_argument(
335
+ "Exactly 2 indices allowed only: first the generator number, second the parameter number."));
336
+ auto it = indices.begin();
337
+ size_type g = *it;
338
+ return this->operator()(g, *(++it));
339
+ }
340
+
341
+ /**
342
+ * @brief Let \f$ g \f$ be the first value in `indices` and \f$ p \f$ the second value.
343
+ * Returns reference to value of parameter \f$ p \f$ of generator \f$ g \f$.
344
+ *
345
+ * @tparam IndexRange Range with a begin() and size() method.
346
+ * @param indices Range with at least two elements. The first element should correspond to the generator number and
347
+ * the second element to the parameter number.
348
+ */
349
+ template <class IndexRange = std::initializer_list<size_type>,
350
+ class = std::enable_if_t<RangeTraits<IndexRange>::has_begin> >
351
+ const_reference operator[](const IndexRange &indices) const
352
+ {
353
+ GUDHI_CHECK(indices.size() == 2,
354
+ std::invalid_argument(
355
+ "Exactly 2 indices allowed only: first the generator number, second the parameter number."));
356
+ auto it = indices.begin();
357
+ size_type g = *it;
358
+ return this->operator()(g, *(++it));
359
+ }
360
+
361
+ /**
362
+ * @brief Returns an iterator pointing the begining of the underlying container. The element `val_i` at index `i`
363
+ * corresponds to the first parameter of the generator `(val_i, i)`.
364
+ */
365
+ iterator begin() noexcept { return generators_.begin(); }
366
+
367
+ /**
368
+ * @brief Returns an iterator pointing the begining of the underlying container. The element `val_i` at index `i`
369
+ * corresponds to the first parameter of the generator `(val_i, i)`.
370
+ */
371
+ const_iterator begin() const noexcept { return generators_.begin(); }
372
+
373
+ /**
374
+ * @brief Returns an iterator pointing the begining of the underlying container. The element `val_i` at index `i`
375
+ * corresponds to the first parameter of the generator `(val_i, i)`.
376
+ */
377
+ const_iterator cbegin() const noexcept { return generators_.cbegin(); }
378
+
379
+ /**
380
+ * @brief Returns an iterator pointing the end of the underlying container.
381
+ */
382
+ iterator end() noexcept { return generators_.end(); }
383
+
384
+ /**
385
+ * @brief Returns an iterator pointing the end of the underlying container.
386
+ */
387
+ const_iterator end() const noexcept { return generators_.end(); }
388
+
389
+ /**
390
+ * @brief Returns an iterator pointing the end of the underlying container.
391
+ */
392
+ const_iterator cend() const noexcept { return generators_.cend(); }
393
+
394
+ /**
395
+ * @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
396
+ * The element `val_i` at index `i` corresponds to the first parameter of the generator `(val_i, i)`.
397
+ */
398
+ reverse_iterator rbegin() noexcept { return generators_.rbegin(); }
399
+
400
+ /**
401
+ * @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
402
+ * The element `val_i` at index `i` corresponds to the first parameter of the generator `(val_i, i)`.
403
+ */
404
+ const_reverse_iterator rbegin() const noexcept { return generators_.rbegin(); }
405
+
406
+ /**
407
+ * @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
408
+ * The element `val_i` at index `i` corresponds to the first parameter of the generator `(val_i, i)`.
409
+ */
410
+ const_reverse_iterator crbegin() const noexcept { return generators_.crbegin(); }
411
+
412
+ /**
413
+ * @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
414
+ */
415
+ reverse_iterator rend() noexcept { return generators_.rend(); }
416
+
417
+ /**
418
+ * @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
419
+ */
420
+ const_reverse_iterator rend() const noexcept { return generators_.rend(); }
421
+
422
+ /**
423
+ * @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
424
+ */
425
+ const_reverse_iterator crend() const noexcept { return generators_.crend(); }
426
+
427
+ /**
428
+ * @brief Returns the size of the underlying container. Corresponds exactly to @ref num_generators(), but enables
429
+ * to use the class as a classic range with a `begin`, `end` and `size` method.
430
+ */
431
+ size_type size() const noexcept { return generators_.size(); }
432
+
433
+ /**
434
+ * @brief Reserves space for the given number of generators in the underlying container. Does nothing if
435
+ * `Ensure1Criticality` is true.
436
+ */
437
+ void reserve([[maybe_unused]] size_type number_of_generators)
438
+ {
439
+ if constexpr (Ensure1Criticality) {
440
+ return;
441
+ } else {
442
+ generators_.reserve(number_of_generators);
443
+ }
444
+ }
445
+
446
+ // CONVERTERS
447
+
448
+ // like numpy
449
+ /**
450
+ * @brief Returns a copy with entries casted into the type given as template parameter.
451
+ *
452
+ * @tparam U New type for the entries.
453
+ * @tparam OCo New value for `Co`. Default value: `Co`.
454
+ * @tparam OEns New value for `Ensure1Criticality`. Note that if `OEns` is set to true and the value is not
455
+ * 1-critical, the method will throw. Default value: `Ensure1Criticality`.
456
+ * @return Copy with new entry type.
457
+ */
458
+ template <typename U, bool OCo = Co, bool OEns = Ensure1Criticality>
459
+ Degree_rips_bifiltration<U, OCo, OEns> as_type() const
460
+ {
461
+ std::vector<U> out(generators_.begin(), generators_.end());
462
+ return Degree_rips_bifiltration<U, OCo, OEns>(std::move(out), num_parameters());
463
+ }
464
+
465
+ /**
466
+ * @brief Converts the filtration value to @ref Multi_parameter_filtration without any set simplification.
467
+ * @warning The filtration value is converted one to one and is not simplified to a minimal set of generators or
468
+ * ordered by lexicographical order, that undefines the behaviour of some methods of the class.
469
+ * Use @ref as_type(const Degree_rips_bifiltration&) instead if needed.
470
+ */
471
+ Multi_parameter_filtration<T, Co, Ensure1Criticality> convert_to_non_simplified_multi_parameter_filtration() const
472
+ {
473
+ std::vector<T> out(generators_.size() * 2);
474
+ size_type i = 0;
475
+ for (size_type g = 0; g < generators_.size(); ++g) {
476
+ out[i] = generators_[g];
477
+ out[i + 1] = g;
478
+ i += 2;
479
+ }
480
+ return Multi_parameter_filtration<T, Co, Ensure1Criticality>(std::move(out), 2);
481
+ }
482
+
483
+ /**
484
+ * @brief Converts the filtration value to @ref Dynamic_multi_parameter_filtration without any set simplification.
485
+ * @warning The filtration value is converted one to one and is not simplified to a minimal set of generators or
486
+ * ordered by lexicographical order, that undefines the behaviour of some methods of the class.
487
+ * Use @ref as_type(const Degree_rips_bifiltration&) instead if needed.
488
+ */
489
+ Dynamic_multi_parameter_filtration<T, Co, Ensure1Criticality>
490
+ convert_to_non_simplified_dynamic_multi_parameter_filtration() const
491
+ {
492
+ std::vector<Multi_parameter_generator<T> > out;
493
+ out.reserve(generators_.size());
494
+ for (size_type g = 0; g < generators_.size(); ++g) {
495
+ std::vector<T> v = {generators_[g], static_cast<T>(g)};
496
+ out.emplace_back(std::move(v));
497
+ }
498
+ return Dynamic_multi_parameter_filtration<T, Co, Ensure1Criticality>(std::move(out), 2);
499
+ }
500
+
501
+ // ACCESS
502
+
503
+ /**
504
+ * @brief Returns the number of parameters in the filtration value.
505
+ */
506
+ static constexpr size_type num_parameters() { return 2; }
507
+
508
+ /**
509
+ * @brief Returns the number of generators in the filtration value, i.e. the criticality of the element.
510
+ */
511
+ size_type num_generators() const { return generators_.size(); }
512
+
513
+ /**
514
+ * @brief Returns the total number of values in the filtration value, that is,
515
+ * @ref num_parameters() * @ref num_generators().
516
+ */
517
+ size_type num_entries() const { return generators_.size() * 2; }
518
+
519
+ /**
520
+ * @brief Returns a filtration value for which @ref is_plus_inf() returns `true`. Throws if `Co` is true.
521
+ */
522
+ static Degree_rips_bifiltration inf(int number_of_parameters = 2)
523
+ {
524
+ if constexpr (Co) {
525
+ throw std::logic_error("No biggest value possible for Co-filtrations yet.");
526
+ } else {
527
+ return Degree_rips_bifiltration(number_of_parameters, T_inf);
528
+ }
529
+ }
530
+
531
+ /**
532
+ * @brief Returns a filtration value for which @ref is_minus_inf() returns `true`.
533
+ */
534
+ static Degree_rips_bifiltration minus_inf(int number_of_parameters = 2)
535
+ {
536
+ return Degree_rips_bifiltration(number_of_parameters, T_m_inf);
537
+ }
538
+
539
+ /**
540
+ * @brief Returns a filtration value for which @ref is_nan() returns `true`.
541
+ */
542
+ static constexpr Degree_rips_bifiltration nan([[maybe_unused]] int number_of_parameters = 2)
543
+ {
544
+ return Degree_rips_bifiltration(Gudhi::simplex_tree::empty_filtration_value_t());
545
+ }
546
+
547
+ // DESCRIPTORS
548
+
549
+ /**
550
+ * @brief Returns value of `Ensure1Criticality`.
551
+ */
552
+ static constexpr bool ensures_1_criticality() { return Ensure1Criticality; }
553
+
554
+ /**
555
+ * @brief Returns value of `Co`.
556
+ */
557
+ static constexpr bool has_negative_cones() { return Co; }
558
+
559
+ /**
560
+ * @brief Returns `true` if and only if the filtration value is considered as plus infinity.
561
+ */
562
+ constexpr bool is_plus_inf() const
563
+ {
564
+ if constexpr (Co) {
565
+ return false;
566
+ } else {
567
+ if (generators_.empty()) return false;
568
+ for (const T &v : generators_) {
569
+ if (v != T_inf) return false;
570
+ }
571
+ return true;
572
+ }
573
+ }
574
+
575
+ /**
576
+ * @brief Returns `true` if and only if the filtration value is considered as minus infinity.
577
+ */
578
+ constexpr bool is_minus_inf() const
579
+ {
580
+ if constexpr (Co) {
581
+ return generators_.size() == 1 && generators_[0] == T_m_inf;
582
+ } else {
583
+ return !generators_.empty() && generators_[0] == T_m_inf;
584
+ }
585
+ }
586
+
587
+ /**
588
+ * @brief Returns `true` if and only if the filtration value is considered as NaN.
589
+ */
590
+ constexpr bool is_nan() const { return generators_.empty(); }
591
+
592
+ /**
593
+ * @brief Returns `true` if and only if the filtration value is non-empty and is not considered as plus infinity,
594
+ * minus infinity or NaN.
595
+ */
596
+ bool is_finite() const
597
+ {
598
+ if constexpr (Co) {
599
+ return !generators_.empty() && (generators_.size() != 1 || generators_[0] != T_m_inf);
600
+ } else {
601
+ if (generators_.empty() || generators_[0] == T_m_inf) return false;
602
+ for (const T &v : generators_) {
603
+ if (v != T_inf) return true;
604
+ }
605
+ return false;
606
+ }
607
+ }
608
+
609
+ // COMPARAISON OPERATORS
610
+
611
+ /**
612
+ * @brief Returns `true` if and only if the first argument is lexicographically strictly less than the second
613
+ * argument. The "words" considered for the lexicographical order are all the generators concatenated together
614
+ * in order of generator index and then in order of parameter index. Different from @ref operator< "", this order
615
+ * is total.
616
+ *
617
+ * @tparam inverse If true, the parameter index and generator index order is inverted.
618
+ */
619
+ template <bool inverse = false>
620
+ friend bool is_strict_less_than_lexicographically(const Degree_rips_bifiltration &a,
621
+ const Degree_rips_bifiltration &b)
622
+ {
623
+ if (&a == &b) return false;
624
+ if (a.is_nan()) return false;
625
+ if (b.is_nan()) return true;
626
+
627
+ if constexpr (inverse) {
628
+ if (a.num_generators() != b.num_generators()) {
629
+ if (a.num_generators() == 0) return true;
630
+ if (b.num_generators() == 0) return false;
631
+ if (a.generators_[0] < b.generators_[0]) return true;
632
+ if (b.generators_[0] < a.generators_[0]) return false;
633
+ return a.num_generators() < b.num_generators();
634
+ }
635
+ }
636
+
637
+ for (std::size_t i = 0U; i < std::min(a.num_generators(), b.num_generators()); ++i) {
638
+ if constexpr (inverse) i = std::min(a.num_generators(), b.num_generators()) - 1 - i;
639
+ if (_is_nan(a.generators_[i]) && !_is_nan(b.generators_[i])) return false;
640
+ if (_is_nan(b.generators_[i])) return true;
641
+ if (a.generators_[i] < b.generators_[i]) return true;
642
+ if (b.generators_[i] < a.generators_[i]) return false;
643
+ if constexpr (inverse) i = std::min(a.num_generators(), b.num_generators()) - 1 - i;
644
+ }
645
+ return a.num_generators() < b.num_generators();
646
+ }
647
+
648
+ /**
649
+ * @brief Returns `true` if and only if the first argument is lexicographically less than or equal to the second
650
+ * argument. The "words" considered for the lexicographical order are all the generators concatenated together
651
+ * in order of generator index and then in order of parameter index. Different from @ref operator<= "", this order
652
+ * is total.
653
+ *
654
+ * @tparam inverse If true, the parameter index and generator index order is inverted.
655
+ */
656
+ template <bool inverse = false>
657
+ friend bool is_less_or_equal_than_lexicographically(const Degree_rips_bifiltration &a,
658
+ const Degree_rips_bifiltration &b)
659
+ {
660
+ if (&a == &b) return true;
661
+ if (b.is_nan()) return true;
662
+ if (a.is_nan()) return false;
663
+
664
+ if constexpr (inverse) {
665
+ if (a.num_generators() != b.num_generators()) {
666
+ if (a.num_generators() == 0) return true;
667
+ if (b.num_generators() == 0) return false;
668
+ if (a.generators_[0] < b.generators_[0]) return true;
669
+ if (b.generators_[0] < a.generators_[0]) return false;
670
+ return a.num_generators() < b.num_generators();
671
+ }
672
+ }
673
+
674
+ for (std::size_t i = 0U; i < std::min(a.num_generators(), b.num_generators()); ++i) {
675
+ if constexpr (inverse) i = std::min(a.num_generators(), b.num_generators()) - 1 - i;
676
+ if (_is_nan(a.generators_[i]) && !_is_nan(b.generators_[i])) return false;
677
+ if (_is_nan(b.generators_[i])) return true;
678
+ if (a.generators_[i] < b.generators_[i]) return true;
679
+ if (b.generators_[i] < a.generators_[i]) return false;
680
+ if constexpr (inverse) i = std::min(a.num_generators(), b.num_generators()) - 1 - i;
681
+ }
682
+ return a.num_generators() <= b.num_generators();
683
+ }
684
+
685
+ /**
686
+ * @brief Returns `true` if and only if the cones generated by @p b are strictly contained in the
687
+ * cones generated by @p a (recall that the cones are positive if `Co` is false and negative if `Co` is true).
688
+ *
689
+ * Note that not all filtration values are comparable. That is, \f$ a < b \f$ and \f$ b < a \f$ returning both false
690
+ * does **not** imply \f$ a == b \f$. If a total order is needed, use @ref is_strict_less_than_lexicographically
691
+ * instead.
692
+ */
693
+ friend bool operator<(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b)
694
+ {
695
+ if (&a == &b) return false;
696
+ if (a.generators_.size() == 0 || b.generators_.size() == 0) return false;
697
+ return _compare_strict(0, a.generators_, b.generators_, a.generators_[0]);
698
+ }
699
+
700
+ /**
701
+ * @brief Returns `true` if and only if the cones generated by @p a are strictly contained in the
702
+ * cones generated by @p b (recall that the cones are positive if `Co` is false and negative if `Co` is true).
703
+ *
704
+ * Note that not all filtration values are comparable. That is, \f$ a \le b \f$ and \f$ b \le a \f$ can both return
705
+ * `false`. If a total order is needed, use @ref is_less_or_equal_than_lexicographically instead.
706
+ */
707
+ friend bool operator<=(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b)
708
+ {
709
+ if (a.generators_.size() == 0 || b.generators_.size() == 0) return false;
710
+ if (&a == &b) return true;
711
+ return _compare(0, a.generators_, b.generators_, a.generators_[0]);
712
+ }
713
+
714
+ /**
715
+ * @brief Returns `true` if and only if the cones generated by @p b are contained in or are (partially)
716
+ * equal to the cones generated by @p a (recall that the cones are positive if `Co` is false and negative if `Co` is
717
+ * true).
718
+ *
719
+ * Note that not all filtration values are comparable. That is, \f$ a > b \f$ and \f$ b > a \f$ returning both false
720
+ * does **not** imply \f$ a == b \f$. If a total order is needed, use @ref is_strict_less_than_lexicographically
721
+ * instead.
722
+ */
723
+ friend bool operator>(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b) { return b < a; }
724
+
725
+ /**
726
+ * @brief Returns `true` if and only if the cones generated by @p a are contained in or are (partially)
727
+ * equal to the cones generated by @p b (recall that the cones are positive if `Co` is false and negative if `Co` is
728
+ * true).
729
+ *
730
+ * Note that not all filtration values are comparable. That is, \f$ a \ge b \f$ and \f$ b \ge a \f$ can both return
731
+ * `false`. If a total order is needed, use @ref is_less_or_equal_than_lexicographically instead.
732
+ */
733
+ friend bool operator>=(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b) { return b <= a; }
734
+
735
+ /**
736
+ * @brief Returns `true` if and only if for each \f$ i,j \f$, \f$ a(i,j) \f$ is equal to \f$ b(i,j) \f$.
737
+ *
738
+ * @warning The method considers different two filtration values with different generators, but as the set of
739
+ * generators is rarely minimal, it is still possible that those two values are equivalent.
740
+ */
741
+ friend bool operator==(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b)
742
+ {
743
+ if (a.is_nan() || b.is_nan()) return false;
744
+ if (&a == &b) return true;
745
+ return a.generators_ == b.generators_;
746
+ }
747
+
748
+ /**
749
+ * @brief Returns `true` if and only if \f$ a == b \f$ returns `false`.
750
+ */
751
+ friend bool operator!=(const Degree_rips_bifiltration &a, const Degree_rips_bifiltration &b) { return !(a == b); }
752
+
753
+ // ARITHMETIC OPERATORS
754
+
755
+ // opposite
756
+ /**
757
+ * @brief Returns a filtration value such that an entry at index \f$ i,0 \f$ is equal to \f$ -f(i,0) \f$.
758
+ *
759
+ * Used conventions:
760
+ * - \f$ -NaN = NaN \f$.
761
+ *
762
+ * @param f Value to opposite.
763
+ * @return The opposite of @p f.
764
+ */
765
+ friend Degree_rips_bifiltration operator-(const Degree_rips_bifiltration &f)
766
+ {
767
+ using F = Degree_rips_bifiltration;
768
+
769
+ Underlying_container result(f.generators_);
770
+ std::for_each(result.begin(), result.end(), [](T &v) {
771
+ if (v == F::T_inf)
772
+ v = F::T_m_inf;
773
+ else if (v == F::T_m_inf)
774
+ v = F::T_inf;
775
+ else
776
+ v = -v;
777
+ });
778
+ return Degree_rips_bifiltration(std::move(result), Degree_rips_bifiltration::num_parameters());
779
+ }
780
+
781
+ // subtraction
782
+ /**
783
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
784
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) - r(0) \f$
785
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
786
+ *
787
+ * Used conventions:
788
+ * - \f$ inf - inf = NaN \f$,
789
+ * - \f$ -inf - (-inf) = NaN \f$,
790
+ * - \f$ NaN - b = NaN \f$,
791
+ * - \f$ a - NaN = NaN \f$.
792
+ *
793
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
794
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
795
+ *
796
+ * @tparam ValueRange Range with a begin() and end() method.
797
+ * @param f First element of the subtraction.
798
+ * @param r Second element of the subtraction.
799
+ */
800
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
801
+ friend Degree_rips_bifiltration operator-(Degree_rips_bifiltration f, const ValueRange &r)
802
+ {
803
+ f -= r;
804
+ return f;
805
+ }
806
+
807
+ /**
808
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
809
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ r(0) - f(g,0) \f$
810
+ * if \f$ 0 < length_r \f$ and to -\f$ f(g,0) \f$ otherwise.
811
+ *
812
+ * Used conventions:
813
+ * - \f$ inf - inf = NaN \f$,
814
+ * - \f$ -inf - (-inf) = NaN \f$,
815
+ * - \f$ NaN - b = NaN \f$,
816
+ * - \f$ a - NaN = NaN \f$.
817
+ *
818
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
819
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
820
+ *
821
+ * @tparam ValueRange Range with a begin() and end() method.
822
+ * @param r First element of the subtraction.
823
+ * @param f Second element of the subtraction.
824
+ */
825
+ template <class ValueRange,
826
+ class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
827
+ !std::is_same_v<ValueRange, Degree_rips_bifiltration> > >
828
+ friend Degree_rips_bifiltration operator-(const ValueRange &r, Degree_rips_bifiltration f)
829
+ {
830
+ if (r.begin() == r.end()) return -f;
831
+ return *(r.begin()) - f;
832
+ }
833
+
834
+ /**
835
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) - val \f$.
836
+ *
837
+ * Used conventions:
838
+ * - \f$ inf - inf = NaN \f$,
839
+ * - \f$ -inf - (-inf) = NaN \f$,
840
+ * - \f$ NaN - b = NaN \f$,
841
+ * - \f$ a - NaN = NaN \f$.
842
+ *
843
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
844
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
845
+ *
846
+ * @param f First element of the subtraction.
847
+ * @param val Second element of the subtraction.
848
+ */
849
+ friend Degree_rips_bifiltration operator-(Degree_rips_bifiltration f, const T &val)
850
+ {
851
+ f -= val;
852
+ return f;
853
+ }
854
+
855
+ /**
856
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ val - f(g,0) \f$.
857
+ *
858
+ * Used conventions:
859
+ * - \f$ inf - inf = NaN \f$,
860
+ * - \f$ -inf - (-inf) = NaN \f$,
861
+ * - \f$ NaN - b = NaN \f$,
862
+ * - \f$ a - NaN = NaN \f$.
863
+ *
864
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
865
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
866
+ *
867
+ * @param val First element of the subtraction.
868
+ * @param f Second element of the subtraction.
869
+ */
870
+ friend Degree_rips_bifiltration operator-(const T &val, Degree_rips_bifiltration f)
871
+ {
872
+ f._apply_operation(val, [](T &valF, const T &valR) {
873
+ valF = -valF;
874
+ _add(valF, valR);
875
+ });
876
+ return f;
877
+ }
878
+
879
+ /**
880
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$, with
881
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) - r(0) \f$
882
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
883
+ *
884
+ * Used conventions:
885
+ * - \f$ inf - inf = NaN \f$,
886
+ * - \f$ -inf - (-inf) = NaN \f$,
887
+ * - \f$ NaN - b = NaN \f$,
888
+ * - \f$ a - NaN = NaN \f$.
889
+ *
890
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
891
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
892
+ *
893
+ * @tparam ValueRange Range with a begin() and end() method.
894
+ * @param f First element of the subtraction.
895
+ * @param r Second element of the subtraction.
896
+ */
897
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
898
+ friend Degree_rips_bifiltration &operator-=(Degree_rips_bifiltration &f, const ValueRange &r)
899
+ {
900
+ if (r.begin() == r.end()) return f;
901
+ f -= *(r.begin());
902
+ return f;
903
+ }
904
+
905
+ /**
906
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) - val \f$.
907
+ *
908
+ * Used conventions:
909
+ * - \f$ inf - inf = NaN \f$,
910
+ * - \f$ -inf - (-inf) = NaN \f$,
911
+ * - \f$ NaN - b = NaN \f$,
912
+ * - \f$ a - NaN = NaN \f$.
913
+ *
914
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
915
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
916
+ *
917
+ * @param f First element of the subtraction.
918
+ * @param val Second element of the subtraction.
919
+ */
920
+ friend Degree_rips_bifiltration &operator-=(Degree_rips_bifiltration &f, const T &val)
921
+ {
922
+ f._apply_operation(val, [](T &valF, const T &valR) { _subtract(valF, valR); });
923
+ return f;
924
+ }
925
+
926
+ // addition
927
+ /**
928
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
929
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) + r(0) \f$
930
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
931
+ *
932
+ * Used conventions:
933
+ * - \f$ inf + (-inf) = NaN \f$,
934
+ * - \f$ -inf + inf = NaN \f$,
935
+ * - \f$ NaN + b = NaN \f$,
936
+ * - \f$ a + NaN = NaN \f$.
937
+ *
938
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
939
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
940
+ *
941
+ * @tparam ValueRange Range with a begin() and end() method.
942
+ * @param f First element of the addition.
943
+ * @param r Second element of the addition.
944
+ */
945
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
946
+ friend Degree_rips_bifiltration operator+(Degree_rips_bifiltration f, const ValueRange &r)
947
+ {
948
+ f += r;
949
+ return f;
950
+ }
951
+
952
+ /**
953
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
954
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ r(0) + f(g,0) \f$
955
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
956
+ *
957
+ * Used conventions:
958
+ * - \f$ inf + (-inf) = NaN \f$,
959
+ * - \f$ -inf + inf = NaN \f$,
960
+ * - \f$ NaN + b = NaN \f$,
961
+ * - \f$ a + NaN = NaN \f$.
962
+ *
963
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
964
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
965
+ *
966
+ * @tparam ValueRange Range with a begin() and end() method.
967
+ * @param r First element of the addition.
968
+ * @param f Second element of the addition.
969
+ */
970
+ template <class ValueRange,
971
+ class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
972
+ !std::is_same_v<ValueRange, Degree_rips_bifiltration> > >
973
+ friend Degree_rips_bifiltration operator+(const ValueRange &r, Degree_rips_bifiltration f)
974
+ {
975
+ f += r;
976
+ return f;
977
+ }
978
+
979
+ /**
980
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) + val \f$.
981
+ *
982
+ * Used conventions:
983
+ * - \f$ inf + (-inf) = NaN \f$,
984
+ * - \f$ -inf + inf = NaN \f$,
985
+ * - \f$ NaN + b = NaN \f$,
986
+ * - \f$ a + NaN = NaN \f$.
987
+ *
988
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
989
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
990
+ *
991
+ * @param f First element of the addition.
992
+ * @param val Second element of the addition.
993
+ */
994
+ friend Degree_rips_bifiltration operator+(Degree_rips_bifiltration f, const T &val)
995
+ {
996
+ f += val;
997
+ return f;
998
+ }
999
+
1000
+ /**
1001
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ val + f(g,0) \f$.
1002
+ *
1003
+ * Used conventions:
1004
+ * - \f$ inf + (-inf) = NaN \f$,
1005
+ * - \f$ -inf + inf = NaN \f$,
1006
+ * - \f$ NaN + b = NaN \f$,
1007
+ * - \f$ a + NaN = NaN \f$.
1008
+ *
1009
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1010
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1011
+ *
1012
+ * @param val First element of the addition.
1013
+ * @param f Second element of the addition.
1014
+ */
1015
+ friend Degree_rips_bifiltration operator+(const T &val, Degree_rips_bifiltration f)
1016
+ {
1017
+ f += val;
1018
+ return f;
1019
+ }
1020
+
1021
+ /**
1022
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$, with
1023
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) + r(0) \f$
1024
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1025
+ *
1026
+ * Used conventions:
1027
+ * - \f$ inf + (-inf) = NaN \f$,
1028
+ * - \f$ -inf + inf = NaN \f$,
1029
+ * - \f$ NaN + b = NaN \f$,
1030
+ * - \f$ a + NaN = NaN \f$.
1031
+ *
1032
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1033
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1034
+ *
1035
+ * @tparam ValueRange Range with a begin() and end() method.
1036
+ * @param f First element of the addition.
1037
+ * @param r Second element of the addition.
1038
+ */
1039
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
1040
+ friend Degree_rips_bifiltration &operator+=(Degree_rips_bifiltration &f, const ValueRange &r)
1041
+ {
1042
+ if (r.begin() == r.end()) return f;
1043
+ f += *(r.begin());
1044
+ return f;
1045
+ }
1046
+
1047
+ /**
1048
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) + val \f$.
1049
+ *
1050
+ * Used conventions:
1051
+ * - \f$ inf + (-inf) = NaN \f$,
1052
+ * - \f$ -inf + inf = NaN \f$,
1053
+ * - \f$ NaN + b = NaN \f$,
1054
+ * - \f$ a + NaN = NaN \f$.
1055
+ *
1056
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1057
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1058
+ *
1059
+ * @param f First element of the addition.
1060
+ * @param val Second element of the addition.
1061
+ */
1062
+ friend Degree_rips_bifiltration &operator+=(Degree_rips_bifiltration &f, const T &val)
1063
+ {
1064
+ f._apply_operation(val, [](T &valF, const T &valR) { _add(valF, valR); });
1065
+ return f;
1066
+ }
1067
+
1068
+ // multiplication
1069
+ /**
1070
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
1071
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) * r(0) \f$
1072
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1073
+ *
1074
+ * Used conventions:
1075
+ * - \f$ inf * 0 = NaN \f$,
1076
+ * - \f$ 0 * inf = NaN \f$,
1077
+ * - \f$ -inf * 0 = NaN \f$,
1078
+ * - \f$ 0 * (-inf) = NaN \f$,
1079
+ * - \f$ NaN * b = NaN \f$,
1080
+ * - \f$ a * NaN = NaN \f$.
1081
+ *
1082
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1083
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1084
+ *
1085
+ * @tparam ValueRange Range with a begin() and end() method.
1086
+ * @param f First element of the multiplication.
1087
+ * @param r Second element of the multiplication.
1088
+ */
1089
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
1090
+ friend Degree_rips_bifiltration operator*(Degree_rips_bifiltration f, const ValueRange &r)
1091
+ {
1092
+ f *= r;
1093
+ return f;
1094
+ }
1095
+
1096
+ /**
1097
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
1098
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ r(0) * f(g,0) \f$
1099
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1100
+ *
1101
+ * Used conventions:
1102
+ * - \f$ inf * 0 = NaN \f$,
1103
+ * - \f$ 0 * inf = NaN \f$,
1104
+ * - \f$ -inf * 0 = NaN \f$,
1105
+ * - \f$ 0 * (-inf) = NaN \f$,
1106
+ * - \f$ NaN * b = NaN \f$,
1107
+ * - \f$ a * NaN = NaN \f$.
1108
+ *
1109
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1110
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1111
+ *
1112
+ * @tparam ValueRange Range with a begin() and end() method.
1113
+ * @param r First element of the multiplication.
1114
+ * @param f Second element of the multiplication.
1115
+ */
1116
+ template <class ValueRange,
1117
+ class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
1118
+ !std::is_same_v<ValueRange, Degree_rips_bifiltration> > >
1119
+ friend Degree_rips_bifiltration operator*(const ValueRange &r, Degree_rips_bifiltration f)
1120
+ {
1121
+ f *= r;
1122
+ return f;
1123
+ }
1124
+
1125
+ /**
1126
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) * val \f$.
1127
+ *
1128
+ * Used conventions:
1129
+ * - \f$ inf * 0 = NaN \f$,
1130
+ * - \f$ 0 * inf = NaN \f$,
1131
+ * - \f$ -inf * 0 = NaN \f$,
1132
+ * - \f$ 0 * (-inf) = NaN \f$,
1133
+ * - \f$ NaN * b = NaN \f$,
1134
+ * - \f$ a * NaN = NaN \f$.
1135
+ *
1136
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1137
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1138
+ *
1139
+ * @param f First element of the multiplication.
1140
+ * @param val Second element of the multiplication.
1141
+ */
1142
+ friend Degree_rips_bifiltration operator*(Degree_rips_bifiltration f, const T &val)
1143
+ {
1144
+ f *= val;
1145
+ return f;
1146
+ }
1147
+
1148
+ /**
1149
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ val * f(g,0) \f$.
1150
+ *
1151
+ * Used conventions:
1152
+ * - \f$ inf * 0 = NaN \f$,
1153
+ * - \f$ 0 * inf = NaN \f$,
1154
+ * - \f$ -inf * 0 = NaN \f$,
1155
+ * - \f$ 0 * (-inf) = NaN \f$,
1156
+ * - \f$ NaN * b = NaN \f$,
1157
+ * - \f$ a * NaN = NaN \f$.
1158
+ *
1159
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1160
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1161
+ *
1162
+ * @param val First element of the multiplication.
1163
+ * @param f Second element of the multiplication.
1164
+ */
1165
+ friend Degree_rips_bifiltration operator*(const T &val, Degree_rips_bifiltration f)
1166
+ {
1167
+ f *= val;
1168
+ return f;
1169
+ }
1170
+
1171
+ /**
1172
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$, with
1173
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) + r(0) \f$
1174
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1175
+ *
1176
+ * Used conventions:
1177
+ * - \f$ inf * 0 = NaN \f$,
1178
+ * - \f$ 0 * inf = NaN \f$,
1179
+ * - \f$ -inf * 0 = NaN \f$,
1180
+ * - \f$ 0 * (-inf) = NaN \f$,
1181
+ * - \f$ NaN * b = NaN \f$,
1182
+ * - \f$ a * NaN = NaN \f$.
1183
+ *
1184
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1185
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1186
+ *
1187
+ * @tparam ValueRange Range with a begin() and end() method.
1188
+ * @param f First element of the multiplication.
1189
+ * @param r Second element of the multiplication.
1190
+ */
1191
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
1192
+ friend Degree_rips_bifiltration &operator*=(Degree_rips_bifiltration &f, const ValueRange &r)
1193
+ {
1194
+ if (r.begin() == r.end()) return f;
1195
+ f *= *(r.begin());
1196
+ return f;
1197
+ }
1198
+
1199
+ /**
1200
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) * val \f$.
1201
+ *
1202
+ * Used conventions:
1203
+ * - \f$ inf * 0 = NaN \f$,
1204
+ * - \f$ 0 * inf = NaN \f$,
1205
+ * - \f$ -inf * 0 = NaN \f$,
1206
+ * - \f$ 0 * (-inf) = NaN \f$,
1207
+ * - \f$ NaN * b = NaN \f$,
1208
+ * - \f$ a * NaN = NaN \f$.
1209
+ *
1210
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1211
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1212
+ *
1213
+ * @param f First element of the multiplication.
1214
+ * @param val Second element of the multiplication.
1215
+ */
1216
+ friend Degree_rips_bifiltration &operator*=(Degree_rips_bifiltration &f, const T &val)
1217
+ {
1218
+ f._apply_operation(val, [](T &valF, const T &valR) { _multiply(valF, valR); });
1219
+ return f;
1220
+ }
1221
+
1222
+ // division
1223
+ /**
1224
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
1225
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) / r(0) \f$
1226
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1227
+ *
1228
+ * Used conventions:
1229
+ * - \f$ a / 0 = NaN \f$,
1230
+ * - \f$ inf / inf = NaN \f$,
1231
+ * - \f$ -inf / inf = NaN \f$,
1232
+ * - \f$ inf / -inf = NaN \f$,
1233
+ * - \f$ -inf / -inf = NaN \f$,
1234
+ * - \f$ NaN / b = NaN \f$,
1235
+ * - \f$ a / NaN = NaN \f$,
1236
+ * - \f$ a / inf = 0 \f$,
1237
+ * - \f$ a / -inf = 0 \f$.
1238
+ *
1239
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1240
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1241
+ *
1242
+ * @tparam ValueRange Range with a begin() and end() method.
1243
+ * @param f First element of the division.
1244
+ * @param r Second element of the division.
1245
+ */
1246
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
1247
+ friend Degree_rips_bifiltration operator/(Degree_rips_bifiltration f, const ValueRange &r)
1248
+ {
1249
+ f /= r;
1250
+ return f;
1251
+ }
1252
+
1253
+ /**
1254
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$, with
1255
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ r(0) / f(g,0) \f$
1256
+ * if \f$ 0 < length_r \f$ and to \f$ 1 / f(g,0) \f$ otherwise.
1257
+ *
1258
+ * Used conventions:
1259
+ * - \f$ a / 0 = NaN \f$,
1260
+ * - \f$ inf / inf = NaN \f$,
1261
+ * - \f$ -inf / inf = NaN \f$,
1262
+ * - \f$ inf / -inf = NaN \f$,
1263
+ * - \f$ -inf / -inf = NaN \f$,
1264
+ * - \f$ NaN / b = NaN \f$,
1265
+ * - \f$ a / NaN = NaN \f$,
1266
+ * - \f$ a / inf = 0 \f$,
1267
+ * - \f$ a / -inf = 0 \f$.
1268
+ *
1269
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1270
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1271
+ *
1272
+ * @tparam ValueRange Range with a begin() and end() method.
1273
+ * @param r First element of the division.
1274
+ * @param f Second element of the division.
1275
+ */
1276
+ template <class ValueRange,
1277
+ class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
1278
+ !std::is_same_v<ValueRange, Degree_rips_bifiltration> > >
1279
+ friend Degree_rips_bifiltration operator/(const ValueRange &r, Degree_rips_bifiltration f)
1280
+ {
1281
+ if (r.begin() == r.end()) return 1 / f;
1282
+ return *(r.begin()) / f;
1283
+ }
1284
+
1285
+ /**
1286
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) / val \f$.
1287
+ *
1288
+ * Used conventions:
1289
+ * - \f$ a / 0 = NaN \f$,
1290
+ * - \f$ inf / inf = NaN \f$,
1291
+ * - \f$ -inf / inf = NaN \f$,
1292
+ * - \f$ inf / -inf = NaN \f$,
1293
+ * - \f$ -inf / -inf = NaN \f$,
1294
+ * - \f$ NaN / b = NaN \f$,
1295
+ * - \f$ a / NaN = NaN \f$,
1296
+ * - \f$ a / inf = 0 \f$,
1297
+ * - \f$ a / -inf = 0 \f$.
1298
+ *
1299
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1300
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1301
+ *
1302
+ * @param f First element of the division.
1303
+ * @param val Second element of the division.
1304
+ */
1305
+ friend Degree_rips_bifiltration operator/(Degree_rips_bifiltration f, const T &val)
1306
+ {
1307
+ f /= val;
1308
+ return f;
1309
+ }
1310
+
1311
+ /**
1312
+ * @brief Returns a filtration value such that an entry at index \f$ (g,0) \f$ is equal to \f$ val / f(g,0) \f$.
1313
+ *
1314
+ * Used conventions:
1315
+ * - \f$ a / 0 = NaN \f$,
1316
+ * - \f$ inf / inf = NaN \f$,
1317
+ * - \f$ -inf / inf = NaN \f$,
1318
+ * - \f$ inf / -inf = NaN \f$,
1319
+ * - \f$ -inf / -inf = NaN \f$,
1320
+ * - \f$ NaN / b = NaN \f$,
1321
+ * - \f$ a / NaN = NaN \f$,
1322
+ * - \f$ a / inf = 0 \f$,
1323
+ * - \f$ a / -inf = 0 \f$.
1324
+ *
1325
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1326
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1327
+ *
1328
+ * @param val First element of the division.
1329
+ * @param f Second element of the division.
1330
+ */
1331
+ friend Degree_rips_bifiltration operator/(const T &val, Degree_rips_bifiltration f)
1332
+ {
1333
+ f._apply_operation(val, [](T &valF, const T &valR) {
1334
+ T tmp = valF;
1335
+ valF = valR;
1336
+ _divide(valF, tmp);
1337
+ });
1338
+ return f;
1339
+ }
1340
+
1341
+ /**
1342
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$, with
1343
+ * \f$ 0 \leq g \leq num_generators \f$ is equal to \f$ f(g,0) / r(0) \f$
1344
+ * if \f$ 0 < length_r \f$ and to \f$ f(g,0) \f$ otherwise.
1345
+ *
1346
+ * Used conventions:
1347
+ * - \f$ a / 0 = NaN \f$,
1348
+ * - \f$ inf / inf = NaN \f$,
1349
+ * - \f$ -inf / inf = NaN \f$,
1350
+ * - \f$ inf / -inf = NaN \f$,
1351
+ * - \f$ -inf / -inf = NaN \f$,
1352
+ * - \f$ NaN / b = NaN \f$,
1353
+ * - \f$ a / NaN = NaN \f$,
1354
+ * - \f$ a / inf = 0 \f$,
1355
+ * - \f$ a / -inf = 0 \f$.
1356
+ *
1357
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1358
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1359
+ *
1360
+ * @tparam ValueRange Range with a begin() and end() method.
1361
+ * @param f First element of the division.
1362
+ * @param r Second element of the division.
1363
+ */
1364
+ template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
1365
+ friend Degree_rips_bifiltration &operator/=(Degree_rips_bifiltration &f, const ValueRange &r)
1366
+ {
1367
+ if (r.begin() == r.end()) return f;
1368
+ f /= *(r.begin());
1369
+ return f;
1370
+ }
1371
+
1372
+ /**
1373
+ * @brief Modifies the first parameter such that an entry at index \f$ (g,0) \f$ is equal to \f$ f(g,0) / val \f$.
1374
+ *
1375
+ * Used conventions:
1376
+ * - \f$ a / 0 = NaN \f$,
1377
+ * - \f$ inf / inf = NaN \f$,
1378
+ * - \f$ -inf / inf = NaN \f$,
1379
+ * - \f$ inf / -inf = NaN \f$,
1380
+ * - \f$ -inf / -inf = NaN \f$,
1381
+ * - \f$ NaN / b = NaN \f$,
1382
+ * - \f$ a / NaN = NaN \f$,
1383
+ * - \f$ a / inf = 0 \f$,
1384
+ * - \f$ a / -inf = 0 \f$.
1385
+ *
1386
+ * All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
1387
+ * `std::numeric_limits<T>::has_quiet_NaN` is true or not.
1388
+ *
1389
+ * @param f First element of the division.
1390
+ * @param val Second element of the division.
1391
+ */
1392
+ friend Degree_rips_bifiltration &operator/=(Degree_rips_bifiltration &f, const T &val)
1393
+ {
1394
+ f._apply_operation(val, [](T &valF, const T &valR) { _divide(valF, valR); });
1395
+ return f;
1396
+ }
1397
+
1398
+ // MODIFIERS
1399
+
1400
+ /**
1401
+ * @brief Sets the number of generators. If there were less generators before, new generators with default values are
1402
+ * constructed. If there were more generators before, the exceed of generators is destroyed (any generator with index
1403
+ * higher or equal to @p g to be more precise). If @p g is zero, the methods does nothing.
1404
+ *
1405
+ * Fails to compile if `Ensure1Criticality` is true.
1406
+ *
1407
+ * @param g New number of generators.
1408
+ */
1409
+ void set_num_generators(size_type g)
1410
+ {
1411
+ static_assert(!Ensure1Criticality, "Number of generators cannot be set for a 1-critical only filtration value.");
1412
+
1413
+ if (g == 0) return;
1414
+ generators_.resize(g, _get_default_value());
1415
+ }
1416
+
1417
+ /**
1418
+ * @brief Adds the given generator to the filtration value.
1419
+ *
1420
+ * It is possible that the generator is ignored if the first parameter is overshadowed by an already existing
1421
+ * generator with same second parameter. This would mean that adding the given generator will not span more
1422
+ * "lifetime" and therefore there is no need to store it.
1423
+ *
1424
+ * Let \f$ max_idx \$f be the highest second parameter stored so far. If the given second parameter \f$ i \$f to add
1425
+ * is strictly higher than \f$ max_idx + 1 \$f, all possible values between \f$ max_idx \$f and \f$ i \$f will also
1426
+ * be added and the corresponding first parameters will be initialized with -inf if `Co` is false and with +inf
1427
+ * if `Co` is true.
1428
+ *
1429
+ * @tparam GeneratorRange Range of elements convertible to `T`. Must have a begin(), end() method and the iterator
1430
+ * type should satisfy the requirements of the standard `LegacyForwardIterator`.
1431
+ * @param x New generator to add. Has to have the 2 parameters.
1432
+ * @return true If and only if the generator is actually added to the set of generators.
1433
+ * @return false Otherwise.
1434
+ */
1435
+ template <class GeneratorRange = std::initializer_list<T>,
1436
+ class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
1437
+ bool add_generator(const GeneratorRange &x)
1438
+ {
1439
+ return add_generator(x.begin(), x.end());
1440
+ }
1441
+
1442
+ /**
1443
+ * @brief Adds the given generator to the filtration value.
1444
+ *
1445
+ * It is possible that the generator is ignored if the first parameter is overshadowed by an already existing
1446
+ * generator with same second parameter. This would mean that adding the given generator will not span more
1447
+ * "lifetime" and therefore there is no need to store it.
1448
+ *
1449
+ * Let \f$ max_idx \$f be the highest second parameter stored so far. If the given second parameter \f$ i \$f to add
1450
+ * is strictly higher than \f$ max_idx + 1 \$f, all possible values between \f$ max_idx \$f and \f$ i \$f will also
1451
+ * be added and the corresponding first parameters will be initialized with inf if `Co` is false and with -inf
1452
+ * if `Co` is true.
1453
+ *
1454
+ * @tparam Iterator Iterator class satisfying the requirements of the standard `LegacyForwardIterator`.
1455
+ * The dereferenced type has to be convertible to `T`.
1456
+ * @param genStart Iterator pointing to the begining of the range of two elements.
1457
+ * @param genEnd Iterator pointing to the end of the range.
1458
+ * @return true If and only if the generator is actually added to the set of generators.
1459
+ * @return false Otherwise.
1460
+ */
1461
+ template <class Iterator>
1462
+ bool add_generator(Iterator genStart, [[maybe_unused]] Iterator genEnd)
1463
+ {
1464
+ GUDHI_CHECK(std::distance(genStart, genEnd) == 2,
1465
+ std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
1466
+
1467
+ const T val = *genStart;
1468
+ ++genStart;
1469
+ const size_type index = *genStart;
1470
+
1471
+ GUDHI_CHECK(index >= 0, std::invalid_argument("Second parameter has to be a positive index."));
1472
+
1473
+ if (_is_nan(val)) return false;
1474
+
1475
+ if (index < generators_.size()) {
1476
+ if (_dominates(val, generators_[index])) return false;
1477
+ generators_[index] = val;
1478
+ return true;
1479
+ }
1480
+
1481
+ if constexpr (Ensure1Criticality) {
1482
+ if (index != 0) throw std::logic_error("Cannot add additional generator to a 1-critical only filtration value.");
1483
+ }
1484
+
1485
+ generators_.resize(index + 1, _get_default_minus_value());
1486
+ generators_[index] = val;
1487
+ return true;
1488
+ }
1489
+
1490
+ /**
1491
+ * @brief Same as @ref add_generator "".
1492
+ */
1493
+ template <class GeneratorRange = std::initializer_list<T>,
1494
+ class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
1495
+ void add_guaranteed_generator(const GeneratorRange &x)
1496
+ {
1497
+ add_generator(x.begin(), x.end());
1498
+ }
1499
+
1500
+ /**
1501
+ * @brief Does nothing, only for interface purposes.
1502
+ */
1503
+ void simplify() {}
1504
+
1505
+ /**
1506
+ * @brief Does nothing, only for interface purposes.
1507
+ */
1508
+ void remove_empty_generators([[maybe_unused]] bool include_infinities = false) {}
1509
+
1510
+ /**
1511
+ * @brief Let \f$ g_c \f$ be the positive cone generated by the generator \f$ g \f$ of the filtration value. And let
1512
+ * \f$ x_c \f$ be the positive cone generated by the given point \f$ x \f$. For each generator \f$ g \f$ of the
1513
+ * filtration value such that \f$ g_c \f$ strictly contains \f$ x_c \f$, pushes it to the border of \f$ x_c \f$.
1514
+ *
1515
+ * Say \f$ x = (val, idx) \f$. The second parameter \f$ idx \f$ is assumed to be positive.
1516
+ * Note also that, if `Co` is true, the filtration value cannot be pushed to \f$ (*,inf) \f$, and that,
1517
+ * if `Ensure1Criticality` is true, \f$ idx \f$ has to be \f$ 0 \f$.
1518
+ *
1519
+ * @tparam GeneratorRange Either a range of into `T` convertible elements with a begin(), end() and size() method,
1520
+ * or @ref Degree_rips_bifiltration<U,...> with `U` convertible into `T`.
1521
+ * @param x Range towards to push. Has to have 2 elements (all other elements above that count are ignored). If the
1522
+ * range is a @ref Degree_rips_bifiltration, the generator at second parameter 0 is chosen.
1523
+ * @param exclude_infinite_values If true, values at infinity or minus infinity are not affected.
1524
+ * @return true If the filtration value was actually modified.
1525
+ * @return false Otherwise.
1526
+ */
1527
+ template <class GeneratorRange = std::initializer_list<value_type>,
1528
+ class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
1529
+ bool push_to_least_common_upper_bound(const GeneratorRange &x, bool exclude_infinite_values = false)
1530
+ {
1531
+ if (is_nan()) return false;
1532
+
1533
+ size_type start = 0;
1534
+ T startVal = 0;
1535
+ T xVal;
1536
+
1537
+ if constexpr (RangeTraits<GeneratorRange>::is_multi_filtration) {
1538
+ if (x.size() == 0) return false; // nan
1539
+ xVal = x(0,0);
1540
+ } else {
1541
+ GUDHI_CHECK(x.size() == 2,
1542
+ std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
1543
+
1544
+ auto it = x.begin();
1545
+ xVal = *it;
1546
+ ++it;
1547
+ startVal = *it;
1548
+ if (startVal < 0) throw std::invalid_argument("Second parameter has to be a positive integer.");
1549
+ }
1550
+
1551
+ if (startVal == T_inf) {
1552
+ if constexpr (Co) {
1553
+ throw std::invalid_argument("Filtration values with negative cones cannot be pushed to (*,inf).");
1554
+ }
1555
+ if (is_plus_inf()) return false;
1556
+ // not exactly true, but the closest feasible representation
1557
+ *this = inf();
1558
+ return true;
1559
+ }
1560
+ start = startVal;
1561
+ if ((start == 0 && xVal == T_m_inf) || _is_nan(xVal)) return false;
1562
+
1563
+ if constexpr (Ensure1Criticality) {
1564
+ // TODO: we could at some point allow [(inf,0),...,(inf,n-1),(v,n)] as 1-critical, but it would make everything
1565
+ // more complicated and this class does not seem to be the right option to handle those cases anyway...
1566
+ if (start != 0)
1567
+ throw std::invalid_argument(
1568
+ "Pushing to an index other than 0 or inf is not permitted with Ensure1Criticality at true.");
1569
+ }
1570
+
1571
+ bool modified = false;
1572
+ T newVal = _get_default_minus_value();
1573
+
1574
+ for (size_type i = 0; i < std::min(start, generators_.size()); ++i){
1575
+ auto& v = generators_[i];
1576
+ if (!_is_nan(v) && (!exclude_infinite_values || (v != T_inf && v != T_m_inf))){
1577
+ T threshold = std::max(v, xVal);
1578
+ if (_dominates(newVal, threshold)) newVal = threshold;
1579
+ if (v != _get_default_minus_value()) {
1580
+ modified = true;
1581
+ v = _get_default_minus_value();
1582
+ }
1583
+ }
1584
+ }
1585
+
1586
+ if (start < generators_.size()){
1587
+ auto& vs = generators_[start];
1588
+ T threshold = std::max(vs, xVal);
1589
+ if (_dominates(newVal, threshold)) newVal = threshold;
1590
+ if (newVal != vs) {
1591
+ modified = true;
1592
+ vs = newVal;
1593
+ }
1594
+
1595
+ for (size_type i = start + 1; i < generators_.size(); ++i) {
1596
+ auto& v = generators_[i];
1597
+ if (!_is_nan(v) && v < xVal && (!exclude_infinite_values || (v != T_inf && v != T_m_inf))) {
1598
+ modified = true;
1599
+ v = xVal;
1600
+ }
1601
+ }
1602
+ } else {
1603
+ modified = true;
1604
+ generators_.resize(start + 1, _get_default_minus_value());
1605
+ generators_[start] = newVal;
1606
+ }
1607
+
1608
+ if (is_plus_inf()) *this = inf();
1609
+
1610
+ return modified;
1611
+ }
1612
+
1613
+ /**
1614
+ * @brief Let \f$ g_c \f$ be the negative cone generated by the generator \f$ g \f$ of the filtration value. And let
1615
+ * \f$ x_c \f$ be the negative cone generated by the given point \f$ x \f$. For each generator \f$ g \f$ of the
1616
+ * filtration value such that \f$ g_c \f$ strictly contains \f$ x_c \f$, pulls it to the border of \f$ x_c \f$.
1617
+ *
1618
+ * Say \f$ x = (val, idx) \f$. The second parameter \f$ idx \f$ is assumed to be positive.
1619
+ *
1620
+ * @tparam GeneratorRange Either a range of into `T` convertible elements with a begin(), end() and size() method,
1621
+ * or @ref Degree_rips_bifiltration<U,...> with `U` convertible into `T`.
1622
+ * @param x Range towards to push. Has to have 2 elements (all other elements above that count are ignored). If the
1623
+ * range is a @ref Degree_rips_bifiltration, the generator at second parameter 0 is chosen.
1624
+ * @param exclude_infinite_values If true, values at infinity or minus infinity are not affected.
1625
+ * @return true If the filtration value was actually modified.
1626
+ * @return false Otherwise.
1627
+ */
1628
+ template <class GeneratorRange = std::initializer_list<value_type>,
1629
+ class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
1630
+ bool pull_to_greatest_common_lower_bound(const GeneratorRange &x, bool exclude_infinite_values = false)
1631
+ {
1632
+ if (is_nan()) return false;
1633
+
1634
+ size_type end;
1635
+ T endVal = 0;
1636
+ T xVal;
1637
+
1638
+ if constexpr (RangeTraits<GeneratorRange>::is_multi_filtration) {
1639
+ if (x.size() == 0) return false; // nan
1640
+ xVal = x(0,0);
1641
+ } else {
1642
+ GUDHI_CHECK(x.size() == 2,
1643
+ std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
1644
+
1645
+ auto it = x.begin();
1646
+ xVal = *it;
1647
+ ++it;
1648
+ endVal = *it;
1649
+ }
1650
+
1651
+ if (endVal < 0) throw std::invalid_argument("Second parameter has to be a positive integer.");
1652
+ if (endVal == T_inf) end = generators_.size();
1653
+ else end = endVal;
1654
+
1655
+ if ((end >= generators_.size() - 1 && xVal == T_inf) || _is_nan(xVal)) return false;
1656
+
1657
+ bool modified = false;
1658
+ T newVal = _get_default_minus_value();
1659
+
1660
+ for (size_type i = 0; i < std::min(end, generators_.size()); ++i) {
1661
+ auto &v = generators_[i];
1662
+ if (!_is_nan(v) && v > xVal && (!exclude_infinite_values || (v != T_inf && v != T_m_inf))) {
1663
+ modified = true;
1664
+ v = xVal;
1665
+ }
1666
+ }
1667
+
1668
+ if (end < generators_.size()) {
1669
+ for (size_type i = end; i < generators_.size(); ++i) {
1670
+ auto v = generators_[i];
1671
+ if (!_is_nan(v) && (!exclude_infinite_values || (v != T_inf && v != T_m_inf))){
1672
+ T threshold = std::min(v, xVal);
1673
+ if (_dominates(newVal, threshold)) newVal = threshold;
1674
+ }
1675
+ }
1676
+
1677
+ modified |= (end + 1) != generators_.size();
1678
+ generators_.resize(end + 1);
1679
+
1680
+ modified |= generators_[end] != newVal;
1681
+ generators_[end] = newVal;
1682
+ }
1683
+
1684
+ return modified;
1685
+ }
1686
+
1687
+ /**
1688
+ * @brief Projects the filtration value into the given grid. If @p coordinate is false, the entries are set to
1689
+ * the nearest upper bound value with the same parameter in the grid. Otherwise, the entries are set to the indices
1690
+ * of those nearest upper bound values.
1691
+ * The grid has to be represented as a vector of ordered ranges of values convertible into `T`. An index
1692
+ * \f$ i \f$ of the vector corresponds to the same parameter as the index \f$ i \f$ in a generator of the filtration
1693
+ * value. The ranges correspond to the possible values of the parameters, ordered by increasing value, forming
1694
+ * therefore all together a 2D grid. The range of the second parameter has to start at 0 and continue continuously.
1695
+ *
1696
+ * @tparam OneDimArray A range of values convertible into `T` ordered by increasing value. Has to implement
1697
+ * a begin, end and operator[] method.
1698
+ * @param grid Vector of @p OneDimArray with size at least 2.
1699
+ * @param coordinate If true, the values are set to the coordinates of the projection in the grid. If false,
1700
+ * the values are set to the values at the coordinates of the projection.
1701
+ */
1702
+ template <typename OneDimArray>
1703
+ void project_onto_grid(const std::vector<OneDimArray> &grid, bool coordinate = true)
1704
+ {
1705
+ GUDHI_CHECK(
1706
+ grid.size() >= 2,
1707
+ std::invalid_argument("The grid should not be smaller than the number of parameters in the filtration value."));
1708
+
1709
+ GUDHI_CHECK_code(const OneDimArray &indices = grid[1]);
1710
+ const OneDimArray &values = grid[0];
1711
+ auto todo = [&](size_type g) {
1712
+ GUDHI_CHECK_code(GUDHI_CHECK(static_cast<size_type>(indices[g]) == g, std::invalid_argument("Unvalid grid.")));
1713
+
1714
+ auto v = static_cast<typename OneDimArray::value_type>(generators_[g]);
1715
+ auto d = std::distance(values.begin(), std::lower_bound(values.begin(), values.end(), v));
1716
+ if (d != 0 && std::abs(v - values[d]) > std::abs(v - values[d - 1])) {
1717
+ --d;
1718
+ }
1719
+ generators_[g] = coordinate ? static_cast<T>(d) : static_cast<T>(values[d]);
1720
+ };
1721
+ #ifdef GUDHI_USE_TBB
1722
+ tbb::parallel_for(size_type{0}, num_generators(), [&](size_type g) { todo(g); });
1723
+ #else
1724
+ for (size_type g = 0; g < num_generators(); ++g) {
1725
+ todo(g);
1726
+ }
1727
+ #endif
1728
+ }
1729
+
1730
+ // FONCTIONNALITIES
1731
+
1732
+ /**
1733
+ * @brief Returns the filtration value that is the greatest lower bound of all generators.
1734
+ */
1735
+ friend Degree_rips_bifiltration factorize_below(const Degree_rips_bifiltration &f)
1736
+ {
1737
+ if (f.num_generators() <= 1) return f;
1738
+
1739
+ bool nan = true;
1740
+ size_type idx = f.num_generators();
1741
+ T val = T_inf;
1742
+
1743
+ if constexpr (Co) {
1744
+ // -inf are "non existing" generators if not all of them are at -inf
1745
+ bool inf = true;
1746
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1747
+ T v = f.generators_[g];
1748
+ if (!_is_nan(v) && v != T_m_inf) {
1749
+ nan = false;
1750
+ inf = false;
1751
+ val = v < val ? v : val;
1752
+ idx = g < idx ? g : idx;
1753
+ } else if (_is_nan(v)) {
1754
+ inf = false;
1755
+ } else {
1756
+ nan = false;
1757
+ }
1758
+ }
1759
+ if (inf) return Degree_rips_bifiltration::minus_inf();
1760
+ } else {
1761
+ idx = 0;
1762
+ for (const T &v : f.generators_) {
1763
+ if (!_is_nan(v)) {
1764
+ nan = false;
1765
+ val = v < val ? v : val;
1766
+ }
1767
+ }
1768
+ }
1769
+
1770
+ if (nan) return Degree_rips_bifiltration::nan();
1771
+
1772
+ Underlying_container result(idx + 1, _get_default_minus_value());
1773
+ result[idx] = val;
1774
+ return Degree_rips_bifiltration(std::move(result), 2);
1775
+ }
1776
+
1777
+ /**
1778
+ * @brief Returns the filtration value that is the least upper bound of all generators.
1779
+ */
1780
+ friend Degree_rips_bifiltration factorize_above(const Degree_rips_bifiltration &f)
1781
+ {
1782
+ if (f.num_generators() <= 1) return f;
1783
+
1784
+ bool nan = true;
1785
+ size_type idx = 0;
1786
+ T val = T_m_inf;
1787
+
1788
+ if constexpr (Co) {
1789
+ idx = f.num_generators() - 1;
1790
+ for (const T &v : f.generators_) {
1791
+ if (!_is_nan(v)) {
1792
+ nan = false;
1793
+ val = v > val ? v : val;
1794
+ }
1795
+ }
1796
+ } else {
1797
+ // inf are "non existing" generators if not all of them are at inf
1798
+ bool inf = true;
1799
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1800
+ T v = f.generators_[g];
1801
+ if (!_is_nan(v) && v != T_inf) {
1802
+ nan = false;
1803
+ inf = false;
1804
+ val = v > val ? v : val;
1805
+ idx = g > idx ? g : idx;
1806
+ } else if (_is_nan(v)) {
1807
+ inf = false;
1808
+ } else {
1809
+ nan = false;
1810
+ }
1811
+ }
1812
+ if (inf) return Degree_rips_bifiltration::inf();
1813
+ }
1814
+
1815
+ if (nan) return Degree_rips_bifiltration::nan();
1816
+
1817
+ Underlying_container result(idx + 1, _get_default_minus_value());
1818
+ result[idx] = val;
1819
+ return Degree_rips_bifiltration(std::move(result), 2);
1820
+ }
1821
+
1822
+ /**
1823
+ * @brief Computes the smallest (resp. the greatest if `Co` is true) scalar product of the all generators with the
1824
+ * given vector.
1825
+ *
1826
+ * @tparam U Arithmetic type of the result. Default value: `T`.
1827
+ * @param f Filtration value.
1828
+ * @param x Vector of coefficients.
1829
+ * @return Scalar product of @p f with @p x.
1830
+ */
1831
+ template <typename U = T>
1832
+ friend U compute_linear_projection(const Degree_rips_bifiltration &f, const std::vector<U> &x)
1833
+ {
1834
+ auto project_generator = [&](size_type g) -> U {
1835
+ U projection = 0;
1836
+ std::size_t size = std::min(x.size(), Degree_rips_bifiltration::num_parameters());
1837
+ for (std::size_t i = 0; i < size; i++) projection += x[i] * static_cast<U>(f(g, i));
1838
+ return projection;
1839
+ };
1840
+
1841
+ if (f.num_generators() == 1) return project_generator(0);
1842
+
1843
+ #ifdef GUDHI_USE_TBB
1844
+ std::vector<U> projections(f.num_generators());
1845
+ tbb::parallel_for(size_type{0}, f.num_generators(), [&](size_type g) {
1846
+ projections[g] = project_generator(g);
1847
+ });
1848
+ if constexpr (Co) {
1849
+ return *std::max_element(projections.begin(), projections.end());
1850
+ } else {
1851
+ return *std::min_element(projections.begin(), projections.end());
1852
+ }
1853
+ #else
1854
+ if constexpr (Co) {
1855
+ U projection = std::numeric_limits<U>::lowest();
1856
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1857
+ // Order in the max important to spread possible NaNs
1858
+ projection = std::max(project_generator(g), projection);
1859
+ }
1860
+ return projection;
1861
+ } else {
1862
+ U projection = std::numeric_limits<U>::max();
1863
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1864
+ // Order in the min important to spread possible NaNs
1865
+ projection = std::min(project_generator(g), projection);
1866
+ }
1867
+ return projection;
1868
+ }
1869
+ #endif
1870
+ }
1871
+
1872
+ /**
1873
+ * @brief Computes the euclidean distance from the first parameter to the second parameter as the minimum of
1874
+ * all Euclidean distances between a generator of @p f and a generator of @p other.
1875
+ *
1876
+ * @param f Source filtration value.
1877
+ * @param other Target filtration value.
1878
+ * @return Euclidean distance between @p f and @p other.
1879
+ */
1880
+ template <typename U = T>
1881
+ friend U compute_euclidean_distance_to(const Degree_rips_bifiltration &f, const Degree_rips_bifiltration &other)
1882
+ {
1883
+ // TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
1884
+ if constexpr (Ensure1Criticality) {
1885
+ return _compute_frobenius_norm(Degree_rips_bifiltration::num_parameters(),
1886
+ [&](size_type p) -> T { return f(0, p) - other(0, p); });
1887
+ } else {
1888
+ U res = std::numeric_limits<U>::max();
1889
+ for (size_type g1 = 0; g1 < f.num_generators(); ++g1) {
1890
+ for (size_type g2 = 0; g2 < other.num_generators(); ++g2) {
1891
+ // Euclidean distance as a Frobenius norm with matrix 1 x p and values 'f(g1, p) - other(g2, p)'
1892
+ // Order in the min important to spread possible NaNs
1893
+ res = std::min(_compute_frobenius_norm(Degree_rips_bifiltration::num_parameters(),
1894
+ [&](size_type p) -> T { return f(g1, p) - other(g2, p); }),
1895
+ res);
1896
+ }
1897
+ }
1898
+ return res;
1899
+ }
1900
+ }
1901
+
1902
+ /**
1903
+ * @brief Computes the norm of the given filtration value.
1904
+ *
1905
+ * The filtration value is seen as a \f$ num_generators x num_parameters \f$ matrix and a standard Frobenius norm
1906
+ * is computed from it: the square root of the sum of the squares of all elements in the matrix.
1907
+ *
1908
+ * @param f Filtration value.
1909
+ * @return The norm of @p f.
1910
+ */
1911
+ template <typename U = T>
1912
+ friend U compute_norm(const Degree_rips_bifiltration &f)
1913
+ {
1914
+ // Frobenius norm with matrix g x p based on Euclidean norm
1915
+
1916
+ if (f.num_generators() == 1) return f.generators_[0];
1917
+
1918
+ U out = 0;
1919
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1920
+ out += g * g;
1921
+ out += f.generators_[g] * f.generators_[g];
1922
+ }
1923
+
1924
+ if constexpr (std::is_integral_v<U>) {
1925
+ // to avoid Windows issue that don't know how to cast integers for cmath methods
1926
+ return std::sqrt(static_cast<double>(out));
1927
+ } else {
1928
+ return std::sqrt(out);
1929
+ }
1930
+ }
1931
+
1932
+ /**
1933
+ * @brief Computes the coordinates in the given grid, corresponding to the nearest upper bounds of the entries
1934
+ * in the given filtration value.
1935
+ * The grid has to be represented as a vector of vectors of ordered values convertible into `OutValue`. An index
1936
+ * \f$ i \f$ of the vector corresponds to the same parameter as the index \f$ i \f$ in a generator of the filtration
1937
+ * value. The ranges correspond to the possible values of the parameters, ordered by increasing value, forming
1938
+ * therefore all together a 2D grid. The range of the second parameter has to start at 0 and continue continuously.
1939
+ *
1940
+ * @tparam OutValue Signed arithmetic type. Default value: std::int32_t.
1941
+ * @tparam U Type which is convertible into `OutValue`.
1942
+ * @param f Filtration value to project.
1943
+ * @param grid Vector of vectors to project into.
1944
+ * @return Filtration value \f$ out \f$ whose entry correspond to the indices of the projected values. That is,
1945
+ * the projection of \f$ f(g,p) \f$ is \f$ grid[p][out(g,p)] \f$.
1946
+ */
1947
+ template <typename OutValue = std::int32_t, typename U = T>
1948
+ friend Degree_rips_bifiltration<OutValue, Co, Ensure1Criticality> compute_coordinates_in_grid(
1949
+ Degree_rips_bifiltration f,
1950
+ const std::vector<std::vector<U> > &grid)
1951
+ {
1952
+ // TODO: by replicating the code of "project_onto_grid", this could be done with just one copy
1953
+ // instead of two. But it is not clear if it is really worth it, i.e., how much the change in type is really
1954
+ // necessary in the use cases. To see later.
1955
+ f.project_onto_grid(grid);
1956
+ if constexpr (std::is_same_v<OutValue, T>) {
1957
+ return f;
1958
+ } else {
1959
+ return f.as_type<OutValue>();
1960
+ }
1961
+ }
1962
+
1963
+ /**
1964
+ * @brief Computes the values in the given grid corresponding to the coordinates given by the given filtration
1965
+ * value. That is, if \f$ out \f$ is the result, \f$ out(g,p) = grid[p][f(g,p)] \f$. Assumes therefore, that the
1966
+ * values stored in the filtration value corresponds to indices existing in the given grid.
1967
+ *
1968
+ * @tparam U Signed arithmetic type.
1969
+ * @param f Filtration value storing coordinates compatible with `grid`.
1970
+ * @param grid Vector of vector.
1971
+ * @return Filtration value \f$ out \f$ whose entry correspond to \f$ out(g,p) = grid[p][f(g,p)] \f$.
1972
+ */
1973
+ template <typename U>
1974
+ friend Degree_rips_bifiltration<U, Co, Ensure1Criticality> evaluate_coordinates_in_grid(
1975
+ const Degree_rips_bifiltration &f,
1976
+ const std::vector<std::vector<U> > &grid)
1977
+ {
1978
+ GUDHI_CHECK(grid.size() >= f.num_parameters(),
1979
+ std::invalid_argument(
1980
+ "The size of the grid should correspond to the number of parameters in the filtration value."));
1981
+
1982
+ U grid_inf = Degree_rips_bifiltration<U, Co, Ensure1Criticality>::T_inf;
1983
+ std::vector<U> outVec(f.num_generators());
1984
+
1985
+ GUDHI_CHECK_code(const std::vector<U> &indices = grid[1]);
1986
+ const std::vector<U> &values = grid[0];
1987
+ for (size_type g = 0; g < f.num_generators(); ++g) {
1988
+ GUDHI_CHECK_code(GUDHI_CHECK(static_cast<size_type>(indices[g]) == g, std::invalid_argument("Unvalid grid.")));
1989
+
1990
+ const T &c = f.generators_[g];
1991
+ outVec[g] = (c == T_inf ? grid_inf : values[c]);
1992
+ }
1993
+
1994
+ return Degree_rips_bifiltration<U, Co, Ensure1Criticality>(std::move(outVec),
1995
+ Degree_rips_bifiltration::num_parameters());
1996
+ }
1997
+
1998
+ // UTILITIES
1999
+
2000
+ /**
2001
+ * @brief Outstream operator.
2002
+ */
2003
+ friend std::ostream &operator<<(std::ostream &stream, const Degree_rips_bifiltration &f)
2004
+ {
2005
+ const size_type num_gen = f.num_generators();
2006
+ const size_type num_param = Degree_rips_bifiltration::num_parameters();
2007
+
2008
+ stream << "( k = " << num_gen << " ) [ ";
2009
+ for (size_type g = 0; g < num_gen; ++g) {
2010
+ stream << "[";
2011
+ for (size_type p = 0; p < num_param; ++p) {
2012
+ stream << f(g, p);
2013
+ if (p < num_param - 1) stream << ", ";
2014
+ }
2015
+ stream << "]";
2016
+ if (g < num_gen - 1) stream << "; ";
2017
+ }
2018
+ stream << " ]";
2019
+
2020
+ return stream;
2021
+ }
2022
+
2023
+ /**
2024
+ * @brief Instream operator.
2025
+ */
2026
+ friend std::istream &operator>>(std::istream &stream, Degree_rips_bifiltration &f)
2027
+ {
2028
+ size_type num_gen;
2029
+ char delimiter;
2030
+ stream >> delimiter; // (
2031
+ stream >> delimiter; // k
2032
+ stream >> delimiter; // =
2033
+ if (delimiter != '=') throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2034
+ stream >> num_gen;
2035
+ if (!stream.good()) throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2036
+ f.generators_.resize(num_gen);
2037
+ stream >> delimiter; // )
2038
+ stream >> delimiter; // [
2039
+ if (delimiter != '[') throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2040
+ if (num_gen == 0) return stream;
2041
+ T val;
2042
+ for (size_type i = 0; i < num_gen; ++i) {
2043
+ stream >> delimiter; // [
2044
+ val = _get_value<T>(stream);
2045
+ f.generators_[i] = val;
2046
+ stream >> delimiter; // ,
2047
+ stream >> val;
2048
+ if (val != static_cast<T>(i))
2049
+ throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2050
+ if (!stream.good()) throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2051
+ stream >> delimiter; // ]
2052
+ stream >> delimiter; // ; or last ]
2053
+ }
2054
+ if (delimiter != ']') throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_generator.");
2055
+
2056
+ return stream;
2057
+ }
2058
+
2059
+ /**
2060
+ * @brief Returns true if and only if the given filtration value is at plus infinity.
2061
+ */
2062
+ friend bool is_positive_infinity(const Degree_rips_bifiltration &f)
2063
+ {
2064
+ return f.is_plus_inf();
2065
+ }
2066
+
2067
+ /**
2068
+ * @brief Adds the generators of the second argument to the first argument.
2069
+ *
2070
+ * @param f1 Filtration value to modify.
2071
+ * @param f2 Filtration value to merge with the first one. Should have the same number of parameters than the other.
2072
+ * @return true If the first argument was actually modified.
2073
+ * @return false Otherwise.
2074
+ */
2075
+ friend bool unify_lifetimes(Degree_rips_bifiltration &f1, const Degree_rips_bifiltration &f2)
2076
+ {
2077
+ bool modified = false;
2078
+ for (size_type g = 0; g < f2.num_generators(); ++g) {
2079
+ modified |= f1.add_generator({f2.generators_[g], static_cast<T>(g)});
2080
+ }
2081
+ return modified;
2082
+ }
2083
+
2084
+ /**
2085
+ * @brief Stores in the first argument the origins of the cones in the intersection of the positive
2086
+ * (negative if `Co` is true) cones generated by the two arguments.
2087
+ *
2088
+ * @param f1 First set of cones which will be modified.
2089
+ * @param f2 Second set of cones. Should have the same number of parameters than the first one.
2090
+ * @return true If the first argument was actually modified.
2091
+ * @return false Otherwise.
2092
+ */
2093
+ friend bool intersect_lifetimes(Degree_rips_bifiltration &f1, const Degree_rips_bifiltration &f2)
2094
+ {
2095
+ if (f2.num_generators() == 0 || f1.num_generators() == 0) {
2096
+ Degree_rips_bifiltration res(2, _get_default_minus_value());
2097
+ swap(f1, res);
2098
+ return f1 != res;
2099
+ }
2100
+
2101
+ bool modified = false;
2102
+ T threshold1 = f1.generators_[0];
2103
+ T threshold2 = f2.generators_[0];
2104
+ for (size_type g = 0; g < std::max(f1.num_generators(), f2.num_generators()); ++g) {
2105
+ if (g < f1.num_generators())
2106
+ threshold1 = _strictly_dominates(threshold1, f1.generators_[g]) ? f1.generators_[g] : threshold1;
2107
+ else {
2108
+ f1.generators_.push_back(0);
2109
+ modified = true;
2110
+ }
2111
+ if (g < f2.num_generators())
2112
+ threshold2 = _strictly_dominates(threshold2, f2.generators_[g]) ? f2.generators_[g] : threshold2;
2113
+ if (_strictly_dominates(threshold2, threshold1)) {
2114
+ if (f1.generators_[g] != threshold2) modified = true;
2115
+ f1.generators_[g] = threshold2;
2116
+ } else {
2117
+ if (f1.generators_[g] != threshold1) modified = true;
2118
+ f1.generators_[g] = threshold1;
2119
+ }
2120
+ }
2121
+
2122
+ return modified;
2123
+ }
2124
+
2125
+ /**
2126
+ * @brief Serialize given value into the buffer at given pointer.
2127
+ *
2128
+ * @param value Value to serialize.
2129
+ * @param start Pointer to the start of the space in the buffer where to store the serialization.
2130
+ * @return End position of the serialization in the buffer.
2131
+ */
2132
+ friend char *serialize_value_to_char_buffer(const Degree_rips_bifiltration &value, char *start)
2133
+ {
2134
+ const size_type length = value.generators_.size();
2135
+ const std::size_t arg_size = sizeof(T) * length;
2136
+ const std::size_t type_size = sizeof(size_type);
2137
+ memcpy(start, &length, type_size);
2138
+ memcpy(start + type_size, value.generators_.data(), arg_size);
2139
+ return start + arg_size + type_size;
2140
+ }
2141
+
2142
+ /**
2143
+ * @brief Deserialize the value from a buffer at given pointer and stores it in given value.
2144
+ *
2145
+ * @param value Value to fill with the deserialized filtration value.
2146
+ * @param start Pointer to the start of the space in the buffer where the serialization is stored.
2147
+ * @return End position of the serialization in the buffer.
2148
+ */
2149
+ friend const char *deserialize_value_from_char_buffer(Degree_rips_bifiltration &value, const char *start)
2150
+ {
2151
+ const std::size_t type_size = sizeof(size_type);
2152
+ size_type length;
2153
+ memcpy(&length, start, type_size);
2154
+ std::size_t arg_size = sizeof(T) * length;
2155
+ value.generators_.resize(length);
2156
+ memcpy(value.generators_.data(), start + type_size, arg_size);
2157
+ return start + arg_size + type_size;
2158
+ }
2159
+
2160
+ /**
2161
+ * @brief Returns the serialization size of the given filtration value.
2162
+ */
2163
+ friend std::size_t get_serialization_size_of(const Degree_rips_bifiltration &value)
2164
+ {
2165
+ return sizeof(size_type) + (sizeof(T) * value.generators_.size());
2166
+ }
2167
+
2168
+ /**
2169
+ * @brief Plus infinity value of an entry of the filtration value.
2170
+ */
2171
+ constexpr static const T T_inf = MF_T_inf<T>;
2172
+
2173
+ /**
2174
+ * @brief Minus infinity value of an entry of the filtration value.
2175
+ */
2176
+ constexpr static const T T_m_inf = MF_T_m_inf<T>;
2177
+
2178
+ private:
2179
+ Underlying_container generators_; /**< Container of the filtration value elements. */
2180
+ mutable T dummy_g_; /**< Dummy value for the first parameter, such that a reference can be returned. */
2181
+
2182
+ /**
2183
+ * @brief Default value of an element in the filtration value.
2184
+ */
2185
+ constexpr static T _get_default_value() { return Co ? T_inf : T_m_inf; }
2186
+ constexpr static T _get_default_minus_value() { return Co ? T_m_inf : T_inf; }
2187
+
2188
+ // <
2189
+ static bool _compare_strict(size_type i, const Underlying_container &a, const Underlying_container &b, T threshold)
2190
+ {
2191
+ if (i >= b.size()) return true;
2192
+ if (_is_nan(b[i])) return false;
2193
+
2194
+ if (i >= a.size() && _strictly_dominates(threshold, b[i])) return false;
2195
+ if (i < a.size()) {
2196
+ if (_is_nan(a[i])) return false;
2197
+ threshold = _strictly_dominates(threshold, a[i]) ? a[i] : threshold;
2198
+ if (a[i] == threshold && b[i] == threshold) return false;
2199
+ }
2200
+ if (_strictly_dominates(threshold, b[i])) return false;
2201
+
2202
+ return _compare_strict(i + 1, a, b, threshold);
2203
+ }
2204
+
2205
+ // <=
2206
+ static bool _compare(size_type i, const Underlying_container &a, const Underlying_container &b, T threshold)
2207
+ {
2208
+ if (i >= b.size()) return true;
2209
+ if (_is_nan(b[i])) return false;
2210
+
2211
+ if (i >= a.size() && _strictly_dominates(threshold, b[i])) return false;
2212
+ if (i < a.size()) {
2213
+ if (_is_nan(a[i])) return false;
2214
+ threshold = _strictly_dominates(threshold, a[i]) ? a[i] : threshold;
2215
+ }
2216
+ if (_strictly_dominates(threshold, b[i])) return false;
2217
+
2218
+ return _compare(i + 1, a, b, threshold);
2219
+ }
2220
+
2221
+ static bool _strictly_dominates(T a, T b)
2222
+ {
2223
+ if constexpr (Co) {
2224
+ return a < b;
2225
+ } else {
2226
+ return a > b;
2227
+ }
2228
+ }
2229
+
2230
+ static bool _dominates(T a, T b)
2231
+ {
2232
+ if constexpr (Co) {
2233
+ return a <= b;
2234
+ } else {
2235
+ return a >= b;
2236
+ }
2237
+ }
2238
+
2239
+ /**
2240
+ * @brief Applies operation on the elements of the filtration value.
2241
+ */
2242
+ template <class F>
2243
+ void _apply_operation(const T &val, F &&operate)
2244
+ {
2245
+ auto &gens = generators_;
2246
+ for (unsigned int i = 0; i < gens.size(); ++i) {
2247
+ std::forward<F>(operate)(gens[i], val);
2248
+ }
2249
+ }
2250
+
2251
+ template <class F, typename U = T>
2252
+ static U _compute_frobenius_norm(size_type number_of_elements, F &&norm)
2253
+ {
2254
+ if (number_of_elements == 1) return norm(0);
2255
+
2256
+ U out = 0;
2257
+ for (size_type p = 0; p < number_of_elements; ++p) {
2258
+ T v = std::forward<F>(norm)(p);
2259
+ out += v * v;
2260
+ }
2261
+ if constexpr (std::is_integral_v<U>) {
2262
+ // to avoid Windows issue that don't know how to cast integers for cmath methods
2263
+ return std::sqrt(static_cast<double>(out));
2264
+ } else {
2265
+ return std::sqrt(out);
2266
+ }
2267
+ }
2268
+ };
2269
+
2270
+ } // namespace Gudhi::multi_filtration
2271
+
2272
+ namespace std {
2273
+
2274
+ template <typename T, bool Co, bool Ensure1Criticality>
2275
+ class numeric_limits<Gudhi::multi_filtration::Degree_rips_bifiltration<T, Co, Ensure1Criticality> >
2276
+ {
2277
+ public:
2278
+ using Filtration_value = Gudhi::multi_filtration::Degree_rips_bifiltration<T, Co, Ensure1Criticality>;
2279
+
2280
+ static constexpr bool has_infinity = !Co;
2281
+ static constexpr bool has_quiet_NaN = true;
2282
+
2283
+ static constexpr Filtration_value infinity(std::size_t p = 2) noexcept(!Co) { return Filtration_value::inf(p); };
2284
+
2285
+ // non-standard
2286
+ static constexpr Filtration_value minus_infinity(std::size_t p = 2) noexcept
2287
+ {
2288
+ return Filtration_value::minus_inf(p);
2289
+ };
2290
+
2291
+ static constexpr Filtration_value max(std::size_t p = 2) noexcept(!Co)
2292
+ {
2293
+ if constexpr (Co) {
2294
+ throw std::logic_error("No biggest value possible for Co-filtrations yet.");
2295
+ } else {
2296
+ return Filtration_value(p, std::numeric_limits<T>::max());
2297
+ }
2298
+ };
2299
+
2300
+ static constexpr Filtration_value lowest(std::size_t p = 2) noexcept { return Filtration_value::minus_inf(p); };
2301
+
2302
+ static constexpr Filtration_value quiet_NaN(std::size_t p = 2) noexcept { return Filtration_value::nan(p); };
2303
+ };
2304
+
2305
+ } // namespace std
2306
+
2307
+ #endif // MF_DEGREE_RIPS_BIFILTRATION_H_