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.
- multipers/.dylibs/libboost_timer.dylib +0 -0
- multipers/.dylibs/libc++.1.0.dylib +0 -0
- multipers/.dylibs/libtbb.12.17.dylib +0 -0
- multipers/__init__.py +33 -0
- multipers/_signed_measure_meta.py +426 -0
- multipers/_slicer_meta.py +231 -0
- multipers/array_api/__init__.py +62 -0
- multipers/array_api/numpy.py +124 -0
- multipers/array_api/torch.py +133 -0
- multipers/data/MOL2.py +458 -0
- multipers/data/UCR.py +18 -0
- multipers/data/__init__.py +1 -0
- multipers/data/graphs.py +466 -0
- multipers/data/immuno_regions.py +27 -0
- multipers/data/minimal_presentation_to_st_bf.py +0 -0
- multipers/data/pytorch2simplextree.py +91 -0
- multipers/data/shape3d.py +101 -0
- multipers/data/synthetic.py +113 -0
- multipers/distances.py +202 -0
- multipers/filtration_conversions.pxd +736 -0
- multipers/filtration_conversions.pxd.tp +226 -0
- multipers/filtrations/__init__.py +21 -0
- multipers/filtrations/density.py +529 -0
- multipers/filtrations/filtrations.py +480 -0
- multipers/filtrations.pxd +534 -0
- multipers/filtrations.pxd.tp +332 -0
- multipers/function_rips.cpython-312-darwin.so +0 -0
- multipers/function_rips.pyx +104 -0
- multipers/grids.cpython-312-darwin.so +0 -0
- multipers/grids.pyx +538 -0
- multipers/gudhi/Persistence_slices_interface.h +213 -0
- multipers/gudhi/Simplex_tree_interface.h +274 -0
- multipers/gudhi/Simplex_tree_multi_interface.h +648 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex.h +450 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex_base.h +1070 -0
- multipers/gudhi/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h +579 -0
- multipers/gudhi/gudhi/Debug_utils.h +52 -0
- multipers/gudhi/gudhi/Degree_rips_bifiltration.h +2307 -0
- multipers/gudhi/gudhi/Dynamic_multi_parameter_filtration.h +2524 -0
- multipers/gudhi/gudhi/Fields/Multi_field.h +453 -0
- multipers/gudhi/gudhi/Fields/Multi_field_operators.h +460 -0
- multipers/gudhi/gudhi/Fields/Multi_field_shared.h +444 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small.h +584 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +490 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +580 -0
- multipers/gudhi/gudhi/Fields/Z2_field.h +391 -0
- multipers/gudhi/gudhi/Fields/Z2_field_operators.h +389 -0
- multipers/gudhi/gudhi/Fields/Zp_field.h +493 -0
- multipers/gudhi/gudhi/Fields/Zp_field_operators.h +384 -0
- multipers/gudhi/gudhi/Fields/Zp_field_shared.h +492 -0
- multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
- multipers/gudhi/gudhi/Matrix.h +2200 -0
- multipers/gudhi/gudhi/Multi_filtration/Multi_parameter_generator.h +1712 -0
- multipers/gudhi/gudhi/Multi_filtration/multi_filtration_conversions.h +237 -0
- multipers/gudhi/gudhi/Multi_filtration/multi_filtration_utils.h +225 -0
- multipers/gudhi/gudhi/Multi_parameter_filtered_complex.h +485 -0
- multipers/gudhi/gudhi/Multi_parameter_filtration.h +2643 -0
- multipers/gudhi/gudhi/Multi_persistence/Box.h +233 -0
- multipers/gudhi/gudhi/Multi_persistence/Line.h +309 -0
- multipers/gudhi/gudhi/Multi_persistence/Multi_parameter_filtered_complex_pcoh_interface.h +268 -0
- multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_cohomology.h +159 -0
- multipers/gudhi/gudhi/Multi_persistence/Persistence_interface_matrix.h +463 -0
- multipers/gudhi/gudhi/Multi_persistence/Point.h +853 -0
- multipers/gudhi/gudhi/Off_reader.h +173 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +834 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +838 -0
- multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +833 -0
- multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1367 -0
- multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1157 -0
- multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +869 -0
- multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +905 -0
- multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +122 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +260 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +288 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +170 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +247 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +571 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +182 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +130 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +235 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +312 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1092 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +923 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +914 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +930 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1071 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +203 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +886 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +984 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1213 -0
- multipers/gudhi/gudhi/Persistence_matrix/index_mapper.h +58 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +227 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +200 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +166 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +319 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +562 -0
- multipers/gudhi/gudhi/Persistence_on_a_line.h +152 -0
- multipers/gudhi/gudhi/Persistence_on_rectangle.h +617 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Field_Zp.h +118 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Multi_field.h +173 -0
- multipers/gudhi/gudhi/Persistent_cohomology/Persistent_cohomology_column.h +128 -0
- multipers/gudhi/gudhi/Persistent_cohomology.h +769 -0
- multipers/gudhi/gudhi/Points_off_io.h +171 -0
- multipers/gudhi/gudhi/Projective_cover_kernel.h +379 -0
- multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +559 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +121 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -0
- multipers/gudhi/gudhi/Simplex_tree/filtration_value_utils.h +155 -0
- multipers/gudhi/gudhi/Simplex_tree/hooks_simplex_base.h +62 -0
- multipers/gudhi/gudhi/Simplex_tree/indexing_tag.h +27 -0
- multipers/gudhi/gudhi/Simplex_tree/serialization_utils.h +60 -0
- multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +105 -0
- multipers/gudhi/gudhi/Simplex_tree.h +3170 -0
- multipers/gudhi/gudhi/Slicer.h +848 -0
- multipers/gudhi/gudhi/Thread_safe_slicer.h +393 -0
- multipers/gudhi/gudhi/distance_functions.h +62 -0
- multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
- multipers/gudhi/gudhi/multi_simplex_tree_helpers.h +147 -0
- multipers/gudhi/gudhi/persistence_interval.h +263 -0
- multipers/gudhi/gudhi/persistence_matrix_options.h +188 -0
- multipers/gudhi/gudhi/reader_utils.h +367 -0
- multipers/gudhi/gudhi/simple_mdspan.h +484 -0
- multipers/gudhi/gudhi/slicer_helpers.h +779 -0
- multipers/gudhi/tmp_h0_pers/mma_interface_h0.h +223 -0
- multipers/gudhi/tmp_h0_pers/naive_merge_tree.h +536 -0
- multipers/io.cpython-312-darwin.so +0 -0
- multipers/io.pyx +472 -0
- multipers/ml/__init__.py +0 -0
- multipers/ml/accuracies.py +90 -0
- multipers/ml/invariants_with_persistable.py +79 -0
- multipers/ml/kernels.py +176 -0
- multipers/ml/mma.py +713 -0
- multipers/ml/one.py +472 -0
- multipers/ml/point_clouds.py +352 -0
- multipers/ml/signed_measures.py +1667 -0
- multipers/ml/sliced_wasserstein.py +461 -0
- multipers/ml/tools.py +113 -0
- multipers/mma_structures.cpython-312-darwin.so +0 -0
- multipers/mma_structures.pxd +134 -0
- multipers/mma_structures.pyx +1483 -0
- multipers/mma_structures.pyx.tp +1126 -0
- multipers/multi_parameter_rank_invariant/diff_helpers.h +85 -0
- multipers/multi_parameter_rank_invariant/euler_characteristic.h +95 -0
- multipers/multi_parameter_rank_invariant/function_rips.h +317 -0
- multipers/multi_parameter_rank_invariant/hilbert_function.h +761 -0
- multipers/multi_parameter_rank_invariant/persistence_slices.h +149 -0
- multipers/multi_parameter_rank_invariant/rank_invariant.h +350 -0
- multipers/multiparameter_edge_collapse.py +41 -0
- multipers/multiparameter_module_approximation/approximation.h +2541 -0
- multipers/multiparameter_module_approximation/debug.h +107 -0
- multipers/multiparameter_module_approximation/format_python-cpp.h +292 -0
- multipers/multiparameter_module_approximation/utilities.h +428 -0
- multipers/multiparameter_module_approximation.cpython-312-darwin.so +0 -0
- multipers/multiparameter_module_approximation.pyx +286 -0
- multipers/ops.cpython-312-darwin.so +0 -0
- multipers/ops.pyx +231 -0
- multipers/pickle.py +89 -0
- multipers/plots.py +550 -0
- multipers/point_measure.cpython-312-darwin.so +0 -0
- multipers/point_measure.pyx +409 -0
- multipers/simplex_tree_multi.cpython-312-darwin.so +0 -0
- multipers/simplex_tree_multi.pxd +136 -0
- multipers/simplex_tree_multi.pyx +11719 -0
- multipers/simplex_tree_multi.pyx.tp +2102 -0
- multipers/slicer.cpython-312-darwin.so +0 -0
- multipers/slicer.pxd +2097 -0
- multipers/slicer.pxd.tp +263 -0
- multipers/slicer.pyx +13042 -0
- multipers/slicer.pyx.tp +1259 -0
- multipers/tensor/tensor.h +672 -0
- multipers/tensor.pxd +13 -0
- multipers/test.pyx +44 -0
- multipers/tests/__init__.py +70 -0
- multipers/torch/__init__.py +1 -0
- multipers/torch/diff_grids.py +240 -0
- multipers/torch/rips_density.py +310 -0
- multipers/vector_interface.pxd +46 -0
- multipers-2.4.0b1.dist-info/METADATA +131 -0
- multipers-2.4.0b1.dist-info/RECORD +184 -0
- multipers-2.4.0b1.dist-info/WHEEL +6 -0
- multipers-2.4.0b1.dist-info/licenses/LICENSE +21 -0
- multipers-2.4.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2643 @@
|
|
|
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 Multi_parameter_filtration.h
|
|
13
|
+
* @author Hannah Schreiber, David Loiseaux
|
|
14
|
+
* @brief Contains the @ref Gudhi::multi_filtration::Multi_parameter_filtration class.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#ifndef MF_MULTI_PARAMETER_FILTRATION_H_
|
|
18
|
+
#define MF_MULTI_PARAMETER_FILTRATION_H_
|
|
19
|
+
|
|
20
|
+
#include <algorithm> //std::lower_bound
|
|
21
|
+
#include <cmath> //std::isnan, std::min, std::abs
|
|
22
|
+
#include <cstddef> //std::size_t
|
|
23
|
+
#include <cstdint> //std::int32_t, std::uint8_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 <numeric> //std::iota
|
|
32
|
+
#include <vector>
|
|
33
|
+
#include <initializer_list>
|
|
34
|
+
|
|
35
|
+
#include <gudhi/Debug_utils.h>
|
|
36
|
+
#include <gudhi/simple_mdspan.h>
|
|
37
|
+
#include <gudhi/Multi_filtration/multi_filtration_utils.h>
|
|
38
|
+
#include <oneapi/tbb/parallel_for.h>
|
|
39
|
+
|
|
40
|
+
namespace Gudhi::multi_filtration {
|
|
41
|
+
|
|
42
|
+
// declaration needed pre C++20 for friends with templates defined inside a class
|
|
43
|
+
template <typename U>
|
|
44
|
+
U compute_linear_projection();
|
|
45
|
+
template <typename U>
|
|
46
|
+
U compute_euclidean_distance_to();
|
|
47
|
+
template <typename U>
|
|
48
|
+
U compute_norm();
|
|
49
|
+
template <typename OutValue, typename U>
|
|
50
|
+
void compute_coordinates_in_grid();
|
|
51
|
+
template <typename U>
|
|
52
|
+
void evaluate_coordinates_in_grid();
|
|
53
|
+
template <bool inverse>
|
|
54
|
+
bool is_strict_less_than_lexicographically();
|
|
55
|
+
template <bool inverse>
|
|
56
|
+
bool is_less_or_equal_than_lexicographically();
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @class Multi_parameter_filtration Multi_parameter_filtration.h gudhi/Multi_parameter_filtration.h
|
|
60
|
+
* @ingroup multi_filtration
|
|
61
|
+
*
|
|
62
|
+
* @brief Class encoding the different generators, i.e., apparition times, of a \f$ k \f$-critical
|
|
63
|
+
* \f$\mathbb R^n\f$-filtration value. E.g., the filtration value of a simplex, or, of the algebraic generator of a
|
|
64
|
+
* module presentation. The encoding is compacted into a single vector, so if a lot of non trivial modifications are
|
|
65
|
+
* done (that not only consists of simply adding new generators at the end of the vector), it is probably preferable
|
|
66
|
+
* to use @ref Dynamic_multi_parameter_filtration instead. Implements the concept @ref FiltrationValue of the
|
|
67
|
+
* @ref Gudhi::Simplex_tree and the concept @ref Gudhi::multi_persistence::MultiFiltrationValue.
|
|
68
|
+
*
|
|
69
|
+
* @details Overloads `std::numeric_limits` such that:
|
|
70
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::has_infinity` returns `true`,
|
|
71
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::has_quiet_NaN` returns `std::numeric_limits<T>::has_quiet_NaN`,
|
|
72
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::infinity(int)` returns
|
|
73
|
+
* @ref Multi_parameter_filtration::inf(int) "",
|
|
74
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::minus_infinity(int)` returns
|
|
75
|
+
* @ref Multi_parameter_filtration::minus_inf(int) "",
|
|
76
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::max(int num_param)` returns a @ref Multi_parameter_filtration
|
|
77
|
+
* with one generator of `num_param` parameters evaluated at value `std::numeric_limits<T>::max()`,
|
|
78
|
+
* - `std::numeric_limits<Multi_parameter_filtration>::quiet_NaN(int)` returns
|
|
79
|
+
* @ref Multi_parameter_filtration::nan(int) if `std::numeric_limits<Multi_parameter_filtration>::has_quiet_NaN`
|
|
80
|
+
* and throws otherwise.
|
|
81
|
+
*
|
|
82
|
+
* Multi-critical filtrations are filtrations such that the lifetime of each object is union of positive cones in
|
|
83
|
+
* \f$\mathbb R^n\f$, e.g.,
|
|
84
|
+
* - \f$ \{ x \in \mathbb R^2 : x \ge (1,2)\} \cap \{ x \in \mathbb R^2 : x \ge (2,1)\} \f$ is finitely critical,
|
|
85
|
+
* and more particularly 2-critical, while
|
|
86
|
+
* - \f$ \{ x \in \mathbb R^2 : x \ge \mathrm{epigraph}(y \mapsto e^{-y})\} \f$ is not.
|
|
87
|
+
*
|
|
88
|
+
* @tparam T Arithmetic type of an entry for one parameter of a filtration value. Has to be **signed** and
|
|
89
|
+
* to implement `std::isnan(T)`, `std::numeric_limits<T>::has_quiet_NaN`, `std::numeric_limits<T>::quiet_NaN()`,
|
|
90
|
+
* `std::numeric_limits<T>::has_infinity`, `std::numeric_limits<T>::infinity()` and `std::numeric_limits<T>::max()`.
|
|
91
|
+
* If `std::numeric_limits<T>::has_infinity` returns `false`, a call to `std::numeric_limits<T>::infinity()`
|
|
92
|
+
* can simply throw. Examples are the native types `double`, `float` and `int`.
|
|
93
|
+
* @tparam Co If `true`, reverses the poset order, i.e., the order \f$ \le \f$ in \f$ \mathbb R^n \f$ becomes
|
|
94
|
+
* \f$ \ge \f$. That is, the positive cones representing a lifetime become all negative instead.
|
|
95
|
+
* @tparam Ensure1Criticality If `true`, the methods ensure that the filtration value is always 1-critical by throwing
|
|
96
|
+
* or refusing to compile if a modification increases the number of generators.
|
|
97
|
+
*/
|
|
98
|
+
template <typename T, bool Co = false, bool Ensure1Criticality = false>
|
|
99
|
+
class Multi_parameter_filtration
|
|
100
|
+
{
|
|
101
|
+
private:
|
|
102
|
+
using view_extents = extents<std::size_t, Gudhi::dynamic_extent, Gudhi::dynamic_extent>;
|
|
103
|
+
using Viewer = Gudhi::Simple_mdspan<T, view_extents>;
|
|
104
|
+
|
|
105
|
+
public:
|
|
106
|
+
using Underlying_container = std::vector<T>; /**< Underlying container for values. */
|
|
107
|
+
|
|
108
|
+
// CONSTRUCTORS
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @brief Default constructor. Builds filtration value with one generator and given number of parameters.
|
|
112
|
+
* If Co is false, all values are at -inf, if Co is true, all values are at +inf.
|
|
113
|
+
*
|
|
114
|
+
* @param number_of_parameters If negative, takes the default value instead. Default value: 2.
|
|
115
|
+
*/
|
|
116
|
+
Multi_parameter_filtration(int number_of_parameters = 2)
|
|
117
|
+
: generators_(number_of_parameters < 0 ? 2 : number_of_parameters, _get_default_value()),
|
|
118
|
+
generator_view_(generators_.data(), generators_.empty() ? 0 : 1, generators_.size())
|
|
119
|
+
{}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @brief Builds filtration value with one generator and given number of parameters.
|
|
123
|
+
* All values are initialized at the given value.
|
|
124
|
+
*
|
|
125
|
+
* @param number_of_parameters If negative, is set to 2 instead.
|
|
126
|
+
* @param value Initialization value for every value in the generator.
|
|
127
|
+
*/
|
|
128
|
+
Multi_parameter_filtration(int number_of_parameters, T value)
|
|
129
|
+
: generators_(number_of_parameters < 0 ? 2 : number_of_parameters, value),
|
|
130
|
+
generator_view_(generators_.data(), generators_.empty() ? 0 : 1, generators_.size())
|
|
131
|
+
{}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @brief Builds filtration value with one generator that is initialized with the given range. The number of
|
|
135
|
+
* parameters are therefore deduced from the length of the range.
|
|
136
|
+
*
|
|
137
|
+
* @tparam ValueRange Range of types convertible to `T`. Should have a begin() and end() method.
|
|
138
|
+
* @param range Values of the generator.
|
|
139
|
+
*/
|
|
140
|
+
template <class ValueRange = std::initializer_list<T>, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
141
|
+
Multi_parameter_filtration(const ValueRange &range)
|
|
142
|
+
: generators_(range.begin(), range.end()),
|
|
143
|
+
generator_view_(generators_.data(), generators_.empty() ? 0 : 1, generators_.size())
|
|
144
|
+
{}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @brief Builds filtration value with one generator that is initialized with the given range. The range is
|
|
148
|
+
* determined from the two given iterators. The number of parameters are therefore deduced from the distance
|
|
149
|
+
* between the two.
|
|
150
|
+
*
|
|
151
|
+
* @tparam Iterator Iterator type that has to satisfy the requirements of standard LegacyInputIterator and
|
|
152
|
+
* dereferenced elements have to be convertible to `T`.
|
|
153
|
+
* @param it_begin Iterator pointing to the start of the range.
|
|
154
|
+
* @param it_end Iterator pointing to the end of the range.
|
|
155
|
+
*/
|
|
156
|
+
template <class Iterator>
|
|
157
|
+
Multi_parameter_filtration(Iterator it_begin, Iterator it_end)
|
|
158
|
+
: generators_(it_begin, it_end),
|
|
159
|
+
generator_view_(generators_.data(), generators_.empty() ? 0 : 1, generators_.size())
|
|
160
|
+
{}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @brief Builds filtration value with given number of parameters and values from the given range. Lets \f$ p \f$
|
|
164
|
+
* be the number of parameters. The \f$ p \f$ first elements of the range have to correspond to the first generator,
|
|
165
|
+
* the \f$ p \f$ next elements to the second generator and so on... So the length of the range has to be a multiple
|
|
166
|
+
* of \f$ p \f$ and the number of generators will be \f$ length / p \f$. The range is represented by two iterators.
|
|
167
|
+
*
|
|
168
|
+
* @tparam Iterator Iterator type that has to satisfy the requirements of standard LegacyInputIterator and
|
|
169
|
+
* dereferenced elements have to be convertible to `T`.
|
|
170
|
+
* @param it_begin Iterator pointing to the start of the range.
|
|
171
|
+
* @param it_end Iterator pointing to the end of the range.
|
|
172
|
+
* @param number_of_parameters Negative values are associated to 0.
|
|
173
|
+
*/
|
|
174
|
+
template <class Iterator, class = std::enable_if_t<!std::is_arithmetic_v<Iterator> > >
|
|
175
|
+
Multi_parameter_filtration(Iterator it_begin, Iterator it_end, int number_of_parameters)
|
|
176
|
+
: generators_(it_begin, it_end),
|
|
177
|
+
generator_view_(generators_.data(),
|
|
178
|
+
number_of_parameters <= 0 ? 0 : generators_.size() / number_of_parameters,
|
|
179
|
+
number_of_parameters)
|
|
180
|
+
{
|
|
181
|
+
if constexpr (Ensure1Criticality) {
|
|
182
|
+
if (generator_view_.extent(0) != 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @brief Builds filtration value with given number of parameters and values from the given range. Lets \f$ p \f$
|
|
188
|
+
* be the number of parameters. The \f$ p \f$ first elements of the range have to correspond to the first generator,
|
|
189
|
+
* the \f$ p \f$ next elements to the second generator and so on... So the length of the range has to be a multiple
|
|
190
|
+
* of \f$ p \f$ and the number of generators will be \f$ length / p \f$. The range is represented by
|
|
191
|
+
* @ref Multi_parameter_filtration::Underlying_container "" and copied into the underlying container of the class.
|
|
192
|
+
*
|
|
193
|
+
* @param generators Values.
|
|
194
|
+
* @param number_of_parameters Negative values are associated to 0.
|
|
195
|
+
*/
|
|
196
|
+
Multi_parameter_filtration(const Underlying_container &generators, int number_of_parameters)
|
|
197
|
+
: generators_(generators),
|
|
198
|
+
generator_view_(generators_.data(),
|
|
199
|
+
number_of_parameters <= 0 ? 0 : generators_.size() / number_of_parameters,
|
|
200
|
+
number_of_parameters)
|
|
201
|
+
{
|
|
202
|
+
GUDHI_CHECK(number_of_parameters > 0 || generators_.empty(),
|
|
203
|
+
std::invalid_argument("Number of parameters cannot be 0 if the container is not empty."));
|
|
204
|
+
|
|
205
|
+
if constexpr (Ensure1Criticality) {
|
|
206
|
+
if (generator_view_.extent(0) != 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @brief Builds filtration value with given number of parameters and values from the given range. Lets \f$ p \f$
|
|
212
|
+
* be the number of parameters. The \f$ p \f$ first elements of the range have to correspond to the first generator,
|
|
213
|
+
* the \f$ p \f$ next elements to the second generator and so on... So the length of the range has to be a multiple
|
|
214
|
+
* of \f$ p \f$ and the number of generators will be \f$ length / p \f$. The range is represented by
|
|
215
|
+
* @ref Multi_parameter_filtration::Underlying_container "" and **moved** into the underlying container of the class.
|
|
216
|
+
*
|
|
217
|
+
* @param generators Values to move.
|
|
218
|
+
* @param number_of_parameters Negative values are associated to 0.
|
|
219
|
+
*/
|
|
220
|
+
Multi_parameter_filtration(Underlying_container &&generators, int number_of_parameters)
|
|
221
|
+
: generators_(std::move(generators)),
|
|
222
|
+
generator_view_(generators_.data(),
|
|
223
|
+
number_of_parameters <= 0 ? 0 : generators_.size() / number_of_parameters,
|
|
224
|
+
number_of_parameters)
|
|
225
|
+
{
|
|
226
|
+
GUDHI_CHECK(number_of_parameters > 0 || generators_.empty(),
|
|
227
|
+
std::invalid_argument("Number of parameters cannot be 0 if the container is not empty."));
|
|
228
|
+
|
|
229
|
+
if constexpr (Ensure1Criticality) {
|
|
230
|
+
if (generator_view_.extent(0) != 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @brief Copy constructor.
|
|
236
|
+
*/
|
|
237
|
+
Multi_parameter_filtration(const Multi_parameter_filtration &other)
|
|
238
|
+
: generators_(other.generators_),
|
|
239
|
+
generator_view_(generators_.data(), other.num_generators(), other.num_parameters())
|
|
240
|
+
{}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @brief Copy constructor.
|
|
244
|
+
*
|
|
245
|
+
* @tparam U Type convertible into `T`.
|
|
246
|
+
*/
|
|
247
|
+
template <typename U, bool OtherCo, bool OtherEnsure1Criticality>
|
|
248
|
+
Multi_parameter_filtration(const Multi_parameter_filtration<U, OtherCo, OtherEnsure1Criticality> &other)
|
|
249
|
+
: generators_(other.begin(), other.end()),
|
|
250
|
+
generator_view_(generators_.data(), other.num_generators(), other.num_parameters())
|
|
251
|
+
{
|
|
252
|
+
if constexpr (Ensure1Criticality && !OtherEnsure1Criticality) {
|
|
253
|
+
if (generator_view_.extent(0) != 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @brief Move constructor.
|
|
259
|
+
*/
|
|
260
|
+
Multi_parameter_filtration(Multi_parameter_filtration &&other) noexcept
|
|
261
|
+
: generators_(std::move(other.generators_)),
|
|
262
|
+
generator_view_(generators_.data(), other.num_generators(), other.num_parameters())
|
|
263
|
+
{}
|
|
264
|
+
|
|
265
|
+
~Multi_parameter_filtration() = default;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @brief Assign operator.
|
|
269
|
+
*/
|
|
270
|
+
Multi_parameter_filtration &operator=(const Multi_parameter_filtration &other)
|
|
271
|
+
{
|
|
272
|
+
generators_ = other.generators_;
|
|
273
|
+
generator_view_ = Viewer(generators_.data(), other.num_generators(), other.num_parameters());
|
|
274
|
+
return *this;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @brief Assign operator.
|
|
279
|
+
*
|
|
280
|
+
* @tparam U Type convertible into `T`.
|
|
281
|
+
*/
|
|
282
|
+
template <typename U, bool OtherCo, bool OtherEnsure1Criticality>
|
|
283
|
+
Multi_parameter_filtration &operator=(const Multi_parameter_filtration<U, OtherCo, OtherEnsure1Criticality> &other)
|
|
284
|
+
{
|
|
285
|
+
if constexpr (Ensure1Criticality && !OtherEnsure1Criticality) {
|
|
286
|
+
if (other.num_generators() != 1) throw std::logic_error("Multiparameter filtration value is not 1-critical.");
|
|
287
|
+
}
|
|
288
|
+
generators_ = Underlying_container(other.begin(), other.end());
|
|
289
|
+
generator_view_ = Viewer(generators_.data(), other.num_generators(), other.num_parameters());
|
|
290
|
+
return *this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @brief Move assign operator.
|
|
295
|
+
*/
|
|
296
|
+
Multi_parameter_filtration &operator=(Multi_parameter_filtration &&other) noexcept
|
|
297
|
+
{
|
|
298
|
+
generators_ = std::move(other.generators_);
|
|
299
|
+
generator_view_ = Viewer(generators_.data(), other.num_generators(), other.num_parameters());
|
|
300
|
+
return *this;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @brief Swap operator.
|
|
305
|
+
*/
|
|
306
|
+
friend void swap(Multi_parameter_filtration &f1, Multi_parameter_filtration &f2) noexcept
|
|
307
|
+
{
|
|
308
|
+
f1.generators_.swap(f2.generators_);
|
|
309
|
+
swap(f1.generator_view_, f2.generator_view_);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// VECTOR-LIKE
|
|
313
|
+
|
|
314
|
+
using value_type = T; /**< Value type. */
|
|
315
|
+
using size_type = typename Underlying_container::size_type; /**< Size type. */
|
|
316
|
+
using difference_type = typename Underlying_container::difference_type; /**< Difference type. */
|
|
317
|
+
using reference = value_type &; /**< Reference type. */
|
|
318
|
+
using const_reference = const value_type &; /**< Const reference type. */
|
|
319
|
+
using pointer = typename Underlying_container::pointer; /**< Pointer type. */
|
|
320
|
+
using const_pointer = typename Underlying_container::const_pointer; /**< Const pointer type. */
|
|
321
|
+
using iterator = typename Underlying_container::iterator; /**< Iterator type. */
|
|
322
|
+
using const_iterator = typename Underlying_container::const_iterator; /**< Const iterator type. */
|
|
323
|
+
using reverse_iterator = typename Underlying_container::reverse_iterator; /**< Reverse iterator type. */
|
|
324
|
+
using const_reverse_iterator = typename Underlying_container::const_reverse_iterator; /**< Const reverse iterator. */
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @brief Returns reference to value of parameter `p` of generator `g`.
|
|
328
|
+
*/
|
|
329
|
+
reference operator()(size_type g, size_type p) { return generator_view_(g, p); }
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @brief Returns const reference to value of parameter `p` of generator `g`.
|
|
333
|
+
*/
|
|
334
|
+
const_reference operator()(size_type g, size_type p) const { return generator_view_(g, p); }
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @brief Let \f$ g \f$ be the first value in `indices` and \f$ p \f$ the second value.
|
|
338
|
+
* Returns reference to value of parameter \f$ p \f$ of generator \f$ g \f$.
|
|
339
|
+
*
|
|
340
|
+
* @tparam IndexRange Range with a begin() and size() method.
|
|
341
|
+
* @param indices Range with at least two elements. The first element should correspond to the generator number and
|
|
342
|
+
* the second element to the parameter number.
|
|
343
|
+
*/
|
|
344
|
+
template <class IndexRange = std::initializer_list<size_type>,
|
|
345
|
+
class = std::enable_if_t<RangeTraits<IndexRange>::has_begin> >
|
|
346
|
+
reference operator[](const IndexRange &indices)
|
|
347
|
+
{
|
|
348
|
+
GUDHI_CHECK(indices.size() == 2,
|
|
349
|
+
std::invalid_argument(
|
|
350
|
+
"Exactly 2 indices allowed only: first the generator number, second the parameter number."));
|
|
351
|
+
auto it = indices.begin();
|
|
352
|
+
size_type g = *it;
|
|
353
|
+
return generator_view_(g, *(++it));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @brief Let \f$ g \f$ be the first value in `indices` and \f$ p \f$ the second value.
|
|
358
|
+
* Returns reference to value of parameter \f$ p \f$ of generator \f$ g \f$.
|
|
359
|
+
*
|
|
360
|
+
* @tparam IndexRange Range with a begin() and size() method.
|
|
361
|
+
* @param indices Range with at least two elements. The first element should correspond to the generator number and
|
|
362
|
+
* the second element to the parameter number.
|
|
363
|
+
*/
|
|
364
|
+
template <class IndexRange = std::initializer_list<size_type>,
|
|
365
|
+
class = std::enable_if_t<RangeTraits<IndexRange>::has_begin> >
|
|
366
|
+
const_reference operator[](const IndexRange &indices) const
|
|
367
|
+
{
|
|
368
|
+
GUDHI_CHECK(indices.size() == 2,
|
|
369
|
+
std::invalid_argument(
|
|
370
|
+
"Exactly 2 indices allowed only: first the generator number, second the parameter number."));
|
|
371
|
+
auto it = indices.begin();
|
|
372
|
+
size_type g = *it;
|
|
373
|
+
return generator_view_(g, *(++it));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* @brief Returns an iterator pointing the begining of the underlying container. The @ref num_parameters() first
|
|
378
|
+
* elements corresponds to the first generator, the @ref num_parameters() next to the second and so on.
|
|
379
|
+
*
|
|
380
|
+
* @warning If a generator is modified and the new set of generators is not minimal or not sorted, the behaviour
|
|
381
|
+
* of most methods is undefined. It is possible to call @ref simplify() after construction if there is a doubt to
|
|
382
|
+
* ensure this property.
|
|
383
|
+
*/
|
|
384
|
+
iterator begin() noexcept { return generators_.begin(); }
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @brief Returns an iterator pointing the begining of the underlying container. The @ref num_parameters() first
|
|
388
|
+
* elements corresponds to the first generator, the @ref num_parameters() next to the second and so on.
|
|
389
|
+
*/
|
|
390
|
+
const_iterator begin() const noexcept { return generators_.begin(); }
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @brief Returns an iterator pointing the begining of the underlying container. The @ref num_parameters() first
|
|
394
|
+
* elements corresponds to the first generator, the @ref num_parameters() next to the second and so on.
|
|
395
|
+
*/
|
|
396
|
+
const_iterator cbegin() const noexcept { return generators_.cbegin(); }
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* @brief Returns an iterator pointing the end of the underlying container.
|
|
400
|
+
*/
|
|
401
|
+
iterator end() noexcept { return generators_.end(); }
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @brief Returns an iterator pointing the end of the underlying container.
|
|
405
|
+
*/
|
|
406
|
+
const_iterator end() const noexcept { return generators_.end(); }
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @brief Returns an iterator pointing the end of the underlying container.
|
|
410
|
+
*/
|
|
411
|
+
const_iterator cend() const noexcept { return generators_.cend(); }
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
|
|
415
|
+
* The @ref num_parameters() first elements corresponds to the last generator (in parameter reverse order), the
|
|
416
|
+
* @ref num_parameters() next to the second to last and so on.
|
|
417
|
+
*
|
|
418
|
+
* @warning If a generator is modified and the new set of generators is not minimal or not sorted, the behaviour
|
|
419
|
+
* of most methods is undefined. It is possible to call @ref simplify() after construction if there is a doubt to
|
|
420
|
+
* ensure this property.
|
|
421
|
+
*/
|
|
422
|
+
reverse_iterator rbegin() noexcept { return generators_.rbegin(); }
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
|
|
426
|
+
* The @ref num_parameters() first elements corresponds to the last generator (in parameter reverse order), the
|
|
427
|
+
* @ref num_parameters() next to the second to last and so on.
|
|
428
|
+
*/
|
|
429
|
+
const_reverse_iterator rbegin() const noexcept { return generators_.rbegin(); }
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @brief Returns a reverse iterator pointing to the first element from the back of the underlying container.
|
|
433
|
+
* The @ref num_parameters() first elements corresponds to the last generator (in parameter reverse order), the
|
|
434
|
+
* @ref num_parameters() next to the second to last and so on.
|
|
435
|
+
*/
|
|
436
|
+
const_reverse_iterator crbegin() const noexcept { return generators_.crbegin(); }
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
|
|
440
|
+
*/
|
|
441
|
+
reverse_iterator rend() noexcept { return generators_.rend(); }
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
|
|
445
|
+
*/
|
|
446
|
+
const_reverse_iterator rend() const noexcept { return generators_.rend(); }
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @brief Returns a reverse iterator pointing to the end of the reversed underlying container.
|
|
450
|
+
*/
|
|
451
|
+
const_reverse_iterator crend() const noexcept { return generators_.crend(); }
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @brief Returns the size of the underlying container. Corresponds exactly to @ref num_entries(), but enables to use
|
|
455
|
+
* the class as a classic range with a `begin`, `end` and `size` method.
|
|
456
|
+
*/
|
|
457
|
+
size_type size() const noexcept { return generators_.size(); }
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @brief Reserves space for the given number of generators in the underlying container. Does nothing if
|
|
461
|
+
* `Ensure1Criticality` is true.
|
|
462
|
+
*/
|
|
463
|
+
void reserve([[maybe_unused]] size_type number_of_generators)
|
|
464
|
+
{
|
|
465
|
+
if constexpr (Ensure1Criticality) {
|
|
466
|
+
return;
|
|
467
|
+
} else {
|
|
468
|
+
generators_.reserve(num_parameters() * number_of_generators);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// CONVERTERS
|
|
473
|
+
|
|
474
|
+
// like numpy
|
|
475
|
+
/**
|
|
476
|
+
* @brief Returns a copy with entries casted into the type given as template parameter.
|
|
477
|
+
*
|
|
478
|
+
* @tparam U New type for the entries.
|
|
479
|
+
* @tparam OCo New value for `Co`. Default value: `Co`.
|
|
480
|
+
* @tparam OEns New value for `Ensure1Criticality`. Note that if `OEns` is set to true and the value is not
|
|
481
|
+
* 1-critical, the method will throw. Default value: `Ensure1Criticality`.
|
|
482
|
+
* @return Copy with new entry type.
|
|
483
|
+
*/
|
|
484
|
+
template <typename U, bool OCo = Co, bool OEns = Ensure1Criticality>
|
|
485
|
+
Multi_parameter_filtration<U, OCo, OEns> as_type() const
|
|
486
|
+
{
|
|
487
|
+
std::vector<U> out(generators_.begin(), generators_.end());
|
|
488
|
+
return Multi_parameter_filtration<U, OCo, OEns>(std::move(out), num_parameters());
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ACCESS
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @brief Returns the number of parameters in the filtration value.
|
|
495
|
+
*/
|
|
496
|
+
size_type num_parameters() const { return generator_view_.extent(1); }
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* @brief Returns the number of generators in the filtration value, i.e. the criticality of the element.
|
|
500
|
+
*/
|
|
501
|
+
size_type num_generators() const
|
|
502
|
+
{
|
|
503
|
+
if constexpr (Ensure1Criticality) {
|
|
504
|
+
return 1; // for possible optimizations? If there is none, we can just keep the other version
|
|
505
|
+
} else {
|
|
506
|
+
return generator_view_.extent(0);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* @brief Returns the total number of values in the filtration value, that is,
|
|
512
|
+
* @ref num_parameters() * @ref num_generators().
|
|
513
|
+
*/
|
|
514
|
+
size_type num_entries() const { return generators_.size(); }
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @brief Returns a filtration value with given number of parameters for which @ref is_plus_inf() returns `true`.
|
|
518
|
+
*/
|
|
519
|
+
static Multi_parameter_filtration inf(int number_of_parameters)
|
|
520
|
+
{
|
|
521
|
+
return Multi_parameter_filtration(number_of_parameters, T_inf);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* @brief Returns a filtration value with given number of parameters for which @ref is_minus_inf() returns `true`.
|
|
526
|
+
*/
|
|
527
|
+
static Multi_parameter_filtration minus_inf(int number_of_parameters)
|
|
528
|
+
{
|
|
529
|
+
return Multi_parameter_filtration(number_of_parameters, T_m_inf);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* @brief If `std::numeric_limits<T>::has_quiet_NaN` is true, returns a filtration value with given number of
|
|
534
|
+
* parameters for which @ref is_nan() returns `true`. Otherwise, throws.
|
|
535
|
+
*/
|
|
536
|
+
static Multi_parameter_filtration nan(int number_of_parameters)
|
|
537
|
+
{
|
|
538
|
+
if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
|
|
539
|
+
return Multi_parameter_filtration(number_of_parameters, std::numeric_limits<T>::quiet_NaN());
|
|
540
|
+
} else {
|
|
541
|
+
throw std::logic_error("No NaN value exists.");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// DESCRIPTORS
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @brief Returns value of `Ensure1Criticality`.
|
|
549
|
+
*/
|
|
550
|
+
static constexpr bool ensures_1_criticality() { return Ensure1Criticality; }
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @brief Returns value of `Co`.
|
|
554
|
+
*/
|
|
555
|
+
static constexpr bool has_negative_cones() { return Co; }
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* @brief Returns `true` if and only if the filtration value is considered as plus infinity.
|
|
559
|
+
*/
|
|
560
|
+
[[nodiscard]] bool is_plus_inf() const
|
|
561
|
+
{
|
|
562
|
+
for (const T &v : generators_) {
|
|
563
|
+
if (v != T_inf) return false;
|
|
564
|
+
}
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* @brief Returns `true` if and only if the filtration value is considered as minus infinity.
|
|
570
|
+
*/
|
|
571
|
+
[[nodiscard]] bool is_minus_inf() const
|
|
572
|
+
{
|
|
573
|
+
for (const T &v : generators_) {
|
|
574
|
+
if (v != T_m_inf) return false;
|
|
575
|
+
}
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* @brief Returns `true` if and only if the filtration value is considered as NaN.
|
|
581
|
+
*/
|
|
582
|
+
[[nodiscard]] bool is_nan() const
|
|
583
|
+
{
|
|
584
|
+
if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
|
|
585
|
+
for (const auto &v : generators_) {
|
|
586
|
+
if (!std::isnan(v)) return false;
|
|
587
|
+
}
|
|
588
|
+
return true;
|
|
589
|
+
} else {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* @brief Returns `true` if and only if the filtration value is non-empty and is not considered as plus infinity,
|
|
596
|
+
* minus infinity or NaN.
|
|
597
|
+
*/
|
|
598
|
+
[[nodiscard]] bool is_finite() const
|
|
599
|
+
{
|
|
600
|
+
bool isInf = true, isMinusInf = true, isNan = true;
|
|
601
|
+
for (const auto &v : generators_) {
|
|
602
|
+
if (v != T_inf) isInf = false;
|
|
603
|
+
if (v != T_m_inf) isMinusInf = false;
|
|
604
|
+
if (!_is_nan(v)) isNan = false;
|
|
605
|
+
if (!isInf && !isMinusInf && !isNan) return true;
|
|
606
|
+
}
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// COMPARAISON OPERATORS
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* @brief Returns `true` if and only if the first argument is lexicographically strictly less than the second
|
|
614
|
+
* argument. The "words" considered for the lexicographical order are all the generators concatenated together
|
|
615
|
+
* in order of generator index and then in order of parameter index. Different from @ref operator< "", this order
|
|
616
|
+
* is total.
|
|
617
|
+
*
|
|
618
|
+
* @tparam inverse If true, the parameter index and generator index order is inverted.
|
|
619
|
+
*/
|
|
620
|
+
template <bool inverse = false>
|
|
621
|
+
friend bool is_strict_less_than_lexicographically(const Multi_parameter_filtration &a,
|
|
622
|
+
const Multi_parameter_filtration &b)
|
|
623
|
+
{
|
|
624
|
+
if (&a == &b) return false;
|
|
625
|
+
|
|
626
|
+
GUDHI_CHECK(a.num_parameters() == b.num_parameters(),
|
|
627
|
+
std::invalid_argument("Only filtration values with same number of parameters can be compared."));
|
|
628
|
+
|
|
629
|
+
for (std::size_t i = 0U; i < a.num_parameters() * std::min(a.num_generators(), b.num_generators()); ++i) {
|
|
630
|
+
std::size_t iA = i;
|
|
631
|
+
std::size_t iB = i;
|
|
632
|
+
if constexpr (inverse) {
|
|
633
|
+
iA = a.generators_.size() - 1 - i;
|
|
634
|
+
iB = b.generators_.size() - 1 - i;
|
|
635
|
+
}
|
|
636
|
+
if (_is_nan(a.generators_[iA]) && !_is_nan(b.generators_[iB])) return false;
|
|
637
|
+
if (_is_nan(b.generators_[iB])) return true;
|
|
638
|
+
if (a.generators_[iA] < b.generators_[iB]) return true;
|
|
639
|
+
if (b.generators_[iB] < a.generators_[iA]) return false;
|
|
640
|
+
}
|
|
641
|
+
return a.num_generators() < b.num_generators();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* @brief Returns `true` if and only if the first argument is lexicographically less than or equal to the second
|
|
646
|
+
* argument. The "words" considered for the lexicographical order are all the generators concatenated together
|
|
647
|
+
* in order of generator index and then in order of parameter index. Different from @ref operator<= "", this order
|
|
648
|
+
* is total.
|
|
649
|
+
*
|
|
650
|
+
* @tparam inverse If true, the parameter index and generator index order is inverted.
|
|
651
|
+
*/
|
|
652
|
+
template <bool inverse = false>
|
|
653
|
+
friend bool is_less_or_equal_than_lexicographically(const Multi_parameter_filtration &a,
|
|
654
|
+
const Multi_parameter_filtration &b)
|
|
655
|
+
{
|
|
656
|
+
if (&a == &b) return true;
|
|
657
|
+
|
|
658
|
+
GUDHI_CHECK(a.num_parameters() == b.num_parameters(),
|
|
659
|
+
std::invalid_argument("Only filtration values with same number of parameters can be compared."));
|
|
660
|
+
|
|
661
|
+
for (std::size_t i = 0U; i < a.num_parameters() * std::min(a.num_generators(), b.num_generators()); ++i) {
|
|
662
|
+
std::size_t iA = i;
|
|
663
|
+
std::size_t iB = i;
|
|
664
|
+
if constexpr (inverse) {
|
|
665
|
+
iA = a.generators_.size() - 1 - i;
|
|
666
|
+
iB = b.generators_.size() - 1 - i;
|
|
667
|
+
}
|
|
668
|
+
if (_is_nan(a.generators_[iA]) && !_is_nan(b.generators_[iB])) return false;
|
|
669
|
+
if (_is_nan(b.generators_[iB])) return true;
|
|
670
|
+
if (a.generators_[iA] < b.generators_[iB]) return true;
|
|
671
|
+
if (b.generators_[iB] < a.generators_[iA]) return false;
|
|
672
|
+
}
|
|
673
|
+
return a.num_generators() <= b.num_generators();
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* @brief Returns `true` if and only if the cones generated by @p b are strictly contained in the
|
|
678
|
+
* cones generated by @p a (recall that the cones are positive if `Co` is false and negative if `Co` is true).
|
|
679
|
+
* Both @p a and @p b have to have the same number of parameters.
|
|
680
|
+
*
|
|
681
|
+
* Note that not all filtration values are comparable. That is, \f$ a < b \f$ and \f$ b < a \f$ returning both false
|
|
682
|
+
* does **not** imply \f$ a == b \f$. If a total order is needed, use @ref is_strict_less_than_lexicographically
|
|
683
|
+
* instead.
|
|
684
|
+
*/
|
|
685
|
+
friend bool operator<(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b)
|
|
686
|
+
{
|
|
687
|
+
if (&a == &b) return false;
|
|
688
|
+
|
|
689
|
+
GUDHI_CHECK(a.num_parameters() == b.num_parameters(),
|
|
690
|
+
std::invalid_argument("Only filtration values with same number of parameters can be compared."));
|
|
691
|
+
|
|
692
|
+
if (a.num_generators() == 0 || b.num_generators() == 0) return false;
|
|
693
|
+
|
|
694
|
+
const auto &view_a = a.generator_view_;
|
|
695
|
+
const auto &view_b = b.generator_view_;
|
|
696
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
697
|
+
if constexpr (Ensure1Criticality) {
|
|
698
|
+
if (_first_dominates(view_a, 0, view_b, 0)) return false;
|
|
699
|
+
return _strictly_contains(view_a, 0, view_b, 0);
|
|
700
|
+
} else {
|
|
701
|
+
for (std::size_t i = 0U; i < b.num_generators(); ++i) {
|
|
702
|
+
// for each generator in b, verify if it is strictly in the cone of at least one generator of a
|
|
703
|
+
bool isContained = false;
|
|
704
|
+
for (std::size_t j = 0U; j < a.num_generators() && !isContained; ++j) {
|
|
705
|
+
// lexicographical order, so if a[j][0] dom b[j][0], than a[j'] can never strictly contain b[i] for all
|
|
706
|
+
// j' > j.
|
|
707
|
+
if (_first_dominates(view_a, j, view_b, i)) return false;
|
|
708
|
+
isContained = _strictly_contains(view_a, j, view_b, i);
|
|
709
|
+
}
|
|
710
|
+
if (!isContained) return false;
|
|
711
|
+
}
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* @brief Returns `true` if and only if the cones generated by @p a are strictly contained in the
|
|
718
|
+
* cones generated by @p b (recall that the cones are positive if `Co` is false and negative if `Co` is true).
|
|
719
|
+
* Both @p a and @p b have to have the same number of parameters.
|
|
720
|
+
*
|
|
721
|
+
* Note that not all filtration values are comparable. That is, \f$ a \le b \f$ and \f$ b \le a \f$ can both return
|
|
722
|
+
* `false`. If a total order is needed, use @ref is_less_or_equal_than_lexicographically instead.
|
|
723
|
+
*/
|
|
724
|
+
friend bool operator<=(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b)
|
|
725
|
+
{
|
|
726
|
+
GUDHI_CHECK(a.num_parameters() == b.num_parameters(),
|
|
727
|
+
std::invalid_argument("Only filtration values with same number of parameters can be compared."));
|
|
728
|
+
|
|
729
|
+
if (a.num_generators() == 0 || b.num_generators() == 0) return false;
|
|
730
|
+
if (a.is_nan() || b.is_nan()) return false;
|
|
731
|
+
if (&a == &b) return true;
|
|
732
|
+
|
|
733
|
+
const auto &view_a = a.generator_view_;
|
|
734
|
+
const auto &view_b = b.generator_view_;
|
|
735
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
736
|
+
if constexpr (Ensure1Criticality) {
|
|
737
|
+
if (_first_strictly_dominates(view_a, 0, view_b, 0)) return false;
|
|
738
|
+
return _contains(view_a, 0, view_b, 0);
|
|
739
|
+
} else {
|
|
740
|
+
// check if this curves is below other's curve
|
|
741
|
+
// ie for each guy in this, check if there is a guy in other that dominates him
|
|
742
|
+
for (std::size_t i = 0U; i < b.num_generators(); ++i) {
|
|
743
|
+
// for each generator in b, verify if it is in the cone of at least one generator of a
|
|
744
|
+
bool isContained = false;
|
|
745
|
+
for (std::size_t j = 0U; j < a.num_generators() && !isContained; ++j) {
|
|
746
|
+
// lexicographical order, so if a[j][0] strictly dom b[j][0], than a[j'] can never contain b[i] for all
|
|
747
|
+
// j' > j.
|
|
748
|
+
if (_first_strictly_dominates(view_a, j, view_b, i)) return false;
|
|
749
|
+
isContained = _contains(view_a, j, view_b, i);
|
|
750
|
+
}
|
|
751
|
+
if (!isContained) return false;
|
|
752
|
+
}
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* @brief Returns `true` if and only if the cones generated by @p b are contained in or are (partially)
|
|
759
|
+
* equal to the cones generated by @p a (recall that the cones are positive if `Co` is false and negative if `Co` is
|
|
760
|
+
* true).
|
|
761
|
+
* Both @p a and @p b have to have the same number of parameters.
|
|
762
|
+
*
|
|
763
|
+
* Note that not all filtration values are comparable. That is, \f$ a > b \f$ and \f$ b > a \f$ returning both false
|
|
764
|
+
* does **not** imply \f$ a == b \f$. If a total order is needed, use @ref is_strict_less_than_lexicographically
|
|
765
|
+
* instead.
|
|
766
|
+
*/
|
|
767
|
+
friend bool operator>(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b) { return b < a; }
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* @brief Returns `true` if and only if the cones generated by @p a are contained in or are (partially)
|
|
771
|
+
* equal to the cones generated by @p b (recall that the cones are positive if `Co` is false and negative if `Co` is
|
|
772
|
+
* true).
|
|
773
|
+
* Both @p a and @p b have to have the same number of parameters.
|
|
774
|
+
*
|
|
775
|
+
* Note that not all filtration values are comparable. That is, \f$ a \ge b \f$ and \f$ b \ge a \f$ can both return
|
|
776
|
+
* `false`. If a total order is needed, use @ref is_less_or_equal_than_lexicographically instead.
|
|
777
|
+
*/
|
|
778
|
+
friend bool operator>=(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b) { return b <= a; }
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* @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$.
|
|
782
|
+
*/
|
|
783
|
+
friend bool operator==(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b)
|
|
784
|
+
{
|
|
785
|
+
if (&a == &b) return true;
|
|
786
|
+
// assumes lexicographical order for both
|
|
787
|
+
return a.generators_ == b.generators_;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* @brief Returns `true` if and only if \f$ a == b \f$ returns `false`.
|
|
792
|
+
*/
|
|
793
|
+
friend bool operator!=(const Multi_parameter_filtration &a, const Multi_parameter_filtration &b) { return !(a == b); }
|
|
794
|
+
|
|
795
|
+
// ARITHMETIC OPERATORS
|
|
796
|
+
|
|
797
|
+
// opposite
|
|
798
|
+
/**
|
|
799
|
+
* @brief Returns a filtration value such that an entry at index \f$ i,j \f$ is equal to \f$ -f(i,j) \f$.
|
|
800
|
+
*
|
|
801
|
+
* Used conventions:
|
|
802
|
+
* - \f$ -NaN = NaN \f$.
|
|
803
|
+
*
|
|
804
|
+
* @param f Value to opposite.
|
|
805
|
+
* @return The opposite of @p f.
|
|
806
|
+
*/
|
|
807
|
+
friend Multi_parameter_filtration operator-(const Multi_parameter_filtration &f)
|
|
808
|
+
{
|
|
809
|
+
using F = Multi_parameter_filtration;
|
|
810
|
+
|
|
811
|
+
Underlying_container result(f.generators_);
|
|
812
|
+
std::for_each(result.begin(), result.end(), [](T &v) {
|
|
813
|
+
if (v == F::T_inf)
|
|
814
|
+
v = F::T_m_inf;
|
|
815
|
+
else if (v == F::T_m_inf)
|
|
816
|
+
v = F::T_inf;
|
|
817
|
+
else
|
|
818
|
+
v = -v;
|
|
819
|
+
});
|
|
820
|
+
return Multi_parameter_filtration(std::move(result), f.num_parameters());
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// subtraction
|
|
824
|
+
/**
|
|
825
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
826
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) - r(p) \f$
|
|
827
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
828
|
+
*
|
|
829
|
+
* Used conventions:
|
|
830
|
+
* - \f$ inf - inf = NaN \f$,
|
|
831
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
832
|
+
* - \f$ NaN - b = NaN \f$,
|
|
833
|
+
* - \f$ a - NaN = NaN \f$.
|
|
834
|
+
*
|
|
835
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
836
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
837
|
+
*
|
|
838
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
839
|
+
* @param f First element of the subtraction.
|
|
840
|
+
* @param r Second element of the subtraction.
|
|
841
|
+
*/
|
|
842
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
843
|
+
friend Multi_parameter_filtration operator-(Multi_parameter_filtration f, const ValueRange &r)
|
|
844
|
+
{
|
|
845
|
+
f -= r;
|
|
846
|
+
return f;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
851
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ r(p) - f(g,p) \f$
|
|
852
|
+
* if \f$ p < length_r \f$ and to \f$ -f(g,p) \f$ otherwise.
|
|
853
|
+
*
|
|
854
|
+
* Used conventions:
|
|
855
|
+
* - \f$ inf - inf = NaN \f$,
|
|
856
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
857
|
+
* - \f$ NaN - b = NaN \f$,
|
|
858
|
+
* - \f$ a - NaN = NaN \f$.
|
|
859
|
+
*
|
|
860
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
861
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
862
|
+
*
|
|
863
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
864
|
+
* @param r First element of the subtraction.
|
|
865
|
+
* @param f Second element of the subtraction.
|
|
866
|
+
*/
|
|
867
|
+
template <class ValueRange,
|
|
868
|
+
class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
|
|
869
|
+
!std::is_same_v<ValueRange, Multi_parameter_filtration> > >
|
|
870
|
+
friend Multi_parameter_filtration operator-(const ValueRange &r, Multi_parameter_filtration f)
|
|
871
|
+
{
|
|
872
|
+
f._apply_operation(r, [](T &valF, const T &valR) {
|
|
873
|
+
valF = -valF;
|
|
874
|
+
_add(valF, valR);
|
|
875
|
+
});
|
|
876
|
+
return f;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) - val \f$.
|
|
881
|
+
*
|
|
882
|
+
* Used conventions:
|
|
883
|
+
* - \f$ inf - inf = NaN \f$,
|
|
884
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
885
|
+
* - \f$ NaN - b = NaN \f$,
|
|
886
|
+
* - \f$ a - NaN = NaN \f$.
|
|
887
|
+
*
|
|
888
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
889
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
890
|
+
*
|
|
891
|
+
* @param f First element of the subtraction.
|
|
892
|
+
* @param val Second element of the subtraction.
|
|
893
|
+
*/
|
|
894
|
+
friend Multi_parameter_filtration operator-(Multi_parameter_filtration f, const T &val)
|
|
895
|
+
{
|
|
896
|
+
f -= val;
|
|
897
|
+
return f;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ val - f(g,p) \f$.
|
|
902
|
+
*
|
|
903
|
+
* Used conventions:
|
|
904
|
+
* - \f$ inf - inf = NaN \f$,
|
|
905
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
906
|
+
* - \f$ NaN - b = NaN \f$,
|
|
907
|
+
* - \f$ a - NaN = NaN \f$.
|
|
908
|
+
*
|
|
909
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
910
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
911
|
+
*
|
|
912
|
+
* @param val First element of the subtraction.
|
|
913
|
+
* @param f Second element of the subtraction.
|
|
914
|
+
*/
|
|
915
|
+
friend Multi_parameter_filtration operator-(const T &val, Multi_parameter_filtration f)
|
|
916
|
+
{
|
|
917
|
+
f._apply_operation(val, [](T &valF, const T &valR) {
|
|
918
|
+
valF = -valF;
|
|
919
|
+
_add(valF, valR);
|
|
920
|
+
});
|
|
921
|
+
return f;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$, with
|
|
926
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) - r(p) \f$
|
|
927
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
928
|
+
*
|
|
929
|
+
* Used conventions:
|
|
930
|
+
* - \f$ inf - inf = NaN \f$,
|
|
931
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
932
|
+
* - \f$ NaN - b = NaN \f$,
|
|
933
|
+
* - \f$ a - NaN = NaN \f$.
|
|
934
|
+
*
|
|
935
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
936
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
937
|
+
*
|
|
938
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
939
|
+
* @param f First element of the subtraction.
|
|
940
|
+
* @param r Second element of the subtraction.
|
|
941
|
+
*/
|
|
942
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
943
|
+
friend Multi_parameter_filtration &operator-=(Multi_parameter_filtration &f, const ValueRange &r)
|
|
944
|
+
{
|
|
945
|
+
f._apply_operation(r, [](T &valF, const T &valR) { _subtract(valF, valR); });
|
|
946
|
+
return f;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) - val \f$.
|
|
951
|
+
*
|
|
952
|
+
* Used conventions:
|
|
953
|
+
* - \f$ inf - inf = NaN \f$,
|
|
954
|
+
* - \f$ -inf - (-inf) = NaN \f$,
|
|
955
|
+
* - \f$ NaN - b = NaN \f$,
|
|
956
|
+
* - \f$ a - NaN = NaN \f$.
|
|
957
|
+
*
|
|
958
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
959
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
960
|
+
*
|
|
961
|
+
* @param f First element of the subtraction.
|
|
962
|
+
* @param val Second element of the subtraction.
|
|
963
|
+
*/
|
|
964
|
+
friend Multi_parameter_filtration &operator-=(Multi_parameter_filtration &f, const T &val)
|
|
965
|
+
{
|
|
966
|
+
f._apply_operation(val, [](T &valF, const T &valR) { _subtract(valF, valR); });
|
|
967
|
+
return f;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// addition
|
|
971
|
+
/**
|
|
972
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
973
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) + r(p) \f$
|
|
974
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
975
|
+
*
|
|
976
|
+
* Used conventions:
|
|
977
|
+
* - \f$ inf + (-inf) = NaN \f$,
|
|
978
|
+
* - \f$ -inf + inf = NaN \f$,
|
|
979
|
+
* - \f$ NaN + b = NaN \f$,
|
|
980
|
+
* - \f$ a + NaN = NaN \f$.
|
|
981
|
+
*
|
|
982
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
983
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
984
|
+
*
|
|
985
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
986
|
+
* @param f First element of the addition.
|
|
987
|
+
* @param r Second element of the addition.
|
|
988
|
+
*/
|
|
989
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
990
|
+
friend Multi_parameter_filtration operator+(Multi_parameter_filtration f, const ValueRange &r)
|
|
991
|
+
{
|
|
992
|
+
f += r;
|
|
993
|
+
return f;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
998
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ r(p) + f(g,p) \f$
|
|
999
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1000
|
+
*
|
|
1001
|
+
* Used conventions:
|
|
1002
|
+
* - \f$ inf + (-inf) = NaN \f$,
|
|
1003
|
+
* - \f$ -inf + inf = NaN \f$,
|
|
1004
|
+
* - \f$ NaN + b = NaN \f$,
|
|
1005
|
+
* - \f$ a + NaN = NaN \f$.
|
|
1006
|
+
*
|
|
1007
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1008
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1009
|
+
*
|
|
1010
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1011
|
+
* @param r First element of the addition.
|
|
1012
|
+
* @param f Second element of the addition.
|
|
1013
|
+
*/
|
|
1014
|
+
template <class ValueRange,
|
|
1015
|
+
class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
|
|
1016
|
+
!std::is_same_v<ValueRange, Multi_parameter_filtration> > >
|
|
1017
|
+
friend Multi_parameter_filtration operator+(const ValueRange &r, Multi_parameter_filtration f)
|
|
1018
|
+
{
|
|
1019
|
+
f += r;
|
|
1020
|
+
return f;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) + val \f$.
|
|
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
|
+
* @param f First element of the addition.
|
|
1036
|
+
* @param val Second element of the addition.
|
|
1037
|
+
*/
|
|
1038
|
+
friend Multi_parameter_filtration operator+(Multi_parameter_filtration f, const T &val)
|
|
1039
|
+
{
|
|
1040
|
+
f += val;
|
|
1041
|
+
return f;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ val + f(g,p) \f$.
|
|
1046
|
+
*
|
|
1047
|
+
* Used conventions:
|
|
1048
|
+
* - \f$ inf + (-inf) = NaN \f$,
|
|
1049
|
+
* - \f$ -inf + inf = NaN \f$,
|
|
1050
|
+
* - \f$ NaN + b = NaN \f$,
|
|
1051
|
+
* - \f$ a + NaN = NaN \f$.
|
|
1052
|
+
*
|
|
1053
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1054
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1055
|
+
*
|
|
1056
|
+
* @param val First element of the addition.
|
|
1057
|
+
* @param f Second element of the addition.
|
|
1058
|
+
*/
|
|
1059
|
+
friend Multi_parameter_filtration operator+(const T &val, Multi_parameter_filtration f)
|
|
1060
|
+
{
|
|
1061
|
+
f += val;
|
|
1062
|
+
return f;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$, with
|
|
1067
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) + r(p) \f$
|
|
1068
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1069
|
+
*
|
|
1070
|
+
* Used conventions:
|
|
1071
|
+
* - \f$ inf + (-inf) = NaN \f$,
|
|
1072
|
+
* - \f$ -inf + inf = NaN \f$,
|
|
1073
|
+
* - \f$ NaN + b = NaN \f$,
|
|
1074
|
+
* - \f$ a + NaN = NaN \f$.
|
|
1075
|
+
*
|
|
1076
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1077
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1078
|
+
*
|
|
1079
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1080
|
+
* @param f First element of the addition.
|
|
1081
|
+
* @param r Second element of the addition.
|
|
1082
|
+
*/
|
|
1083
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
1084
|
+
friend Multi_parameter_filtration &operator+=(Multi_parameter_filtration &f, const ValueRange &r)
|
|
1085
|
+
{
|
|
1086
|
+
f._apply_operation(r, [](T &valF, const T &valR) { _add(valF, valR); });
|
|
1087
|
+
return f;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) + val \f$.
|
|
1092
|
+
*
|
|
1093
|
+
* Used conventions:
|
|
1094
|
+
* - \f$ inf + (-inf) = NaN \f$,
|
|
1095
|
+
* - \f$ -inf + inf = NaN \f$,
|
|
1096
|
+
* - \f$ NaN + b = NaN \f$,
|
|
1097
|
+
* - \f$ a + NaN = NaN \f$.
|
|
1098
|
+
*
|
|
1099
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1100
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1101
|
+
*
|
|
1102
|
+
* @param f First element of the addition.
|
|
1103
|
+
* @param val Second element of the addition.
|
|
1104
|
+
*/
|
|
1105
|
+
friend Multi_parameter_filtration &operator+=(Multi_parameter_filtration &f, const T &val)
|
|
1106
|
+
{
|
|
1107
|
+
f._apply_operation(val, [](T &valF, const T &valR) { _add(valF, valR); });
|
|
1108
|
+
return f;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// multiplication
|
|
1112
|
+
/**
|
|
1113
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
1114
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) * r(p) \f$
|
|
1115
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1116
|
+
*
|
|
1117
|
+
* Used conventions:
|
|
1118
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1119
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1120
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1121
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1122
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1123
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1124
|
+
*
|
|
1125
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1126
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1127
|
+
*
|
|
1128
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1129
|
+
* @param f First element of the multiplication.
|
|
1130
|
+
* @param r Second element of the multiplication.
|
|
1131
|
+
*/
|
|
1132
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
1133
|
+
friend Multi_parameter_filtration operator*(Multi_parameter_filtration f, const ValueRange &r)
|
|
1134
|
+
{
|
|
1135
|
+
f *= r;
|
|
1136
|
+
return f;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
1141
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ r(p) * f(g,p) \f$
|
|
1142
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1143
|
+
*
|
|
1144
|
+
* Used conventions:
|
|
1145
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1146
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1147
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1148
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1149
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1150
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1151
|
+
*
|
|
1152
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1153
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1154
|
+
*
|
|
1155
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1156
|
+
* @param r First element of the multiplication.
|
|
1157
|
+
* @param f Second element of the multiplication.
|
|
1158
|
+
*/
|
|
1159
|
+
template <class ValueRange,
|
|
1160
|
+
class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
|
|
1161
|
+
!std::is_same_v<ValueRange, Multi_parameter_filtration> > >
|
|
1162
|
+
friend Multi_parameter_filtration operator*(const ValueRange &r, Multi_parameter_filtration f)
|
|
1163
|
+
{
|
|
1164
|
+
f *= r;
|
|
1165
|
+
return f;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) * val \f$.
|
|
1170
|
+
*
|
|
1171
|
+
* Used conventions:
|
|
1172
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1173
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1174
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1175
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1176
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1177
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1178
|
+
*
|
|
1179
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1180
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1181
|
+
*
|
|
1182
|
+
* @param f First element of the multiplication.
|
|
1183
|
+
* @param val Second element of the multiplication.
|
|
1184
|
+
*/
|
|
1185
|
+
friend Multi_parameter_filtration operator*(Multi_parameter_filtration f, const T &val)
|
|
1186
|
+
{
|
|
1187
|
+
f *= val;
|
|
1188
|
+
return f;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ val * f(g,p) \f$.
|
|
1193
|
+
*
|
|
1194
|
+
* Used conventions:
|
|
1195
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1196
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1197
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1198
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1199
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1200
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1201
|
+
*
|
|
1202
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1203
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1204
|
+
*
|
|
1205
|
+
* @param val First element of the multiplication.
|
|
1206
|
+
* @param f Second element of the multiplication.
|
|
1207
|
+
*/
|
|
1208
|
+
friend Multi_parameter_filtration operator*(const T &val, Multi_parameter_filtration f)
|
|
1209
|
+
{
|
|
1210
|
+
f *= val;
|
|
1211
|
+
return f;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$, with
|
|
1216
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) * r(p) \f$
|
|
1217
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1218
|
+
*
|
|
1219
|
+
* Used conventions:
|
|
1220
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1221
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1222
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1223
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1224
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1225
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1226
|
+
*
|
|
1227
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1228
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1229
|
+
*
|
|
1230
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1231
|
+
* @param f First element of the multiplication.
|
|
1232
|
+
* @param r Second element of the multiplication.
|
|
1233
|
+
*/
|
|
1234
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
1235
|
+
friend Multi_parameter_filtration &operator*=(Multi_parameter_filtration &f, const ValueRange &r)
|
|
1236
|
+
{
|
|
1237
|
+
f._apply_operation(r, [](T &valF, const T &valR) { _multiply(valF, valR); });
|
|
1238
|
+
return f;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) * val \f$.
|
|
1243
|
+
*
|
|
1244
|
+
* Used conventions:
|
|
1245
|
+
* - \f$ inf * 0 = NaN \f$,
|
|
1246
|
+
* - \f$ 0 * inf = NaN \f$,
|
|
1247
|
+
* - \f$ -inf * 0 = NaN \f$,
|
|
1248
|
+
* - \f$ 0 * (-inf) = NaN \f$,
|
|
1249
|
+
* - \f$ NaN * b = NaN \f$,
|
|
1250
|
+
* - \f$ a * NaN = NaN \f$.
|
|
1251
|
+
*
|
|
1252
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1253
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1254
|
+
*
|
|
1255
|
+
* @param f First element of the multiplication.
|
|
1256
|
+
* @param val Second element of the multiplication.
|
|
1257
|
+
*/
|
|
1258
|
+
friend Multi_parameter_filtration &operator*=(Multi_parameter_filtration &f, const T &val)
|
|
1259
|
+
{
|
|
1260
|
+
f._apply_operation(val, [](T &valF, const T &valR) { _multiply(valF, valR); });
|
|
1261
|
+
return f;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// division
|
|
1265
|
+
/**
|
|
1266
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
1267
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) / r(p) \f$
|
|
1268
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1269
|
+
*
|
|
1270
|
+
* Used conventions:
|
|
1271
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1272
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1273
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1274
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1275
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1276
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1277
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1278
|
+
* - \f$ a / inf = 0 \f$,
|
|
1279
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1280
|
+
*
|
|
1281
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1282
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1283
|
+
*
|
|
1284
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1285
|
+
* @param f First element of the division.
|
|
1286
|
+
* @param r Second element of the division.
|
|
1287
|
+
*/
|
|
1288
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
1289
|
+
friend Multi_parameter_filtration operator/(Multi_parameter_filtration f, const ValueRange &r)
|
|
1290
|
+
{
|
|
1291
|
+
f /= r;
|
|
1292
|
+
return f;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$, with
|
|
1297
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ r(p) / f(g,p) \f$
|
|
1298
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1299
|
+
*
|
|
1300
|
+
* Used conventions:
|
|
1301
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1302
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1303
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1304
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1305
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1306
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1307
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1308
|
+
* - \f$ a / inf = 0 \f$,
|
|
1309
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1310
|
+
*
|
|
1311
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1312
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1313
|
+
*
|
|
1314
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1315
|
+
* @param r First element of the division.
|
|
1316
|
+
* @param f Second element of the division.
|
|
1317
|
+
*/
|
|
1318
|
+
template <class ValueRange,
|
|
1319
|
+
class = std::enable_if_t<RangeTraits<ValueRange>::has_begin &&
|
|
1320
|
+
!std::is_same_v<ValueRange, Multi_parameter_filtration> > >
|
|
1321
|
+
friend Multi_parameter_filtration operator/(const ValueRange &r, Multi_parameter_filtration f)
|
|
1322
|
+
{
|
|
1323
|
+
f._apply_operation(r, [](T &valF, const T &valR) {
|
|
1324
|
+
T tmp = valF;
|
|
1325
|
+
valF = valR;
|
|
1326
|
+
_divide(valF, tmp);
|
|
1327
|
+
});
|
|
1328
|
+
return f;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) / val \f$.
|
|
1333
|
+
*
|
|
1334
|
+
* Used conventions:
|
|
1335
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1336
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1337
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1338
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1339
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1340
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1341
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1342
|
+
* - \f$ a / inf = 0 \f$,
|
|
1343
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1344
|
+
*
|
|
1345
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1346
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1347
|
+
*
|
|
1348
|
+
* @param f First element of the division.
|
|
1349
|
+
* @param val Second element of the division.
|
|
1350
|
+
*/
|
|
1351
|
+
friend Multi_parameter_filtration operator/(Multi_parameter_filtration f, const T &val)
|
|
1352
|
+
{
|
|
1353
|
+
f /= val;
|
|
1354
|
+
return f;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* @brief Returns a filtration value such that an entry at index \f$ (g,p) \f$ is equal to \f$ val / f(g,p) \f$.
|
|
1359
|
+
*
|
|
1360
|
+
* Used conventions:
|
|
1361
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1362
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1363
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1364
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1365
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1366
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1367
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1368
|
+
* - \f$ a / inf = 0 \f$,
|
|
1369
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1370
|
+
*
|
|
1371
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1372
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1373
|
+
*
|
|
1374
|
+
* @param val First element of the division.
|
|
1375
|
+
* @param f Second element of the division.
|
|
1376
|
+
*/
|
|
1377
|
+
friend Multi_parameter_filtration operator/(const T &val, Multi_parameter_filtration f)
|
|
1378
|
+
{
|
|
1379
|
+
f._apply_operation(val, [](T &valF, const T &valR) {
|
|
1380
|
+
T tmp = valF;
|
|
1381
|
+
valF = valR;
|
|
1382
|
+
_divide(valF, tmp);
|
|
1383
|
+
});
|
|
1384
|
+
return f;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$, with
|
|
1389
|
+
* \f$ 0 \leq g \leq num_generators \f$ and \f$ 0 \leq p \leq num_parameters \f$ is equal to \f$ f(g,p) / r(p) \f$
|
|
1390
|
+
* if \f$ p < length_r \f$ and to \f$ f(g,p) \f$ otherwise.
|
|
1391
|
+
*
|
|
1392
|
+
* Used conventions:
|
|
1393
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1394
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1395
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1396
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1397
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1398
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1399
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1400
|
+
* - \f$ a / inf = 0 \f$,
|
|
1401
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1402
|
+
*
|
|
1403
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1404
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1405
|
+
*
|
|
1406
|
+
* @tparam ValueRange Range with a begin() and end() method.
|
|
1407
|
+
* @param f First element of the division.
|
|
1408
|
+
* @param r Second element of the division.
|
|
1409
|
+
*/
|
|
1410
|
+
template <class ValueRange, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
1411
|
+
friend Multi_parameter_filtration &operator/=(Multi_parameter_filtration &f, const ValueRange &r)
|
|
1412
|
+
{
|
|
1413
|
+
f._apply_operation(r, [](T &valF, const T &valR) { _divide(valF, valR); });
|
|
1414
|
+
return f;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* @brief Modifies the first parameter such that an entry at index \f$ (g,p) \f$ is equal to \f$ f(g,p) / val \f$.
|
|
1419
|
+
*
|
|
1420
|
+
* Used conventions:
|
|
1421
|
+
* - \f$ a / 0 = NaN \f$,
|
|
1422
|
+
* - \f$ inf / inf = NaN \f$,
|
|
1423
|
+
* - \f$ -inf / inf = NaN \f$,
|
|
1424
|
+
* - \f$ inf / -inf = NaN \f$,
|
|
1425
|
+
* - \f$ -inf / -inf = NaN \f$,
|
|
1426
|
+
* - \f$ NaN / b = NaN \f$,
|
|
1427
|
+
* - \f$ a / NaN = NaN \f$,
|
|
1428
|
+
* - \f$ a / inf = 0 \f$,
|
|
1429
|
+
* - \f$ a / -inf = 0 \f$.
|
|
1430
|
+
*
|
|
1431
|
+
* All NaN values are represented by `std::numeric_limits<T>::quiet_NaN()` independently if
|
|
1432
|
+
* `std::numeric_limits<T>::has_quiet_NaN` is true or not.
|
|
1433
|
+
*
|
|
1434
|
+
* @param f First element of the division.
|
|
1435
|
+
* @param val Second element of the division.
|
|
1436
|
+
*/
|
|
1437
|
+
friend Multi_parameter_filtration &operator/=(Multi_parameter_filtration &f, const T &val)
|
|
1438
|
+
{
|
|
1439
|
+
f._apply_operation(val, [](T &valF, const T &valR) { _divide(valF, valR); });
|
|
1440
|
+
return f;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// MODIFIERS
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* @brief Sets the number of generators. If there were less generators before, new generators with default values are
|
|
1447
|
+
* constructed. If there were more generators before, the exceed of generators is destroyed (any generator with index
|
|
1448
|
+
* higher or equal to @p g to be more precise). If @p g is zero, the methods does nothing.
|
|
1449
|
+
*
|
|
1450
|
+
* Fails to compile if `Ensure1Criticality` is true.
|
|
1451
|
+
*
|
|
1452
|
+
* @warning All new generators will be set to infinity (`Co` is true) or -infinity (`Co` is false). That is, the new
|
|
1453
|
+
* filtration value is not minimal anymore. Make sure to fill them with real generators or to remove them before
|
|
1454
|
+
* using other methods.
|
|
1455
|
+
*
|
|
1456
|
+
* @warning Be sure to call @ref simplify if necessary after initializing all the generators. Most methods will have
|
|
1457
|
+
* an undefined behaviour if the set of generators is not minimal or sorted.
|
|
1458
|
+
*
|
|
1459
|
+
* @param g New number of generators.
|
|
1460
|
+
*/
|
|
1461
|
+
void set_num_generators(size_type g)
|
|
1462
|
+
{
|
|
1463
|
+
static_assert(!Ensure1Criticality, "Number of generators cannot be set for a 1-critical only filtration value.");
|
|
1464
|
+
|
|
1465
|
+
if (g == 0) return;
|
|
1466
|
+
generators_.resize(g * num_parameters(), _get_default_value());
|
|
1467
|
+
generator_view_.update_data(generators_.data()); // in case it was relocated
|
|
1468
|
+
generator_view_.update_extent(0, g);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/**
|
|
1472
|
+
* @brief Adds the given generator to the filtration value such that the set remains minimal and sorted.
|
|
1473
|
+
* It is therefore possible that the generator is ignored if it does not generated any new lifetime or that
|
|
1474
|
+
* old generators disappear if they are overshadowed by the new one.
|
|
1475
|
+
*
|
|
1476
|
+
* @tparam GeneratorRange Range of elements convertible to `T`. Must have a begin(), end() method and the iterator
|
|
1477
|
+
* type should satisfy the requirements of the standard `LegacyForwardIterator`.
|
|
1478
|
+
* @param x New generator to add. Has to have the same number of parameters than @ref num_parameters().
|
|
1479
|
+
* @return true If and only if the generator is actually added to the set of generators.
|
|
1480
|
+
* @return false Otherwise.
|
|
1481
|
+
*/
|
|
1482
|
+
template <class GeneratorRange = std::initializer_list<T>,
|
|
1483
|
+
class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
|
|
1484
|
+
bool add_generator(const GeneratorRange &x)
|
|
1485
|
+
{
|
|
1486
|
+
return add_generator(x.begin(), x.end());
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* @brief Adds the given generator to the filtration value such that the set remains minimal and sorted.
|
|
1491
|
+
* It is therefore possible that the generator is ignored if it does not generated any new lifetime or that
|
|
1492
|
+
* old generators disappear if they are overshadowed by the new one.
|
|
1493
|
+
*
|
|
1494
|
+
* @tparam Iterator Iterator class satisfying the requirements of the standard `LegacyForwardIterator`.
|
|
1495
|
+
* The dereferenced type has to be convertible to `T`.
|
|
1496
|
+
* @param genStart Iterator pointing to the begining of the range.
|
|
1497
|
+
* @param genEnd Iterator pointing to the end of the range.
|
|
1498
|
+
* @return true If and only if the generator is actually added to the set of generators.
|
|
1499
|
+
* @return false Otherwise.
|
|
1500
|
+
*/
|
|
1501
|
+
template <class Iterator>
|
|
1502
|
+
bool add_generator(Iterator genStart, Iterator genEnd)
|
|
1503
|
+
{
|
|
1504
|
+
GUDHI_CHECK(std::distance(genStart, genEnd) == static_cast<int>(num_parameters()),
|
|
1505
|
+
std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
|
|
1506
|
+
|
|
1507
|
+
const int newIndex = -1;
|
|
1508
|
+
|
|
1509
|
+
std::size_t end = num_generators();
|
|
1510
|
+
std::vector<int> indices(end);
|
|
1511
|
+
std::iota(indices.begin(), indices.end(), 0);
|
|
1512
|
+
|
|
1513
|
+
if (_generator_can_be_added(genStart, 0, end, indices)) {
|
|
1514
|
+
indices.resize(end);
|
|
1515
|
+
indices.push_back(newIndex);
|
|
1516
|
+
_build_from(indices, newIndex, genStart, genEnd);
|
|
1517
|
+
if constexpr (Ensure1Criticality) {
|
|
1518
|
+
if (generator_view_.extent(0) != 1)
|
|
1519
|
+
throw std::logic_error("Multiparameter filtration value is not 1-critical anymore.");
|
|
1520
|
+
}
|
|
1521
|
+
return true;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
return false;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* @brief Adds the given generator to the filtration value without any verifications or simplifications at the end
|
|
1529
|
+
* of the set.
|
|
1530
|
+
*
|
|
1531
|
+
* Fails to compile if `Ensure1Criticality` is true.
|
|
1532
|
+
*
|
|
1533
|
+
* @warning If the resulting set of generators is not minimal or sorted after modification, some methods will have an
|
|
1534
|
+
* undefined behaviour. Be sure to call @ref simplify() before using them.
|
|
1535
|
+
*
|
|
1536
|
+
* @tparam GeneratorRange Range of elements convertible to `T`. Must have a begin(), end() and size() method.
|
|
1537
|
+
* @param x New generator to add. Must have the same number of parameters than @ref num_parameters().
|
|
1538
|
+
*/
|
|
1539
|
+
template <class GeneratorRange = std::initializer_list<T>,
|
|
1540
|
+
class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
|
|
1541
|
+
void add_guaranteed_generator(const GeneratorRange &x)
|
|
1542
|
+
{
|
|
1543
|
+
static_assert(!Ensure1Criticality, "Cannot add additional generator to a 1-critical only filtration value.");
|
|
1544
|
+
|
|
1545
|
+
GUDHI_CHECK(x.size() == num_parameters(),
|
|
1546
|
+
std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
|
|
1547
|
+
|
|
1548
|
+
generators_.insert(generators_.end(), x.begin(), x.end());
|
|
1549
|
+
generator_view_.update_data(generators_.data()); // in case it was relocated
|
|
1550
|
+
generator_view_.update_extent(0, num_generators() + 1);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
* @brief Simplifies the current set of generators such that it becomes minimal. Also orders it in increasing
|
|
1555
|
+
* lexicographical order. Only necessary if generators were added "by hand" without verification either trough the
|
|
1556
|
+
* constructor or with @ref add_guaranteed_generator "", etc.
|
|
1557
|
+
*/
|
|
1558
|
+
void simplify()
|
|
1559
|
+
{
|
|
1560
|
+
if constexpr (Ensure1Criticality) {
|
|
1561
|
+
return;
|
|
1562
|
+
} else {
|
|
1563
|
+
std::size_t end = 0;
|
|
1564
|
+
std::vector<int> indices(num_generators());
|
|
1565
|
+
std::iota(indices.begin(), indices.end(), 0);
|
|
1566
|
+
|
|
1567
|
+
for (std::size_t curr = 0; curr < num_generators(); ++curr) {
|
|
1568
|
+
if (_generator_can_be_added(
|
|
1569
|
+
generators_.begin() + generator_view_.mapping()(indices[curr], 0), 0, end, indices)) {
|
|
1570
|
+
std::swap(indices[end], indices[curr]);
|
|
1571
|
+
++end;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
indices.resize(end);
|
|
1576
|
+
_build_from(indices);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* @brief Removes all empty generators from the filtration value. If @p include_infinities is true, it also
|
|
1582
|
+
* removes the generators at infinity or minus infinity. As empty generators are not possible (except if number of
|
|
1583
|
+
* parameters is 0), the method does nothing except sorting the set of generators if @p include_infinities is false.
|
|
1584
|
+
* Exists mostly for interface purposes.
|
|
1585
|
+
* If the set of generators is empty after removals, it is set to minus infinity if `Co` is false or to infinity
|
|
1586
|
+
* if `Co` is true.
|
|
1587
|
+
*
|
|
1588
|
+
* @warning If the resulting set of generators is not minimal after the removals/sorting, some methods will have an
|
|
1589
|
+
* undefined behaviour. Be sure to call @ref simplify() before using them.
|
|
1590
|
+
*
|
|
1591
|
+
* @param include_infinities If true, removes also infinity values.
|
|
1592
|
+
*/
|
|
1593
|
+
void remove_empty_generators(bool include_infinities = false)
|
|
1594
|
+
{
|
|
1595
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
1596
|
+
if constexpr (Ensure1Criticality) {
|
|
1597
|
+
if (!include_infinities) return;
|
|
1598
|
+
bool allNaN = true, allInf = true;
|
|
1599
|
+
bool toEmpty = true;
|
|
1600
|
+
for (size_type p = 0; p < num_parameters() && toEmpty; ++p) {
|
|
1601
|
+
if (!_is_nan(generators_[p])) allNaN = false;
|
|
1602
|
+
if constexpr (Co) {
|
|
1603
|
+
if (generators_[p] != T_m_inf) allInf = false;
|
|
1604
|
+
} else {
|
|
1605
|
+
if (generators_[p] != T_inf) allInf = false;
|
|
1606
|
+
}
|
|
1607
|
+
if (!allNaN && !allInf) toEmpty = false;
|
|
1608
|
+
}
|
|
1609
|
+
if (toEmpty) generators_.clear();
|
|
1610
|
+
} else {
|
|
1611
|
+
std::vector<int> indices;
|
|
1612
|
+
indices.reserve(num_generators());
|
|
1613
|
+
for (int i = 0; i < static_cast<int>(num_generators()); ++i) {
|
|
1614
|
+
if (!include_infinities || _is_finite(i)) indices.push_back(i);
|
|
1615
|
+
}
|
|
1616
|
+
_build_from(indices); // sorts
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
if (generators_.empty()) {
|
|
1620
|
+
generators_.resize(num_parameters(), _get_default_value());
|
|
1621
|
+
generator_view_.update_data(generators_.data()); // in case it was relocated
|
|
1622
|
+
generator_view_.update_extent(0, 1);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* @brief Sets each generator of the filtration value to the least common upper bound between it and the given value.
|
|
1628
|
+
*
|
|
1629
|
+
* More formally, it pushes the current generator to the cone \f$ \{ y \in \mathbb R^n : y \ge x \} \f$
|
|
1630
|
+
* originating in \f$ x \f$. The resulting value corresponds to the intersection of both
|
|
1631
|
+
* cones: \f$ \mathrm{this} = \min \{ y \in \mathbb R^n : y \ge this \} \cap \{ y \in \mathbb R^n : y \ge x \} \f$.
|
|
1632
|
+
*
|
|
1633
|
+
* @tparam GeneratorRange Range of elements convertible to `T`. Must have a begin(), end() and size() method.
|
|
1634
|
+
* @param x Range towards to push. Has to have as many elements than @ref num_parameters().
|
|
1635
|
+
* @param exclude_infinite_values If true, values at infinity or minus infinity are not affected.
|
|
1636
|
+
* @return true If the filtration value was actually modified.
|
|
1637
|
+
* @return false Otherwise.
|
|
1638
|
+
*/
|
|
1639
|
+
template <class GeneratorRange = std::initializer_list<value_type>,
|
|
1640
|
+
class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
|
|
1641
|
+
bool push_to_least_common_upper_bound(const GeneratorRange &x, bool exclude_infinite_values = false)
|
|
1642
|
+
{
|
|
1643
|
+
GUDHI_CHECK(x.size() == num_parameters(),
|
|
1644
|
+
std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
|
|
1645
|
+
|
|
1646
|
+
bool xIsInf = true, xIsMinusInf = true, xIsNaN = true;
|
|
1647
|
+
bool thisIsInf = true, thisIsMinusInf = true, thisIsNaN = true;
|
|
1648
|
+
|
|
1649
|
+
// if one is not finite, we can avoid the heavy simplification process
|
|
1650
|
+
_get_infinity_statuses(generator_view_, x, thisIsInf, thisIsMinusInf, thisIsNaN, xIsInf, xIsMinusInf, xIsNaN);
|
|
1651
|
+
|
|
1652
|
+
if (thisIsInf || thisIsNaN || xIsNaN || xIsMinusInf || (xIsInf && exclude_infinite_values)) return false;
|
|
1653
|
+
|
|
1654
|
+
if (xIsInf || thisIsMinusInf) {
|
|
1655
|
+
generators_ = Underlying_container(x.begin(), x.end());
|
|
1656
|
+
generator_view_.update_data(generators_.data()); // in case it was relocated
|
|
1657
|
+
generator_view_.update_extent(0, 1);
|
|
1658
|
+
return true;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
bool modified = false;
|
|
1662
|
+
|
|
1663
|
+
auto push_generator_value = [&](T &val, T valX) {
|
|
1664
|
+
if (exclude_infinite_values && (valX == T_inf || valX == T_m_inf)) return;
|
|
1665
|
+
modified |= val < valX;
|
|
1666
|
+
val = valX > val ? valX : val;
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
1670
|
+
if constexpr (Ensure1Criticality) {
|
|
1671
|
+
auto it = x.begin();
|
|
1672
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1673
|
+
push_generator_value(generators_[p], *it);
|
|
1674
|
+
++it;
|
|
1675
|
+
}
|
|
1676
|
+
} else {
|
|
1677
|
+
for (size_type g = 0; g < num_generators(); ++g) {
|
|
1678
|
+
auto it = x.begin();
|
|
1679
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1680
|
+
push_generator_value(generator_view_(g, p), *it);
|
|
1681
|
+
++it;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
if (modified && num_generators() > 1) simplify();
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
return modified;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* @brief Sets each generator of the filtration value to the greatest common lower bound between it and the given
|
|
1693
|
+
* value.
|
|
1694
|
+
*
|
|
1695
|
+
* More formally, it pulls the current generator to the cone \f$ \{ y \in \mathbb R^n : y \le x \} \f$
|
|
1696
|
+
* originating in \f$ x \f$. The resulting value corresponds to the intersection of both
|
|
1697
|
+
* cones: \f$ \mathrm{this} = \min \{ y \in \mathbb R^n : y \le this \} \cap \{ y \in \mathbb R^n : y \le x \} \f$.
|
|
1698
|
+
*
|
|
1699
|
+
* @tparam GeneratorRange Range of elements convertible to `T`. Must have a begin(), end() and size() method.
|
|
1700
|
+
* @param x Range towards to pull. Has to have as many elements than @ref num_parameters().
|
|
1701
|
+
* @param exclude_infinite_values If true, values at infinity or minus infinity are not affected.
|
|
1702
|
+
* @return true If the filtration value was actually modified.
|
|
1703
|
+
* @return false Otherwise.
|
|
1704
|
+
*/
|
|
1705
|
+
template <class GeneratorRange = std::initializer_list<value_type>,
|
|
1706
|
+
class = std::enable_if_t<RangeTraits<GeneratorRange>::has_begin> >
|
|
1707
|
+
bool pull_to_greatest_common_lower_bound(const GeneratorRange &x, bool exclude_infinite_values = false)
|
|
1708
|
+
{
|
|
1709
|
+
GUDHI_CHECK(x.size() == num_parameters(),
|
|
1710
|
+
std::invalid_argument("Wrong range size. Should correspond to the number of parameters."));
|
|
1711
|
+
|
|
1712
|
+
bool xIsInf = true, xIsMinusInf = true, xIsNaN = true;
|
|
1713
|
+
bool thisIsInf = true, thisIsMinusInf = true, thisIsNaN = true;
|
|
1714
|
+
|
|
1715
|
+
// if one is not finite, we can avoid the heavy simplification process
|
|
1716
|
+
_get_infinity_statuses(generator_view_, x, thisIsInf, thisIsMinusInf, thisIsNaN, xIsInf, xIsMinusInf, xIsNaN);
|
|
1717
|
+
|
|
1718
|
+
if (xIsInf || thisIsNaN || xIsNaN || thisIsMinusInf || (xIsMinusInf && exclude_infinite_values)) return false;
|
|
1719
|
+
|
|
1720
|
+
if (thisIsInf || xIsMinusInf) {
|
|
1721
|
+
generators_ = Underlying_container(x.begin(), x.end());
|
|
1722
|
+
generator_view_.update_data(generators_.data()); // in case it was relocated
|
|
1723
|
+
generator_view_.update_extent(0, 1);
|
|
1724
|
+
return true;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
bool modified = false;
|
|
1728
|
+
|
|
1729
|
+
auto pull_generator_value = [&](T &val, T valX) {
|
|
1730
|
+
if (exclude_infinite_values && (valX == T_inf || valX == T_m_inf)) return;
|
|
1731
|
+
modified |= val > valX;
|
|
1732
|
+
val = valX < val ? valX : val;
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
1736
|
+
if constexpr (Ensure1Criticality) {
|
|
1737
|
+
auto it = x.begin();
|
|
1738
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1739
|
+
pull_generator_value(generators_[p], *it);
|
|
1740
|
+
++it;
|
|
1741
|
+
}
|
|
1742
|
+
} else {
|
|
1743
|
+
for (size_type g = 0; g < num_generators(); ++g) {
|
|
1744
|
+
auto it = x.begin();
|
|
1745
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1746
|
+
pull_generator_value(generator_view_(g, p), *it);
|
|
1747
|
+
++it;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
if (modified && num_generators() > 1) simplify();
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
return modified;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/**
|
|
1758
|
+
* @brief Projects the filtration value into the given grid. If @p coordinate is false, the entries are set to
|
|
1759
|
+
* the nearest upper bound value with the same parameter in the grid. Otherwise, the entries are set to the indices
|
|
1760
|
+
* of those nearest upper bound values.
|
|
1761
|
+
* The grid has to be represented as a vector of ordered ranges of values convertible into `T`. An index
|
|
1762
|
+
* \f$ i \f$ of the vector corresponds to the same parameter as the index \f$ i \f$ in a generator of the filtration
|
|
1763
|
+
* value. The ranges correspond to the possible values of the parameters, ordered by increasing value, forming
|
|
1764
|
+
* therefore all together a 2D grid.
|
|
1765
|
+
*
|
|
1766
|
+
* @tparam OneDimArray A range of values convertible into `T` ordered by increasing value. Has to implement
|
|
1767
|
+
* a begin, end and operator[] method.
|
|
1768
|
+
* @param grid Vector of @p OneDimArray with size at least number of filtration parameters.
|
|
1769
|
+
* @param coordinate If true, the values are set to the coordinates of the projection in the grid. If false,
|
|
1770
|
+
* the values are set to the values at the coordinates of the projection.
|
|
1771
|
+
*/
|
|
1772
|
+
template <typename OneDimArray>
|
|
1773
|
+
void project_onto_grid(const std::vector<OneDimArray> &grid, bool coordinate = true)
|
|
1774
|
+
{
|
|
1775
|
+
GUDHI_CHECK(
|
|
1776
|
+
grid.size() >= num_parameters(),
|
|
1777
|
+
std::invalid_argument("The grid should not be smaller than the number of parameters in the filtration value."));
|
|
1778
|
+
|
|
1779
|
+
auto project_generator_value = [&](T &val, const OneDimArray &filtration) {
|
|
1780
|
+
auto v = static_cast<typename OneDimArray::value_type>(val);
|
|
1781
|
+
auto d = std::distance(filtration.begin(), std::lower_bound(filtration.begin(), filtration.end(), v));
|
|
1782
|
+
if (d != 0 && std::abs(v - filtration[d]) > std::abs(v - filtration[d - 1])) {
|
|
1783
|
+
--d;
|
|
1784
|
+
}
|
|
1785
|
+
val = coordinate ? static_cast<T>(d) : static_cast<T>(filtration[d]);
|
|
1786
|
+
};
|
|
1787
|
+
|
|
1788
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
1789
|
+
if constexpr (Ensure1Criticality) {
|
|
1790
|
+
#ifdef GUDHI_USE_TBB
|
|
1791
|
+
tbb::parallel_for(size_type(0), num_parameters(), [&](size_type p){
|
|
1792
|
+
project_generator_value(generators_[p], grid[p]);
|
|
1793
|
+
});
|
|
1794
|
+
#else
|
|
1795
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1796
|
+
project_generator_value(generators_[p], grid[p]);
|
|
1797
|
+
}
|
|
1798
|
+
#endif
|
|
1799
|
+
} else {
|
|
1800
|
+
|
|
1801
|
+
#ifdef GUDHI_USE_TBB
|
|
1802
|
+
|
|
1803
|
+
tbb::parallel_for(size_type(0), num_generators(), [&](size_type g) {
|
|
1804
|
+
tbb::parallel_for(size_type(0), num_parameters(), [&](size_type p) {
|
|
1805
|
+
project_generator_value(generator_view_(g, p), grid[p]);
|
|
1806
|
+
});
|
|
1807
|
+
});
|
|
1808
|
+
#else
|
|
1809
|
+
for (size_type g = 0; g < num_generators(); ++g) {
|
|
1810
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
1811
|
+
project_generator_value(generator_view_(g, p), grid[p]);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
#endif
|
|
1815
|
+
|
|
1816
|
+
if (!coordinate && num_generators() > 1) simplify();
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// FONCTIONNALITIES
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* @brief Returns a generator with the minimal values of all parameters in any generator of the given filtration
|
|
1824
|
+
* value. That is, the greatest lower bound of all generators.
|
|
1825
|
+
*/
|
|
1826
|
+
friend Multi_parameter_filtration factorize_below(const Multi_parameter_filtration &f)
|
|
1827
|
+
{
|
|
1828
|
+
if (f.num_generators() <= 1) return f;
|
|
1829
|
+
|
|
1830
|
+
bool nan = true;
|
|
1831
|
+
Underlying_container result(f.num_parameters(), T_inf);
|
|
1832
|
+
for (size_type p = 0; p < f.num_parameters(); ++p) {
|
|
1833
|
+
for (size_type g = 0; g < f.num_generators(); ++g) {
|
|
1834
|
+
T val = f(g, p);
|
|
1835
|
+
if (!_is_nan(val)) {
|
|
1836
|
+
nan = false;
|
|
1837
|
+
result[p] = val < result[p] ? val : result[p];
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
if (nan)
|
|
1841
|
+
result[p] = std::numeric_limits<T>::quiet_NaN();
|
|
1842
|
+
else
|
|
1843
|
+
nan = true;
|
|
1844
|
+
}
|
|
1845
|
+
return Multi_parameter_filtration(std::move(result), f.num_parameters());
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/**
|
|
1849
|
+
* @brief Returns a generator with the maximal values of all parameters in any generator of the given filtration
|
|
1850
|
+
* value. That is, the least upper bound of all generators.
|
|
1851
|
+
*/
|
|
1852
|
+
friend Multi_parameter_filtration factorize_above(const Multi_parameter_filtration &f)
|
|
1853
|
+
{
|
|
1854
|
+
if (f.num_generators() <= 1) return f;
|
|
1855
|
+
|
|
1856
|
+
bool nan = true;
|
|
1857
|
+
Underlying_container result(f.num_parameters(), T_m_inf);
|
|
1858
|
+
for (size_type p = 0; p < f.num_parameters(); ++p) {
|
|
1859
|
+
for (size_type g = 0; g < f.num_generators(); ++g) {
|
|
1860
|
+
T val = f(g, p);
|
|
1861
|
+
if (!_is_nan(val)) {
|
|
1862
|
+
nan = false;
|
|
1863
|
+
result[p] = val > result[p] ? val : result[p];
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
if (nan)
|
|
1867
|
+
result[p] = std::numeric_limits<T>::quiet_NaN();
|
|
1868
|
+
else
|
|
1869
|
+
nan = true;
|
|
1870
|
+
}
|
|
1871
|
+
return Multi_parameter_filtration(std::move(result), f.num_parameters());
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
/**
|
|
1875
|
+
* @brief Computes the smallest (resp. the greatest if `Co` is true) scalar product of the all generators with the
|
|
1876
|
+
* given vector.
|
|
1877
|
+
*
|
|
1878
|
+
* @tparam U Arithmetic type of the result. Default value: `T`.
|
|
1879
|
+
* @param f Filtration value.
|
|
1880
|
+
* @param x Vector of coefficients.
|
|
1881
|
+
* @return Scalar product of @p f with @p x.
|
|
1882
|
+
*/
|
|
1883
|
+
template <typename U = T>
|
|
1884
|
+
friend U compute_linear_projection(const Multi_parameter_filtration &f, const std::vector<U> &x)
|
|
1885
|
+
{
|
|
1886
|
+
auto project_generator = [&](size_type g) -> U {
|
|
1887
|
+
U projection = 0;
|
|
1888
|
+
std::size_t size = std::min(x.size(), f.num_parameters());
|
|
1889
|
+
for (std::size_t i = 0; i < size; i++) projection += x[i] * static_cast<U>(f(g, i));
|
|
1890
|
+
return projection;
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
if (f.num_generators() == 1) return project_generator(0);
|
|
1894
|
+
|
|
1895
|
+
if constexpr (Co) {
|
|
1896
|
+
U projection = std::numeric_limits<U>::lowest();
|
|
1897
|
+
for (size_type g = 0; g < f.num_generators(); ++g) {
|
|
1898
|
+
// Order in the max important to spread possible NaNs
|
|
1899
|
+
projection = std::max(project_generator(g), projection);
|
|
1900
|
+
}
|
|
1901
|
+
return projection;
|
|
1902
|
+
} else {
|
|
1903
|
+
U projection = std::numeric_limits<U>::max();
|
|
1904
|
+
for (size_type g = 0; g < f.num_generators(); ++g) {
|
|
1905
|
+
// Order in the min important to spread possible NaNs
|
|
1906
|
+
projection = std::min(project_generator(g), projection);
|
|
1907
|
+
}
|
|
1908
|
+
return projection;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* @brief Computes the euclidean distance from the first parameter to the second parameter as the minimum of
|
|
1914
|
+
* all Euclidean distances between a generator of @p f and a generator of @p other.
|
|
1915
|
+
*
|
|
1916
|
+
* @param f Source filtration value.
|
|
1917
|
+
* @param other Target filtration value.
|
|
1918
|
+
* @return Euclidean distance between @p f and @p other.
|
|
1919
|
+
*/
|
|
1920
|
+
template <typename U = T>
|
|
1921
|
+
friend U compute_euclidean_distance_to(const Multi_parameter_filtration &f, const Multi_parameter_filtration &other)
|
|
1922
|
+
{
|
|
1923
|
+
GUDHI_CHECK(f.num_parameters() == other.num_parameters(),
|
|
1924
|
+
std::invalid_argument("We cannot compute the distance between two points of different dimensions."));
|
|
1925
|
+
|
|
1926
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
1927
|
+
if constexpr (Ensure1Criticality) {
|
|
1928
|
+
return _compute_frobenius_norm(f.num_parameters(),
|
|
1929
|
+
[&](size_type p) -> T { return f.generators_[p] - other.generators_[p]; });
|
|
1930
|
+
} else {
|
|
1931
|
+
U res = std::numeric_limits<U>::max();
|
|
1932
|
+
for (size_type g1 = 0; g1 < f.num_generators(); ++g1) {
|
|
1933
|
+
for (size_type g2 = 0; g2 < other.num_generators(); ++g2) {
|
|
1934
|
+
// Euclidean distance as a Frobenius norm with matrix 1 x p and values 'f(g1, p) - other(g2, p)'
|
|
1935
|
+
// Order in the min important to spread possible NaNs
|
|
1936
|
+
res = std::min(
|
|
1937
|
+
_compute_frobenius_norm(f.num_parameters(), [&](size_type p) -> T { return f(g1, p) - other(g2, p); }),
|
|
1938
|
+
res);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return res;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
/**
|
|
1946
|
+
* @brief Computes the norm of the given filtration value.
|
|
1947
|
+
*
|
|
1948
|
+
* The filtration value is seen as a \f$ num_generators x num_parameters \f$ matrix and a standard Frobenius norm
|
|
1949
|
+
* is computed from it: the square root of the sum of the squares of all elements in the matrix.
|
|
1950
|
+
*
|
|
1951
|
+
* @param f Filtration value.
|
|
1952
|
+
* @return The norm of @p f.
|
|
1953
|
+
*/
|
|
1954
|
+
template <typename U = T>
|
|
1955
|
+
friend U compute_norm(const Multi_parameter_filtration &f)
|
|
1956
|
+
{
|
|
1957
|
+
// Frobenius norm with matrix g x p based on Euclidean norm
|
|
1958
|
+
return _compute_frobenius_norm(f.num_entries(), [&](size_type i) -> T { return f.generators_[i]; });
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* @brief Computes the coordinates in the given grid, corresponding to the nearest upper bounds of the entries
|
|
1963
|
+
* in the given filtration value.
|
|
1964
|
+
* The grid has to be represented as a vector of vectors of ordered values convertible into `OutValue`. An index
|
|
1965
|
+
* \f$ i \f$ of the vector corresponds to the same parameter as the index \f$ i \f$ in a generator of the filtration
|
|
1966
|
+
* value. The ranges correspond to the possible values of the parameters, ordered by increasing value, forming
|
|
1967
|
+
* therefore all together a 2D grid.
|
|
1968
|
+
*
|
|
1969
|
+
* @tparam OutValue Signed arithmetic type. Default value: std::int32_t.
|
|
1970
|
+
* @tparam U Type which is convertible into `OutValue`.
|
|
1971
|
+
* @param f Filtration value to project.
|
|
1972
|
+
* @param grid Vector of vectors to project into.
|
|
1973
|
+
* @return Filtration value \f$ out \f$ whose entry correspond to the indices of the projected values. That is,
|
|
1974
|
+
* the projection of \f$ f(g,p) \f$ is \f$ grid[p][out(g,p)] \f$.
|
|
1975
|
+
*/
|
|
1976
|
+
template <typename OutValue = std::int32_t, typename U = T>
|
|
1977
|
+
friend Multi_parameter_filtration<OutValue, Co, Ensure1Criticality> compute_coordinates_in_grid(
|
|
1978
|
+
Multi_parameter_filtration f,
|
|
1979
|
+
const std::vector<std::vector<U> > &grid)
|
|
1980
|
+
{
|
|
1981
|
+
// TODO: by replicating the code of "project_onto_grid", this could be done with just one copy
|
|
1982
|
+
// instead of two. But it is not clear if it is really worth it, i.e., how much the change in type is really
|
|
1983
|
+
// necessary in the use cases. To see later.
|
|
1984
|
+
f.project_onto_grid(grid);
|
|
1985
|
+
if constexpr (std::is_same_v<OutValue, T>) {
|
|
1986
|
+
return f;
|
|
1987
|
+
} else {
|
|
1988
|
+
return f.as_type<OutValue>();
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* @brief Computes the values in the given grid corresponding to the coordinates given by the given filtration
|
|
1994
|
+
* value. That is, if \f$ out \f$ is the result, \f$ out(g,p) = grid[p][f(g,p)] \f$. Assumes therefore, that the
|
|
1995
|
+
* values stored in the filtration value corresponds to indices existing in the given grid.
|
|
1996
|
+
*
|
|
1997
|
+
* @tparam U Signed arithmetic type.
|
|
1998
|
+
* @param f Filtration value storing coordinates compatible with `grid`.
|
|
1999
|
+
* @param grid Vector of vector.
|
|
2000
|
+
* @return Filtration value \f$ out \f$ whose entry correspond to \f$ out(g,p) = grid[p][f(g,p)] \f$.
|
|
2001
|
+
*/
|
|
2002
|
+
template <typename U>
|
|
2003
|
+
friend Multi_parameter_filtration<U, Co, Ensure1Criticality> evaluate_coordinates_in_grid(
|
|
2004
|
+
const Multi_parameter_filtration &f,
|
|
2005
|
+
const std::vector<std::vector<U> > &grid)
|
|
2006
|
+
{
|
|
2007
|
+
GUDHI_CHECK(grid.size() >= f.num_parameters(),
|
|
2008
|
+
std::invalid_argument(
|
|
2009
|
+
"The size of the grid should correspond to the number of parameters in the filtration value."));
|
|
2010
|
+
|
|
2011
|
+
U grid_inf = Multi_parameter_filtration<U, Co, Ensure1Criticality>::T_inf;
|
|
2012
|
+
std::vector<U> outVec(f.num_entries());
|
|
2013
|
+
|
|
2014
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
2015
|
+
if constexpr (Ensure1Criticality) {
|
|
2016
|
+
for (size_type p = 0; p < f.num_parameters(); ++p) {
|
|
2017
|
+
const std::vector<U> &filtration = grid[p];
|
|
2018
|
+
const T &c = f.generators_[p];
|
|
2019
|
+
outVec[p] = (c == T_inf ? grid_inf : filtration[c]);
|
|
2020
|
+
}
|
|
2021
|
+
} else {
|
|
2022
|
+
for (size_type g = 0; g < f.num_generators(); ++g) {
|
|
2023
|
+
for (size_type p = 0; p < f.num_parameters(); ++p) {
|
|
2024
|
+
const std::vector<U> &filtration = grid[p];
|
|
2025
|
+
const T &c = f(g, p);
|
|
2026
|
+
outVec[f.generator_view_.mapping()(g, p)] = (c == T_inf ? grid_inf : filtration[c]);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
Multi_parameter_filtration<U, Co, Ensure1Criticality> out(std::move(outVec), f.num_parameters());
|
|
2032
|
+
if constexpr (!Ensure1Criticality)
|
|
2033
|
+
if (out.num_generators() > 1) out.simplify();
|
|
2034
|
+
return out;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// UTILITIES
|
|
2038
|
+
|
|
2039
|
+
/**
|
|
2040
|
+
* @brief Outstream operator.
|
|
2041
|
+
*/
|
|
2042
|
+
friend std::ostream &operator<<(std::ostream &stream, const Multi_parameter_filtration &f)
|
|
2043
|
+
{
|
|
2044
|
+
const size_type num_gen = f.num_generators();
|
|
2045
|
+
const size_type num_param = f.num_parameters();
|
|
2046
|
+
|
|
2047
|
+
stream << "( k = " << num_gen << " ) ( p = " << num_param << " ) [ ";
|
|
2048
|
+
for (size_type g = 0; g < num_gen; ++g) {
|
|
2049
|
+
stream << "[";
|
|
2050
|
+
for (size_type p = 0; p < num_param; ++p) {
|
|
2051
|
+
stream << f(g, p);
|
|
2052
|
+
if (p < num_param - 1) stream << ", ";
|
|
2053
|
+
}
|
|
2054
|
+
stream << "]";
|
|
2055
|
+
if (g < num_gen - 1) stream << "; ";
|
|
2056
|
+
}
|
|
2057
|
+
stream << " ]";
|
|
2058
|
+
|
|
2059
|
+
return stream;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* @brief Instream operator.
|
|
2064
|
+
*/
|
|
2065
|
+
friend std::istream &operator>>(std::istream &stream, Multi_parameter_filtration &f)
|
|
2066
|
+
{
|
|
2067
|
+
size_type num_gen;
|
|
2068
|
+
size_type num_param;
|
|
2069
|
+
char delimiter;
|
|
2070
|
+
stream >> delimiter; // (
|
|
2071
|
+
stream >> delimiter; // k
|
|
2072
|
+
stream >> delimiter; // =
|
|
2073
|
+
stream >> num_gen;
|
|
2074
|
+
if (!stream.good()) throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_filtration.");
|
|
2075
|
+
stream >> delimiter; // )
|
|
2076
|
+
stream >> delimiter; // (
|
|
2077
|
+
stream >> delimiter; // p
|
|
2078
|
+
stream >> delimiter; // =
|
|
2079
|
+
stream >> num_param;
|
|
2080
|
+
if (!stream.good()) throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_filtration.");
|
|
2081
|
+
f.generators_.resize(num_gen * num_param);
|
|
2082
|
+
f.generator_view_ = Viewer(f.generators_.data(), num_gen, num_param);
|
|
2083
|
+
stream >> delimiter; // )
|
|
2084
|
+
stream >> delimiter; // [
|
|
2085
|
+
if (delimiter != '[') throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_filtration.");
|
|
2086
|
+
if (num_gen == 0) return stream;
|
|
2087
|
+
for (size_type i = 0; i < num_gen; ++i) {
|
|
2088
|
+
stream >> delimiter; // [
|
|
2089
|
+
for (size_type j = 0; j < num_param; ++j) {
|
|
2090
|
+
f(i, j) = _get_value<T>(stream);
|
|
2091
|
+
if (!stream.good())
|
|
2092
|
+
throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_filtration.");
|
|
2093
|
+
stream >> delimiter; // , or last ]
|
|
2094
|
+
}
|
|
2095
|
+
stream >> delimiter; // ; or last ]
|
|
2096
|
+
}
|
|
2097
|
+
if (delimiter != ']') throw std::invalid_argument("Invalid incoming stream format for Multi_parameter_filtration.");
|
|
2098
|
+
|
|
2099
|
+
return stream;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
/**
|
|
2103
|
+
* @brief Returns true if and only if the given filtration value is at plus infinity.
|
|
2104
|
+
*/
|
|
2105
|
+
friend bool is_positive_infinity(const Multi_parameter_filtration &f)
|
|
2106
|
+
{
|
|
2107
|
+
return f.is_plus_inf();
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/**
|
|
2111
|
+
* @brief Adds the generators of the second argument to the first argument. If `Ensure1Criticality` is true,
|
|
2112
|
+
* the method assumes that the two filtration values are comparable, that is, that the result of the union is also
|
|
2113
|
+
* 1-critical. A check for this is only done in Debug Mode, as it is costly.
|
|
2114
|
+
*
|
|
2115
|
+
* @param f1 Filtration value to modify.
|
|
2116
|
+
* @param f2 Filtration value to merge with the first one. Should have the same number of parameters than the other.
|
|
2117
|
+
* @return true If the first argument was actually modified.
|
|
2118
|
+
* @return false Otherwise.
|
|
2119
|
+
*/
|
|
2120
|
+
friend bool unify_lifetimes(Multi_parameter_filtration &f1, const Multi_parameter_filtration &f2)
|
|
2121
|
+
{
|
|
2122
|
+
GUDHI_CHECK(f1.num_parameters() == f2.num_parameters(),
|
|
2123
|
+
std::invalid_argument("Cannot unify two filtration values with different number of parameters."));
|
|
2124
|
+
|
|
2125
|
+
// TODO: verify if this really makes a differences in the 1-critical case, otherwise just keep the general case
|
|
2126
|
+
// if general case is kept: add (num_gen == 1) test to throw if unification is not 1-critical anymore.
|
|
2127
|
+
if constexpr (Ensure1Criticality) {
|
|
2128
|
+
// WARNING: costly check
|
|
2129
|
+
GUDHI_CHECK(
|
|
2130
|
+
f1 <= f2 || f2 <= f1,
|
|
2131
|
+
std::invalid_argument("When 1-critical only, two non-comparable filtration values cannot be unified."));
|
|
2132
|
+
|
|
2133
|
+
if constexpr (Co) {
|
|
2134
|
+
return f1.push_to_least_common_upper_bound(f2);
|
|
2135
|
+
} else {
|
|
2136
|
+
return f1.pull_to_greatest_common_lower_bound(f2);
|
|
2137
|
+
}
|
|
2138
|
+
} else {
|
|
2139
|
+
bool modified = false;
|
|
2140
|
+
for (size_type g = 0; g < f2.num_generators(); ++g) {
|
|
2141
|
+
auto start = f2.begin();
|
|
2142
|
+
start += g * f2.num_parameters();
|
|
2143
|
+
auto end = start + f2.num_parameters();
|
|
2144
|
+
modified |= f1.add_generator(start, end);
|
|
2145
|
+
}
|
|
2146
|
+
return modified;
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
/**
|
|
2151
|
+
* @brief Stores in the first argument the origins of the cones in the intersection of the positive
|
|
2152
|
+
* (negative if `Co` is true) cones generated by the two arguments.
|
|
2153
|
+
*
|
|
2154
|
+
* @param f1 First set of cones which will be modified.
|
|
2155
|
+
* @param f2 Second set of cones. Should have the same number of parameters than the first one.
|
|
2156
|
+
* @return true If the first argument was actually modified.
|
|
2157
|
+
* @return false Otherwise.
|
|
2158
|
+
*/
|
|
2159
|
+
friend bool intersect_lifetimes(Multi_parameter_filtration &f1, const Multi_parameter_filtration &f2)
|
|
2160
|
+
{
|
|
2161
|
+
GUDHI_CHECK(f1.num_parameters() == f2.num_parameters(),
|
|
2162
|
+
std::invalid_argument("Cannot intersect two filtration values with different number of parameters."));
|
|
2163
|
+
|
|
2164
|
+
if constexpr (Ensure1Criticality) {
|
|
2165
|
+
if constexpr (Co) {
|
|
2166
|
+
return f1.pull_to_greatest_common_lower_bound(f2);
|
|
2167
|
+
} else {
|
|
2168
|
+
return f1.push_to_least_common_upper_bound(f2);
|
|
2169
|
+
}
|
|
2170
|
+
} else {
|
|
2171
|
+
bool f2IsInf = true, f2IsMinusInf = true, f2IsNaN = true;
|
|
2172
|
+
bool f1IsInf = true, f1IsMinusInf = true, f1IsNaN = true;
|
|
2173
|
+
|
|
2174
|
+
// if one is not finite, we can avoid the heavy simplification process
|
|
2175
|
+
_get_infinity_statuses(f1.generator_view_, f2, f1IsInf, f1IsMinusInf, f1IsNaN, f2IsInf, f2IsMinusInf, f2IsNaN);
|
|
2176
|
+
|
|
2177
|
+
if (f1IsNaN || f2IsNaN) return false;
|
|
2178
|
+
|
|
2179
|
+
// inf cases first to avoid costly g1 * g2 check
|
|
2180
|
+
if constexpr (Co) {
|
|
2181
|
+
if (f1IsInf) {
|
|
2182
|
+
if (f2IsInf) return false;
|
|
2183
|
+
f1 = f2;
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
if (f1IsMinusInf) {
|
|
2187
|
+
return false;
|
|
2188
|
+
}
|
|
2189
|
+
} else {
|
|
2190
|
+
if (f1IsMinusInf) {
|
|
2191
|
+
if (f2IsMinusInf) return false;
|
|
2192
|
+
f1 = f2;
|
|
2193
|
+
return true;
|
|
2194
|
+
}
|
|
2195
|
+
if (f1IsInf) {
|
|
2196
|
+
return false;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
const size_type num_param = f1.num_parameters();
|
|
2201
|
+
Multi_parameter_filtration res(num_param, Co ? T_m_inf : T_inf);
|
|
2202
|
+
std::vector<T> newGen(num_param);
|
|
2203
|
+
// TODO: see if the order can be used to avoid g1 * g2 add_generator and
|
|
2204
|
+
// perhaps even to replace add_generator by add_guaranteed_generator
|
|
2205
|
+
for (size_type g1 = 0; g1 < f1.num_generators(); ++g1) {
|
|
2206
|
+
for (size_type g2 = 0; g2 < f2.num_generators(); ++g2) {
|
|
2207
|
+
for (size_type p = 0; p < num_param; ++p) {
|
|
2208
|
+
if constexpr (Co) {
|
|
2209
|
+
newGen[p] = std::min(f1(g1, p), f2(g2, p));
|
|
2210
|
+
} else {
|
|
2211
|
+
newGen[p] = std::max(f1(g1, p), f2(g2, p));
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
res.add_generator(newGen);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
swap(f1, res);
|
|
2218
|
+
|
|
2219
|
+
return f1 != res;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
/**
|
|
2224
|
+
* @brief Serialize given value into the buffer at given pointer.
|
|
2225
|
+
*
|
|
2226
|
+
* @param value Value to serialize.
|
|
2227
|
+
* @param start Pointer to the start of the space in the buffer where to store the serialization.
|
|
2228
|
+
* @return End position of the serialization in the buffer.
|
|
2229
|
+
*/
|
|
2230
|
+
friend char *serialize_value_to_char_buffer(const Multi_parameter_filtration &value, char *start)
|
|
2231
|
+
{
|
|
2232
|
+
const size_type length = value.generators_.size();
|
|
2233
|
+
const size_type num_param = value.num_parameters();
|
|
2234
|
+
const std::size_t arg_size = sizeof(T) * length;
|
|
2235
|
+
const std::size_t type_size = sizeof(size_type);
|
|
2236
|
+
memcpy(start, &num_param, type_size);
|
|
2237
|
+
memcpy(start + type_size, &length, type_size);
|
|
2238
|
+
memcpy(start + (type_size * 2), value.generators_.data(), arg_size);
|
|
2239
|
+
return start + arg_size + (type_size * 2);
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
/**
|
|
2243
|
+
* @brief Deserialize the value from a buffer at given pointer and stores it in given value.
|
|
2244
|
+
*
|
|
2245
|
+
* @param value Value to fill with the deserialized filtration value.
|
|
2246
|
+
* @param start Pointer to the start of the space in the buffer where the serialization is stored.
|
|
2247
|
+
* @return End position of the serialization in the buffer.
|
|
2248
|
+
*/
|
|
2249
|
+
friend const char *deserialize_value_from_char_buffer(Multi_parameter_filtration &value, const char *start)
|
|
2250
|
+
{
|
|
2251
|
+
const std::size_t type_size = sizeof(size_type);
|
|
2252
|
+
size_type length;
|
|
2253
|
+
size_type num_param;
|
|
2254
|
+
memcpy(&num_param, start, type_size);
|
|
2255
|
+
memcpy(&length, start + type_size, type_size);
|
|
2256
|
+
std::size_t arg_size = sizeof(T) * length;
|
|
2257
|
+
value.generators_.resize(length);
|
|
2258
|
+
memcpy(value.generators_.data(), start + (type_size * 2), arg_size);
|
|
2259
|
+
value.generator_view_ =
|
|
2260
|
+
Viewer(value.generators_.data(), num_param == 0 ? 0 : value.generators_.size() / num_param, num_param);
|
|
2261
|
+
return start + arg_size + (type_size * 2);
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
/**
|
|
2265
|
+
* @brief Returns the serialization size of the given filtration value.
|
|
2266
|
+
*/
|
|
2267
|
+
friend std::size_t get_serialization_size_of(const Multi_parameter_filtration &value)
|
|
2268
|
+
{
|
|
2269
|
+
return (sizeof(size_type) * 2) + (sizeof(T) * value.num_entries());
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
/**
|
|
2273
|
+
* @brief Plus infinity value of an entry of the filtration value.
|
|
2274
|
+
*/
|
|
2275
|
+
constexpr static const T T_inf = MF_T_inf<T>;
|
|
2276
|
+
|
|
2277
|
+
/**
|
|
2278
|
+
* @brief Minus infinity value of an entry of the filtration value.
|
|
2279
|
+
*/
|
|
2280
|
+
constexpr static const T T_m_inf = MF_T_m_inf<T>;
|
|
2281
|
+
|
|
2282
|
+
private:
|
|
2283
|
+
Underlying_container generators_; /**< Container of the filtration value elements. */
|
|
2284
|
+
Viewer generator_view_; /**< Matrix view of the container. Has to be created after generators_. */
|
|
2285
|
+
|
|
2286
|
+
/**
|
|
2287
|
+
* @brief Default value of an element in the filtration value.
|
|
2288
|
+
*/
|
|
2289
|
+
constexpr static T _get_default_value() { return Co ? T_inf : T_m_inf; }
|
|
2290
|
+
|
|
2291
|
+
/**
|
|
2292
|
+
* @brief Verifies if @p b is strictly contained in the positive cone originating in `a`.
|
|
2293
|
+
*/
|
|
2294
|
+
static bool _strictly_contains(const Viewer &a, size_type g_a, const Viewer &b, size_type g_b)
|
|
2295
|
+
{
|
|
2296
|
+
bool isSame = true;
|
|
2297
|
+
for (auto i = 0U; i < a.extent(1); ++i) {
|
|
2298
|
+
T a_i, b_i;
|
|
2299
|
+
if constexpr (Co) {
|
|
2300
|
+
a_i = b(g_b, i);
|
|
2301
|
+
b_i = a(g_a, i);
|
|
2302
|
+
} else {
|
|
2303
|
+
a_i = a(g_a, i);
|
|
2304
|
+
b_i = b(g_b, i);
|
|
2305
|
+
}
|
|
2306
|
+
if (a_i > b_i || _is_nan(a_i) || _is_nan(b_i)) return false;
|
|
2307
|
+
if (isSame && a_i != b_i) isSame = false;
|
|
2308
|
+
}
|
|
2309
|
+
return !isSame;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
/**
|
|
2313
|
+
* @brief Verifies if @p b is contained in the positive cone originating in `a`.
|
|
2314
|
+
*/
|
|
2315
|
+
static bool _contains(const Viewer &a, size_type g_a, const Viewer &b, size_type g_b)
|
|
2316
|
+
{
|
|
2317
|
+
for (std::size_t i = 0U; i < a.extent(1); ++i) {
|
|
2318
|
+
T a_i, b_i;
|
|
2319
|
+
if constexpr (Co) {
|
|
2320
|
+
a_i = b(g_b, i);
|
|
2321
|
+
b_i = a(g_a, i);
|
|
2322
|
+
} else {
|
|
2323
|
+
a_i = a(g_a, i);
|
|
2324
|
+
b_i = b(g_b, i);
|
|
2325
|
+
}
|
|
2326
|
+
if (a_i > b_i || (!_is_nan(a_i) && _is_nan(b_i)) || (_is_nan(a_i) && !_is_nan(b_i))) return false;
|
|
2327
|
+
}
|
|
2328
|
+
return true;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
/**
|
|
2332
|
+
* @brief Verifies if the first element of @p b strictly dominates the first element of `a`.
|
|
2333
|
+
*/
|
|
2334
|
+
static bool _first_strictly_dominates(const Viewer &a, size_type g_a, const Viewer &b, size_type g_b)
|
|
2335
|
+
{
|
|
2336
|
+
if constexpr (Co) {
|
|
2337
|
+
return a(g_a, 0) < b(g_b, 0);
|
|
2338
|
+
} else {
|
|
2339
|
+
return a(g_a, 0) > b(g_b, 0);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/**
|
|
2344
|
+
* @brief Verifies if the first element of @p b dominates the first element of `a`.
|
|
2345
|
+
*/
|
|
2346
|
+
static bool _first_dominates(const Viewer &a, size_type g_a, const Viewer &b, size_type g_b)
|
|
2347
|
+
{
|
|
2348
|
+
if constexpr (Co) {
|
|
2349
|
+
return a(g_a, 0) <= b(g_b, 0);
|
|
2350
|
+
} else {
|
|
2351
|
+
return a(g_a, 0) >= b(g_b, 0);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
/**
|
|
2356
|
+
* @brief Applies operation on the elements of the filtration value.
|
|
2357
|
+
*/
|
|
2358
|
+
template <class ValueRange, class F, class = std::enable_if_t<RangeTraits<ValueRange>::has_begin> >
|
|
2359
|
+
void _apply_operation(const ValueRange &range, F &&operate)
|
|
2360
|
+
{
|
|
2361
|
+
auto &view = generator_view_;
|
|
2362
|
+
for (unsigned int g = 0; g < num_generators(); ++g) {
|
|
2363
|
+
auto it = range.begin();
|
|
2364
|
+
for (unsigned int p = 0; p < num_parameters() && it != range.end(); ++p) {
|
|
2365
|
+
std::forward<F>(operate)(view(g, p), *it);
|
|
2366
|
+
++it;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
/**
|
|
2372
|
+
* @brief Applies operation on the elements of the filtration value.
|
|
2373
|
+
*/
|
|
2374
|
+
template <class F>
|
|
2375
|
+
void _apply_operation(const T &val, F &&operate)
|
|
2376
|
+
{
|
|
2377
|
+
auto &gens = generators_;
|
|
2378
|
+
for (unsigned int i = 0; i < gens.size(); ++i) {
|
|
2379
|
+
std::forward<F>(operate)(gens[i], val);
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
template <class GeneratorRange>
|
|
2384
|
+
static void _get_infinity_statuses(const Viewer &a,
|
|
2385
|
+
const GeneratorRange &b,
|
|
2386
|
+
bool &aIsInf,
|
|
2387
|
+
bool &aIsMinusInf,
|
|
2388
|
+
bool &aIsNaN,
|
|
2389
|
+
bool &bIsInf,
|
|
2390
|
+
bool &bIsMinusInf,
|
|
2391
|
+
bool &bIsNaN)
|
|
2392
|
+
{
|
|
2393
|
+
auto itB = b.begin();
|
|
2394
|
+
for (std::size_t i = 0; i < a.extent(1); ++i) {
|
|
2395
|
+
if (a(0, i) != T_inf) aIsInf = false;
|
|
2396
|
+
if (a(0, i) != T_m_inf) aIsMinusInf = false;
|
|
2397
|
+
if (!_is_nan(a(0, i))) aIsNaN = false;
|
|
2398
|
+
if (*itB != T_inf) bIsInf = false;
|
|
2399
|
+
if (*itB != T_m_inf) bIsMinusInf = false;
|
|
2400
|
+
if (!_is_nan(*itB)) bIsNaN = false;
|
|
2401
|
+
if (!aIsInf && !aIsMinusInf && !aIsNaN && !bIsInf && !bIsMinusInf && !bIsNaN) return;
|
|
2402
|
+
++itB;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
enum class Rel : std::uint8_t { EQUAL, DOMINATES, IS_DOMINATED, NONE };
|
|
2407
|
+
|
|
2408
|
+
template <class Iterator>
|
|
2409
|
+
static Rel _get_domination_relation(const Viewer &a, size_type g_a, Iterator itB)
|
|
2410
|
+
{
|
|
2411
|
+
bool equal = true;
|
|
2412
|
+
bool allGreater = true;
|
|
2413
|
+
bool allSmaller = true;
|
|
2414
|
+
bool allNaNA = true;
|
|
2415
|
+
bool allNaNB = true;
|
|
2416
|
+
for (unsigned int i = 0; i < a.extent(1); ++i) {
|
|
2417
|
+
if (a(g_a, i) < *itB) {
|
|
2418
|
+
if (!allSmaller) return Rel::NONE;
|
|
2419
|
+
equal = false;
|
|
2420
|
+
allGreater = false;
|
|
2421
|
+
} else if (a(g_a, i) > *itB) {
|
|
2422
|
+
if (!allGreater) return Rel::NONE;
|
|
2423
|
+
equal = false;
|
|
2424
|
+
allSmaller = false;
|
|
2425
|
+
}
|
|
2426
|
+
if (!_is_nan(a(g_a, i))) allNaNA = false;
|
|
2427
|
+
if (!_is_nan(*itB)) allNaNB = false;
|
|
2428
|
+
++itB;
|
|
2429
|
+
}
|
|
2430
|
+
if (allNaNA || allNaNB) return Rel::IS_DOMINATED;
|
|
2431
|
+
if (equal) return Rel::EQUAL;
|
|
2432
|
+
|
|
2433
|
+
if constexpr (Co) {
|
|
2434
|
+
if (allSmaller) return Rel::DOMINATES;
|
|
2435
|
+
return Rel::IS_DOMINATED;
|
|
2436
|
+
} else {
|
|
2437
|
+
if (allGreater) return Rel::DOMINATES;
|
|
2438
|
+
return Rel::IS_DOMINATED;
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
/**
|
|
2443
|
+
* @brief Verifies how x can be added as a new generator with respect to an already existing generator, represented
|
|
2444
|
+
* by `indices[curr]`. If x is dominated by or is equal to `indices[curr]`, it cannot be added. If it dominates
|
|
2445
|
+
* `indices[curr]`, it has to replace `indices[curr]`. If there is no relation between both, `indices[curr]` has
|
|
2446
|
+
* no influence on the addition of x.
|
|
2447
|
+
*
|
|
2448
|
+
* Assumes between 'curr' and 'end' everything is simplified:
|
|
2449
|
+
* no nan values and if there is an inf/-inf, then 'end - curr == 1'.
|
|
2450
|
+
*/
|
|
2451
|
+
template <class Iterator>
|
|
2452
|
+
bool _generator_can_be_added(Iterator x, std::size_t curr, std::size_t &end, std::vector<int> &indices)
|
|
2453
|
+
{
|
|
2454
|
+
while (curr != end) {
|
|
2455
|
+
Rel res = _get_domination_relation(generator_view_, indices[curr], x);
|
|
2456
|
+
if (res == Rel::IS_DOMINATED || res == Rel::EQUAL) return false; // x dominates or is equal
|
|
2457
|
+
if (res == Rel::DOMINATES) { // x is dominated
|
|
2458
|
+
--end;
|
|
2459
|
+
std::swap(indices[curr], indices[end]);
|
|
2460
|
+
} else { // no relation
|
|
2461
|
+
++curr;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return true;
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/**
|
|
2468
|
+
* @brief Rebuild the generators from the given set.
|
|
2469
|
+
*/
|
|
2470
|
+
template <class Iterator>
|
|
2471
|
+
void _build_from(std::vector<int> &indices, const int newIndex, Iterator xStart, Iterator xEnd)
|
|
2472
|
+
{
|
|
2473
|
+
auto comp = [&](int g1, int g2) -> bool {
|
|
2474
|
+
if (g1 == g2) {
|
|
2475
|
+
return false;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
if (g1 == newIndex) {
|
|
2479
|
+
auto it = xStart;
|
|
2480
|
+
for (size_type i = 0; i < num_parameters(); ++i) {
|
|
2481
|
+
T v = generator_view_(g2, i);
|
|
2482
|
+
if (*it != v) {
|
|
2483
|
+
if (_is_nan(v)) return true;
|
|
2484
|
+
return *it < v;
|
|
2485
|
+
}
|
|
2486
|
+
++it;
|
|
2487
|
+
}
|
|
2488
|
+
return false;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
if (g2 == newIndex) {
|
|
2492
|
+
auto it = xStart;
|
|
2493
|
+
for (size_type i = 0; i < num_parameters(); ++i) {
|
|
2494
|
+
T v = generator_view_(g1, i);
|
|
2495
|
+
if (v != *it) {
|
|
2496
|
+
if (_is_nan(*it)) return true;
|
|
2497
|
+
return v < *it;
|
|
2498
|
+
}
|
|
2499
|
+
++it;
|
|
2500
|
+
}
|
|
2501
|
+
return false;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
for (size_type i = 0; i < num_parameters(); ++i) {
|
|
2505
|
+
T v1 = generator_view_(g1, i);
|
|
2506
|
+
T v2 = generator_view_(g2, i);
|
|
2507
|
+
if (v1 != v2) {
|
|
2508
|
+
if (_is_nan(v2)) return true;
|
|
2509
|
+
return v1 < v2;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
return false;
|
|
2513
|
+
};
|
|
2514
|
+
std::sort(indices.begin(), indices.end(), comp);
|
|
2515
|
+
|
|
2516
|
+
Underlying_container new_container;
|
|
2517
|
+
new_container.reserve((indices.size() + 1) * num_parameters());
|
|
2518
|
+
for (int i : indices) {
|
|
2519
|
+
if (i == newIndex) {
|
|
2520
|
+
new_container.insert(new_container.end(), xStart, xEnd);
|
|
2521
|
+
} else {
|
|
2522
|
+
T *ptr = &generator_view_(i, 0);
|
|
2523
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
2524
|
+
new_container.push_back(*ptr);
|
|
2525
|
+
++ptr;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
generators_.swap(new_container);
|
|
2530
|
+
generator_view_ = Viewer(generators_.data(), generators_.size() / num_parameters(), num_parameters());
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
/**
|
|
2534
|
+
* @brief Rebuild the generators from the given set.
|
|
2535
|
+
*/
|
|
2536
|
+
void _build_from(std::vector<int> &indices)
|
|
2537
|
+
{
|
|
2538
|
+
auto comp = [&](int g1, int g2) -> bool {
|
|
2539
|
+
for (std::size_t i = 0; i < num_parameters(); ++i) {
|
|
2540
|
+
T v1 = generator_view_(g1, i);
|
|
2541
|
+
T v2 = generator_view_(g2, i);
|
|
2542
|
+
if (v1 != v2) {
|
|
2543
|
+
if (_is_nan(v2)) return true;
|
|
2544
|
+
return v1 < v2;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
return false;
|
|
2548
|
+
};
|
|
2549
|
+
std::sort(indices.begin(), indices.end(), comp);
|
|
2550
|
+
|
|
2551
|
+
Underlying_container new_container;
|
|
2552
|
+
new_container.reserve(indices.size() * num_parameters());
|
|
2553
|
+
for (int i : indices) {
|
|
2554
|
+
T *ptr = &generator_view_(i, 0);
|
|
2555
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
2556
|
+
new_container.push_back(*ptr);
|
|
2557
|
+
++ptr;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
generators_.swap(new_container);
|
|
2561
|
+
generator_view_ = Viewer(generators_.data(), generators_.size() / num_parameters(), num_parameters());
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
bool _is_finite(size_type g)
|
|
2565
|
+
{
|
|
2566
|
+
bool isInf = true, isMinusInf = true, isNan = true;
|
|
2567
|
+
for (size_type p = 0; p < num_parameters(); ++p) {
|
|
2568
|
+
T v = generator_view_(g, p);
|
|
2569
|
+
if (v != T_inf) isInf = false;
|
|
2570
|
+
if (v != T_m_inf) isMinusInf = false;
|
|
2571
|
+
if (!_is_nan(v)) isNan = false;
|
|
2572
|
+
if (!isInf && !isMinusInf && !isNan) return true;
|
|
2573
|
+
}
|
|
2574
|
+
return false;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
template <class F, typename U = T>
|
|
2578
|
+
static U _compute_frobenius_norm(size_type number_of_elements, F &&norm)
|
|
2579
|
+
{
|
|
2580
|
+
if (number_of_elements == 1) return std::forward<F>(norm)(0);
|
|
2581
|
+
|
|
2582
|
+
U out = 0;
|
|
2583
|
+
for (size_type p = 0; p < number_of_elements; ++p) {
|
|
2584
|
+
T v = std::forward<F>(norm)(p);
|
|
2585
|
+
out += v * v;
|
|
2586
|
+
}
|
|
2587
|
+
if constexpr (std::is_integral_v<U>) {
|
|
2588
|
+
// to avoid Windows issue that don't know how to cast integers for cmath methods
|
|
2589
|
+
return std::sqrt(static_cast<double>(out));
|
|
2590
|
+
} else {
|
|
2591
|
+
return std::sqrt(out);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2596
|
+
} // namespace Gudhi::multi_filtration
|
|
2597
|
+
|
|
2598
|
+
namespace std {
|
|
2599
|
+
|
|
2600
|
+
template <typename T, bool Co, bool Ensure1Criticality>
|
|
2601
|
+
class numeric_limits<Gudhi::multi_filtration::Multi_parameter_filtration<T, Co, Ensure1Criticality> >
|
|
2602
|
+
{
|
|
2603
|
+
public:
|
|
2604
|
+
using Filtration_value = Gudhi::multi_filtration::Multi_parameter_filtration<T, Co, Ensure1Criticality>;
|
|
2605
|
+
|
|
2606
|
+
static constexpr bool has_infinity = true;
|
|
2607
|
+
static constexpr bool has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
|
|
2608
|
+
|
|
2609
|
+
static constexpr Filtration_value infinity(std::size_t p = 1) noexcept { return Filtration_value::inf(p); };
|
|
2610
|
+
|
|
2611
|
+
// non-standard
|
|
2612
|
+
static constexpr Filtration_value minus_infinity(std::size_t p = 1) noexcept
|
|
2613
|
+
{
|
|
2614
|
+
return Filtration_value::minus_inf(p);
|
|
2615
|
+
};
|
|
2616
|
+
|
|
2617
|
+
static constexpr Filtration_value max() noexcept(false)
|
|
2618
|
+
{
|
|
2619
|
+
throw std::logic_error(
|
|
2620
|
+
"The max value cannot be represented with no finite numbers of parameters."
|
|
2621
|
+
"Use `max(number_of_parameters)` instead");
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
static constexpr Filtration_value max(std::size_t p) noexcept
|
|
2625
|
+
{
|
|
2626
|
+
return Filtration_value(p, std::numeric_limits<T>::max());
|
|
2627
|
+
};
|
|
2628
|
+
|
|
2629
|
+
static constexpr Filtration_value lowest(std::size_t p = 1) noexcept { return Filtration_value::minus_inf(p); };
|
|
2630
|
+
|
|
2631
|
+
static constexpr Filtration_value quiet_NaN(std::size_t p = 1) noexcept(false)
|
|
2632
|
+
{
|
|
2633
|
+
if constexpr (std::numeric_limits<T>::has_quiet_NaN) {
|
|
2634
|
+
return Filtration_value::nan(p);
|
|
2635
|
+
} else {
|
|
2636
|
+
throw std::logic_error("Does not have a NaN value.");
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
};
|
|
2640
|
+
|
|
2641
|
+
} // namespace std
|
|
2642
|
+
|
|
2643
|
+
#endif // MF_MULTI_PARAMETER_FILTRATION_H_
|