multipers 2.3.3b6__cp310-cp310-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.
Potentially problematic release.
This version of multipers might be problematic. Click here for more details.
- multipers/.dylibs/libc++.1.0.dylib +0 -0
- multipers/.dylibs/libtbb.12.16.dylib +0 -0
- multipers/__init__.py +33 -0
- multipers/_signed_measure_meta.py +453 -0
- multipers/_slicer_meta.py +211 -0
- multipers/array_api/__init__.py +45 -0
- multipers/array_api/numpy.py +41 -0
- multipers/array_api/torch.py +58 -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 +229 -0
- multipers/filtration_conversions.pxd.tp +84 -0
- multipers/filtrations/__init__.py +18 -0
- multipers/filtrations/density.py +574 -0
- multipers/filtrations/filtrations.py +361 -0
- multipers/filtrations.pxd +224 -0
- multipers/function_rips.cpython-310-darwin.so +0 -0
- multipers/function_rips.pyx +105 -0
- multipers/grids.cpython-310-darwin.so +0 -0
- multipers/grids.pyx +433 -0
- multipers/gudhi/Persistence_slices_interface.h +132 -0
- multipers/gudhi/Simplex_tree_interface.h +239 -0
- multipers/gudhi/Simplex_tree_multi_interface.h +551 -0
- multipers/gudhi/cubical_to_boundary.h +59 -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 +45 -0
- multipers/gudhi/gudhi/Fields/Multi_field.h +484 -0
- multipers/gudhi/gudhi/Fields/Multi_field_operators.h +455 -0
- multipers/gudhi/gudhi/Fields/Multi_field_shared.h +450 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small.h +531 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_operators.h +507 -0
- multipers/gudhi/gudhi/Fields/Multi_field_small_shared.h +531 -0
- multipers/gudhi/gudhi/Fields/Z2_field.h +355 -0
- multipers/gudhi/gudhi/Fields/Z2_field_operators.h +376 -0
- multipers/gudhi/gudhi/Fields/Zp_field.h +420 -0
- multipers/gudhi/gudhi/Fields/Zp_field_operators.h +400 -0
- multipers/gudhi/gudhi/Fields/Zp_field_shared.h +418 -0
- multipers/gudhi/gudhi/Flag_complex_edge_collapser.h +337 -0
- multipers/gudhi/gudhi/Matrix.h +2107 -0
- multipers/gudhi/gudhi/Multi_critical_filtration.h +1038 -0
- multipers/gudhi/gudhi/Multi_persistence/Box.h +174 -0
- multipers/gudhi/gudhi/Multi_persistence/Line.h +282 -0
- multipers/gudhi/gudhi/Off_reader.h +173 -0
- multipers/gudhi/gudhi/One_critical_filtration.h +1441 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix.h +769 -0
- multipers/gudhi/gudhi/Persistence_matrix/Base_matrix_with_column_compression.h +686 -0
- multipers/gudhi/gudhi/Persistence_matrix/Boundary_matrix.h +842 -0
- multipers/gudhi/gudhi/Persistence_matrix/Chain_matrix.h +1350 -0
- multipers/gudhi/gudhi/Persistence_matrix/Id_to_index_overlay.h +1105 -0
- multipers/gudhi/gudhi/Persistence_matrix/Position_to_index_overlay.h +859 -0
- multipers/gudhi/gudhi/Persistence_matrix/RU_matrix.h +910 -0
- multipers/gudhi/gudhi/Persistence_matrix/allocators/entry_constructors.h +139 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_pairing.h +230 -0
- multipers/gudhi/gudhi/Persistence_matrix/base_swap.h +211 -0
- multipers/gudhi/gudhi/Persistence_matrix/boundary_cell_position_to_id_mapper.h +60 -0
- multipers/gudhi/gudhi/Persistence_matrix/boundary_face_position_to_id_mapper.h +60 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_pairing.h +136 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_rep_cycles.h +190 -0
- multipers/gudhi/gudhi/Persistence_matrix/chain_vine_swap.h +616 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/chain_column_extra_properties.h +150 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_dimension_holder.h +106 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/column_utilities.h +219 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/entry_types.h +327 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/heap_column.h +1140 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_list_column.h +934 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/intrusive_set_column.h +934 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/list_column.h +980 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/naive_vector_column.h +1092 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/row_access.h +192 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/set_column.h +921 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/small_vector_column.h +1093 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/unordered_set_column.h +1012 -0
- multipers/gudhi/gudhi/Persistence_matrix/columns/vector_column.h +1244 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_dimension_holders.h +186 -0
- multipers/gudhi/gudhi/Persistence_matrix/matrix_row_access.h +164 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_pairing.h +156 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_rep_cycles.h +376 -0
- multipers/gudhi/gudhi/Persistence_matrix/ru_vine_swap.h +540 -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 +745 -0
- multipers/gudhi/gudhi/Points_off_io.h +171 -0
- multipers/gudhi/gudhi/Simple_object_pool.h +69 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_iterators.h +463 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +83 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_siblings.h +106 -0
- multipers/gudhi/gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h +277 -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 +62 -0
- multipers/gudhi/gudhi/Simplex_tree/simplex_tree_options.h +157 -0
- multipers/gudhi/gudhi/Simplex_tree.h +2794 -0
- multipers/gudhi/gudhi/Simplex_tree_multi.h +152 -0
- multipers/gudhi/gudhi/distance_functions.h +62 -0
- multipers/gudhi/gudhi/graph_simplicial_complex.h +104 -0
- multipers/gudhi/gudhi/persistence_interval.h +253 -0
- multipers/gudhi/gudhi/persistence_matrix_options.h +170 -0
- multipers/gudhi/gudhi/reader_utils.h +367 -0
- multipers/gudhi/mma_interface_coh.h +256 -0
- multipers/gudhi/mma_interface_h0.h +223 -0
- multipers/gudhi/mma_interface_matrix.h +293 -0
- multipers/gudhi/naive_merge_tree.h +536 -0
- multipers/gudhi/scc_io.h +310 -0
- multipers/gudhi/truc.h +1403 -0
- multipers/io.cpython-310-darwin.so +0 -0
- multipers/io.pyx +644 -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 +1589 -0
- multipers/ml/sliced_wasserstein.py +461 -0
- multipers/ml/tools.py +113 -0
- multipers/mma_structures.cpython-310-darwin.so +0 -0
- multipers/mma_structures.pxd +128 -0
- multipers/mma_structures.pyx +2786 -0
- multipers/mma_structures.pyx.tp +1094 -0
- multipers/multi_parameter_rank_invariant/diff_helpers.h +84 -0
- multipers/multi_parameter_rank_invariant/euler_characteristic.h +97 -0
- multipers/multi_parameter_rank_invariant/function_rips.h +322 -0
- multipers/multi_parameter_rank_invariant/hilbert_function.h +769 -0
- multipers/multi_parameter_rank_invariant/persistence_slices.h +148 -0
- multipers/multi_parameter_rank_invariant/rank_invariant.h +369 -0
- multipers/multiparameter_edge_collapse.py +41 -0
- multipers/multiparameter_module_approximation/approximation.h +2330 -0
- multipers/multiparameter_module_approximation/combinatory.h +129 -0
- multipers/multiparameter_module_approximation/debug.h +107 -0
- multipers/multiparameter_module_approximation/euler_curves.h +0 -0
- multipers/multiparameter_module_approximation/format_python-cpp.h +286 -0
- multipers/multiparameter_module_approximation/heap_column.h +238 -0
- multipers/multiparameter_module_approximation/images.h +79 -0
- multipers/multiparameter_module_approximation/list_column.h +174 -0
- multipers/multiparameter_module_approximation/list_column_2.h +232 -0
- multipers/multiparameter_module_approximation/ru_matrix.h +347 -0
- multipers/multiparameter_module_approximation/set_column.h +135 -0
- multipers/multiparameter_module_approximation/structure_higher_dim_barcode.h +36 -0
- multipers/multiparameter_module_approximation/unordered_set_column.h +166 -0
- multipers/multiparameter_module_approximation/utilities.h +403 -0
- multipers/multiparameter_module_approximation/vector_column.h +223 -0
- multipers/multiparameter_module_approximation/vector_matrix.h +331 -0
- multipers/multiparameter_module_approximation/vineyards.h +464 -0
- multipers/multiparameter_module_approximation/vineyards_trajectories.h +649 -0
- multipers/multiparameter_module_approximation.cpython-310-darwin.so +0 -0
- multipers/multiparameter_module_approximation.pyx +235 -0
- multipers/pickle.py +90 -0
- multipers/plots.py +456 -0
- multipers/point_measure.cpython-310-darwin.so +0 -0
- multipers/point_measure.pyx +395 -0
- multipers/simplex_tree_multi.cpython-310-darwin.so +0 -0
- multipers/simplex_tree_multi.pxd +134 -0
- multipers/simplex_tree_multi.pyx +10840 -0
- multipers/simplex_tree_multi.pyx.tp +2009 -0
- multipers/slicer.cpython-310-darwin.so +0 -0
- multipers/slicer.pxd +3034 -0
- multipers/slicer.pxd.tp +234 -0
- multipers/slicer.pyx +20481 -0
- multipers/slicer.pyx.tp +1088 -0
- multipers/tensor/tensor.h +672 -0
- multipers/tensor.pxd +13 -0
- multipers/test.pyx +44 -0
- multipers/tests/__init__.py +62 -0
- multipers/torch/__init__.py +1 -0
- multipers/torch/diff_grids.py +240 -0
- multipers/torch/rips_density.py +310 -0
- multipers-2.3.3b6.dist-info/METADATA +128 -0
- multipers-2.3.3b6.dist-info/RECORD +183 -0
- multipers-2.3.3b6.dist-info/WHEEL +6 -0
- multipers-2.3.3b6.dist-info/licenses/LICENSE +21 -0
- multipers-2.3.3b6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2794 @@
|
|
|
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): Clément Maria
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2014 Inria
|
|
6
|
+
*
|
|
7
|
+
* Modification(s):
|
|
8
|
+
* - 2023/02 Vincent Rouvreau: Add de/serialize methods for pickle feature
|
|
9
|
+
* - 2023/07 Clément Maria: Option to link all simplex tree nodes with same label in an intrusive list
|
|
10
|
+
* - 2023/05 Clément Maria: Edge insertion method for flag complexes
|
|
11
|
+
* - 2023/05 Hannah Schreiber: Factorization of expansion methods
|
|
12
|
+
* - 2023/08 Hannah Schreiber (& Clément Maria): Add possibility of stable simplex handles.
|
|
13
|
+
* - 2024/08 Hannah Schreiber: Generalization of the notion of filtration values.
|
|
14
|
+
* - 2024/08 Hannah Schreiber: Addition of customizable copy constructor.
|
|
15
|
+
* - YYYY/MM Author: Description of the modification
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#ifndef SIMPLEX_TREE_H_
|
|
19
|
+
#define SIMPLEX_TREE_H_
|
|
20
|
+
|
|
21
|
+
#include <gudhi/Simplex_tree/simplex_tree_options.h>
|
|
22
|
+
#include <gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h>
|
|
23
|
+
#include <gudhi/Simplex_tree/Simplex_tree_siblings.h>
|
|
24
|
+
#include <gudhi/Simplex_tree/Simplex_tree_iterators.h>
|
|
25
|
+
#include <gudhi/Simplex_tree/Simplex_tree_star_simplex_iterators.h>
|
|
26
|
+
#include <gudhi/Simplex_tree/serialization_utils.h> // for Gudhi::simplex_tree::de/serialize_trivial
|
|
27
|
+
#include <gudhi/Simplex_tree/hooks_simplex_base.h>
|
|
28
|
+
|
|
29
|
+
#include <gudhi/reader_utils.h>
|
|
30
|
+
#include <gudhi/graph_simplicial_complex.h>
|
|
31
|
+
#include <gudhi/Debug_utils.h>
|
|
32
|
+
|
|
33
|
+
#include <boost/container/map.hpp>
|
|
34
|
+
#include <boost/container/flat_map.hpp>
|
|
35
|
+
#include <boost/iterator/transform_iterator.hpp>
|
|
36
|
+
#include <boost/graph/adjacency_list.hpp>
|
|
37
|
+
#include <boost/range/adaptor/reversed.hpp>
|
|
38
|
+
#include <boost/range/adaptor/transformed.hpp>
|
|
39
|
+
#include <boost/range/size.hpp>
|
|
40
|
+
#include <boost/container/static_vector.hpp>
|
|
41
|
+
#include <boost/range/adaptors.hpp>
|
|
42
|
+
|
|
43
|
+
#include <boost/intrusive/list.hpp>
|
|
44
|
+
#include <boost/intrusive/parent_from_member.hpp>
|
|
45
|
+
#include <cstddef>
|
|
46
|
+
|
|
47
|
+
#ifdef GUDHI_USE_TBB
|
|
48
|
+
#include <tbb/parallel_sort.h>
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
#include <utility> // for std::move
|
|
52
|
+
#include <vector>
|
|
53
|
+
#include <functional> // for greater<>
|
|
54
|
+
#include <stdexcept>
|
|
55
|
+
#include <limits> // Inf
|
|
56
|
+
#include <initializer_list>
|
|
57
|
+
#include <algorithm> // for std::max
|
|
58
|
+
#include <iterator> // for std::distance
|
|
59
|
+
#include <type_traits> // for std::conditional
|
|
60
|
+
#include <unordered_map>
|
|
61
|
+
#include <iterator> // for std::prev
|
|
62
|
+
|
|
63
|
+
namespace Gudhi {
|
|
64
|
+
|
|
65
|
+
/** \addtogroup simplex_tree
|
|
66
|
+
* @{
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* \class Extended_simplex_type Simplex_tree.h gudhi/Simplex_tree.h
|
|
71
|
+
* \brief Extended simplex type data structure for representing the type of simplices in an extended filtration.
|
|
72
|
+
*
|
|
73
|
+
* \details The extended simplex type can be either UP (which means
|
|
74
|
+
* that the simplex was present originally, and is thus part of the ascending extended filtration), DOWN (which means
|
|
75
|
+
* that the simplex is the cone of an original simplex, and is thus part of the descending extended filtration) or
|
|
76
|
+
* EXTRA (which means the simplex is the cone point).
|
|
77
|
+
*
|
|
78
|
+
* Details may be found in \cite Cohen-Steiner2009 and section 2.2 in \cite Carriere16.
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
81
|
+
enum class Extended_simplex_type { UP, DOWN, EXTRA };
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* \class Simplex_tree Simplex_tree.h gudhi/Simplex_tree.h
|
|
85
|
+
* \brief Simplex Tree data structure for representing simplicial complexes.
|
|
86
|
+
*
|
|
87
|
+
* \details Every simplex \f$[v_0, \cdots ,v_d]\f$ admits a canonical orientation
|
|
88
|
+
* induced by the order relation on vertices \f$ v_0 < \cdots < v_d \f$.
|
|
89
|
+
*
|
|
90
|
+
* Details may be found in \cite boissonnatmariasimplextreealgorithmica.
|
|
91
|
+
*
|
|
92
|
+
* \implements FilteredComplex
|
|
93
|
+
*
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
template <typename SimplexTreeOptions = Simplex_tree_options_default>
|
|
97
|
+
class Simplex_tree {
|
|
98
|
+
public:
|
|
99
|
+
typedef SimplexTreeOptions Options;
|
|
100
|
+
typedef typename Options::Indexing_tag Indexing_tag;
|
|
101
|
+
/** \brief Type for the value of the filtration function.
|
|
102
|
+
*
|
|
103
|
+
* Must be comparable with <. */
|
|
104
|
+
typedef typename Options::Filtration_value Filtration_value;
|
|
105
|
+
/** \brief Key associated to each simplex.
|
|
106
|
+
*
|
|
107
|
+
* Must be an integer type. */
|
|
108
|
+
typedef typename Options::Simplex_key Simplex_key;
|
|
109
|
+
/** \brief Extra data stored in each simplex. */
|
|
110
|
+
typedef typename Get_simplex_data_type<Options>::type Simplex_data;
|
|
111
|
+
/** \brief Type for the vertex handle.
|
|
112
|
+
*
|
|
113
|
+
* Must be a signed integer type. It admits a total order <. */
|
|
114
|
+
typedef typename Options::Vertex_handle Vertex_handle;
|
|
115
|
+
|
|
116
|
+
/* Type of node in the simplex tree. */
|
|
117
|
+
typedef Simplex_tree_node_explicit_storage<Simplex_tree> Node;
|
|
118
|
+
/* Type of dictionary Vertex_handle -> Node for traversing the simplex tree. */
|
|
119
|
+
// Note: this wastes space when Vertex_handle is 32 bits and Node is aligned on 64 bits. It would be better to use a
|
|
120
|
+
// flat_set (with our own comparator) where we can control the layout of the struct (put Vertex_handle and
|
|
121
|
+
// Simplex_key next to each other).
|
|
122
|
+
typedef typename boost::container::flat_map<Vertex_handle, Node> flat_map;
|
|
123
|
+
// Dictionary::iterator remain valid under insertions and deletions,
|
|
124
|
+
// necessary e.g. when computing oscillating rips zigzag filtrations.
|
|
125
|
+
typedef typename boost::container::map<Vertex_handle, Node> map;
|
|
126
|
+
typedef typename std::conditional<Options::stable_simplex_handles, map, flat_map>::type Dictionary;
|
|
127
|
+
|
|
128
|
+
/** \brief Set of nodes sharing a same parent in the simplex tree. */
|
|
129
|
+
typedef Simplex_tree_siblings<Simplex_tree, Dictionary> Siblings;
|
|
130
|
+
|
|
131
|
+
struct Key_simplex_base_real {
|
|
132
|
+
Key_simplex_base_real() : key_(-1) {}
|
|
133
|
+
|
|
134
|
+
Key_simplex_base_real(Simplex_key k) : key_(k) {}
|
|
135
|
+
|
|
136
|
+
void assign_key(Simplex_key k) { key_ = k; }
|
|
137
|
+
|
|
138
|
+
Simplex_key key() const { return key_; }
|
|
139
|
+
|
|
140
|
+
private:
|
|
141
|
+
Simplex_key key_;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
struct Key_simplex_base_dummy {
|
|
145
|
+
Key_simplex_base_dummy() {}
|
|
146
|
+
|
|
147
|
+
// Undefined so it will not link
|
|
148
|
+
void assign_key(Simplex_key);
|
|
149
|
+
Simplex_key key() const;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
struct Extended_filtration_data {
|
|
153
|
+
Filtration_value minval;
|
|
154
|
+
Filtration_value maxval;
|
|
155
|
+
|
|
156
|
+
Extended_filtration_data() {}
|
|
157
|
+
|
|
158
|
+
Extended_filtration_data(const Filtration_value& vmin, const Filtration_value& vmax) : minval(vmin), maxval(vmax) {}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
typedef typename std::conditional<Options::store_key, Key_simplex_base_real, Key_simplex_base_dummy>::type
|
|
162
|
+
Key_simplex_base;
|
|
163
|
+
|
|
164
|
+
struct Filtration_simplex_base_real {
|
|
165
|
+
Filtration_simplex_base_real() : filt_(0) {}
|
|
166
|
+
|
|
167
|
+
Filtration_simplex_base_real(Filtration_value f) : filt_(f) {}
|
|
168
|
+
|
|
169
|
+
void assign_filtration(const Filtration_value& f) { filt_ = f; }
|
|
170
|
+
|
|
171
|
+
const Filtration_value& filtration() const { return filt_; }
|
|
172
|
+
|
|
173
|
+
Filtration_value& filtration() { return filt_; }
|
|
174
|
+
|
|
175
|
+
static const Filtration_value& get_infinity() { return inf_; }
|
|
176
|
+
|
|
177
|
+
private:
|
|
178
|
+
Filtration_value filt_;
|
|
179
|
+
|
|
180
|
+
inline static const Filtration_value inf_ = std::numeric_limits<Filtration_value>::has_infinity
|
|
181
|
+
? std::numeric_limits<Filtration_value>::infinity()
|
|
182
|
+
: std::numeric_limits<Filtration_value>::max();
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
struct Filtration_simplex_base_dummy {
|
|
186
|
+
Filtration_simplex_base_dummy() {}
|
|
187
|
+
|
|
188
|
+
Filtration_simplex_base_dummy(Filtration_value GUDHI_CHECK_code(f)) {
|
|
189
|
+
GUDHI_CHECK(f == Filtration_value(),
|
|
190
|
+
"filtration value specified in the constructor for a complex that does not store them");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
void assign_filtration(const Filtration_value& GUDHI_CHECK_code(f)) {
|
|
194
|
+
GUDHI_CHECK(f == Filtration_value(), "filtration value assigned for a complex that does not store them");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const Filtration_value& filtration() const { return null_; }
|
|
198
|
+
|
|
199
|
+
private:
|
|
200
|
+
static constexpr const Filtration_value null_ = Filtration_value();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
typedef typename std::conditional<Options::store_filtration,
|
|
204
|
+
Filtration_simplex_base_real,
|
|
205
|
+
Filtration_simplex_base_dummy>::type Filtration_simplex_base;
|
|
206
|
+
|
|
207
|
+
public:
|
|
208
|
+
/** \brief Handle type to a simplex contained in the simplicial complex represented
|
|
209
|
+
* by the simplex tree.
|
|
210
|
+
*
|
|
211
|
+
* They are essentially pointers into internal vectors, and any insertion or removal
|
|
212
|
+
* of a simplex may invalidate any other Simplex_handle in the complex,
|
|
213
|
+
* unless Options::stable_simplex_handles == true. */
|
|
214
|
+
typedef typename Dictionary::iterator Simplex_handle;
|
|
215
|
+
|
|
216
|
+
private:
|
|
217
|
+
typedef typename Dictionary::iterator Dictionary_it;
|
|
218
|
+
typedef typename Dictionary_it::value_type Dit_value_t;
|
|
219
|
+
|
|
220
|
+
struct return_first {
|
|
221
|
+
Vertex_handle operator()(const Dit_value_t& p_sh) const { return p_sh.first; }
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
private:
|
|
225
|
+
/** \brief An iterator for an optimized search for the star of a simplex.
|
|
226
|
+
*
|
|
227
|
+
* \details It requires the Options::link_nodes_by_label to be true and store two
|
|
228
|
+
* extra pointers in each node of the simplex tree. The Nodes of same label are
|
|
229
|
+
* linked in a list.
|
|
230
|
+
*/
|
|
231
|
+
using Optimized_star_simplex_iterator = Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
|
|
232
|
+
/** \brief Range for an optimized search for the star of a simplex. */
|
|
233
|
+
using Optimized_star_simplex_range = boost::iterator_range<Optimized_star_simplex_iterator>;
|
|
234
|
+
|
|
235
|
+
class Fast_cofaces_predicate {
|
|
236
|
+
Simplex_tree* st_;
|
|
237
|
+
int codim_;
|
|
238
|
+
int dim_;
|
|
239
|
+
|
|
240
|
+
public:
|
|
241
|
+
Fast_cofaces_predicate(Simplex_tree* st, int codim, int dim) : st_(st), codim_(codim), dim_(codim + dim) {}
|
|
242
|
+
|
|
243
|
+
bool operator()(const Simplex_handle iter) const {
|
|
244
|
+
if (codim_ == 0)
|
|
245
|
+
// Always true for a star
|
|
246
|
+
return true;
|
|
247
|
+
// Specific coface case
|
|
248
|
+
return dim_ == st_->dimension(iter);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// WARNING: this is safe only because boost::filtered_range is containing a copy of begin and end iterator.
|
|
253
|
+
// This would not be safe if it was containing a pointer to a range (maybe the case for std::views)
|
|
254
|
+
using Optimized_cofaces_simplex_filtered_range =
|
|
255
|
+
boost::filtered_range<Fast_cofaces_predicate, Optimized_star_simplex_range>;
|
|
256
|
+
|
|
257
|
+
/** The largest dimension supported for simplex trees.
|
|
258
|
+
* 40 seems a conservative bound for now, as 2^41 simplices would not fit on the biggest hard-drive. */
|
|
259
|
+
static constexpr int max_dimension() { return 40; }
|
|
260
|
+
|
|
261
|
+
public:
|
|
262
|
+
/** \name Range and iterator types
|
|
263
|
+
*
|
|
264
|
+
* The naming convention is Container_content_(iterator/range). A Container_content_range is
|
|
265
|
+
* essentially an object on which the methods begin() and end() can be called. They both return
|
|
266
|
+
* an object of type Container_content_iterator, and allow the traversal of the range
|
|
267
|
+
* [ begin();end() ).
|
|
268
|
+
* @{ */
|
|
269
|
+
|
|
270
|
+
/** \brief Iterator over the vertices of the simplicial complex.
|
|
271
|
+
*
|
|
272
|
+
* 'value_type' is Vertex_handle. */
|
|
273
|
+
typedef boost::transform_iterator<return_first, Dictionary_it> Complex_vertex_iterator;
|
|
274
|
+
/** \brief Range over the vertices of the simplicial complex. */
|
|
275
|
+
typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
|
|
276
|
+
/** \brief Iterator over the vertices of a simplex.
|
|
277
|
+
*
|
|
278
|
+
* 'value_type' is Vertex_handle. */
|
|
279
|
+
typedef Simplex_tree_simplex_vertex_iterator<Simplex_tree> Simplex_vertex_iterator;
|
|
280
|
+
/** \brief Range over the vertices of a simplex. */
|
|
281
|
+
typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
|
|
282
|
+
/** \brief Range over the cofaces of a simplex. */
|
|
283
|
+
typedef typename std::conditional<Options::link_nodes_by_label,
|
|
284
|
+
Optimized_cofaces_simplex_filtered_range, // faster implementation
|
|
285
|
+
std::vector<Simplex_handle>>::type Cofaces_simplex_range;
|
|
286
|
+
|
|
287
|
+
/** \private
|
|
288
|
+
* static_vector still has some overhead compared to a trivial hand-made version using std::aligned_storage, or
|
|
289
|
+
* compared to reusing a static object. */
|
|
290
|
+
using Static_vertex_vector = boost::container::static_vector<Vertex_handle, max_dimension()>;
|
|
291
|
+
|
|
292
|
+
/** \brief Iterator over the simplices of the boundary of a simplex.
|
|
293
|
+
*
|
|
294
|
+
* 'value_type' is Simplex_handle. */
|
|
295
|
+
typedef Simplex_tree_boundary_simplex_iterator<Simplex_tree> Boundary_simplex_iterator;
|
|
296
|
+
/** \brief Range over the simplices of the boundary of a simplex. */
|
|
297
|
+
typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
|
|
298
|
+
/** \brief Iterator over the simplices of the boundary of a simplex and their opposite vertices.
|
|
299
|
+
*
|
|
300
|
+
* 'value_type' is std::pair<Simplex_handle, Vertex_handle>. */
|
|
301
|
+
typedef Simplex_tree_boundary_opposite_vertex_simplex_iterator<Simplex_tree>
|
|
302
|
+
Boundary_opposite_vertex_simplex_iterator;
|
|
303
|
+
/** \brief Range over the simplices of the boundary of a simplex and their opposite vertices. */
|
|
304
|
+
typedef boost::iterator_range<Boundary_opposite_vertex_simplex_iterator> Boundary_opposite_vertex_simplex_range;
|
|
305
|
+
/** \brief Iterator over the simplices of the simplicial complex.
|
|
306
|
+
*
|
|
307
|
+
* 'value_type' is Simplex_handle. */
|
|
308
|
+
typedef Simplex_tree_complex_simplex_iterator<Simplex_tree> Complex_simplex_iterator;
|
|
309
|
+
/** \brief Range over the simplices of the simplicial complex. */
|
|
310
|
+
typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
|
|
311
|
+
/** \brief Iterator over the simplices of the skeleton of the simplicial complex, for a given
|
|
312
|
+
* dimension.
|
|
313
|
+
*
|
|
314
|
+
* 'value_type' is Simplex_handle. */
|
|
315
|
+
typedef Simplex_tree_skeleton_simplex_iterator<Simplex_tree> Skeleton_simplex_iterator;
|
|
316
|
+
/** \brief Range over the simplices of the skeleton of the simplicial complex, for a given
|
|
317
|
+
* dimension. */
|
|
318
|
+
typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
|
|
319
|
+
/** \brief Range over the simplices of the simplicial complex, ordered by the filtration. */
|
|
320
|
+
typedef std::vector<Simplex_handle> Filtration_simplex_range;
|
|
321
|
+
/** \brief Iterator over the simplices of the simplicial complex, ordered by the filtration.
|
|
322
|
+
*
|
|
323
|
+
* 'value_type' is Simplex_handle. */
|
|
324
|
+
typedef typename Filtration_simplex_range::const_iterator Filtration_simplex_iterator;
|
|
325
|
+
|
|
326
|
+
/* @} */ // end name range and iterator types
|
|
327
|
+
/** \name Range and iterator methods
|
|
328
|
+
* @{ */
|
|
329
|
+
|
|
330
|
+
/** \brief Returns a range over the vertices of the simplicial complex.
|
|
331
|
+
* The order is increasing according to < on Vertex_handles.*/
|
|
332
|
+
Complex_vertex_range complex_vertex_range() {
|
|
333
|
+
return Complex_vertex_range(boost::make_transform_iterator(root_.members_.begin(), return_first()),
|
|
334
|
+
boost::make_transform_iterator(root_.members_.end(), return_first()));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** \brief Returns a range over the simplices of the simplicial complex.
|
|
338
|
+
*
|
|
339
|
+
* In the Simplex_tree, the tree is traverse in a depth-first fashion.
|
|
340
|
+
* Consequently, simplices are ordered according to lexicographic order on the list of
|
|
341
|
+
* Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
|
|
342
|
+
Complex_simplex_range complex_simplex_range() {
|
|
343
|
+
return Complex_simplex_range(Complex_simplex_iterator(this), Complex_simplex_iterator());
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** \brief Returns a range over the simplices of the dim-skeleton of the simplicial complex.
|
|
347
|
+
*
|
|
348
|
+
* The \f$d\f$-skeleton of a simplicial complex \f$\mathbf{K}\f$ is the simplicial complex containing the
|
|
349
|
+
* simplices of \f$\mathbf{K}\f$ of dimension at most \f$d\f$.
|
|
350
|
+
*
|
|
351
|
+
* @param[in] dim The maximal dimension of the simplices in the skeleton.
|
|
352
|
+
*
|
|
353
|
+
* The simplices are ordered according to lexicographic order on the list of
|
|
354
|
+
* Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
|
|
355
|
+
Skeleton_simplex_range skeleton_simplex_range(int dim) {
|
|
356
|
+
return Skeleton_simplex_range(Skeleton_simplex_iterator(this, dim), Skeleton_simplex_iterator());
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** \brief Returns a range over the simplices of the simplicial complex,
|
|
360
|
+
* in the order of the filtration.
|
|
361
|
+
*
|
|
362
|
+
* The filtration is a monotonic function \f$ f: \mathbf{K} \rightarrow \mathbb{R} \f$, i.e. if two simplices
|
|
363
|
+
* \f$\tau\f$ and \f$\sigma\f$ satisfy \f$\tau \subseteq \sigma\f$ then
|
|
364
|
+
* \f$f(\tau) \leq f(\sigma)\f$.
|
|
365
|
+
*
|
|
366
|
+
* The method returns simplices ordered according to increasing filtration values. Ties are
|
|
367
|
+
* resolved by considering inclusion relation (subsimplices appear before their cofaces). If two
|
|
368
|
+
* simplices have same filtration value but are not comparable w.r.t. inclusion, lexicographic
|
|
369
|
+
* order is used.
|
|
370
|
+
*
|
|
371
|
+
* The filtration must be valid. If the filtration has not been initialized yet, the
|
|
372
|
+
* method initializes it (i.e. order the simplices). If the complex has changed since the last time the filtration
|
|
373
|
+
* was initialized, please call `clear_filtration()` or `initialize_filtration()` to recompute it. */
|
|
374
|
+
Filtration_simplex_range const& filtration_simplex_range(Indexing_tag = Indexing_tag()) {
|
|
375
|
+
maybe_initialize_filtration();
|
|
376
|
+
return filtration_vect_;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** \brief Returns a range over the vertices of a simplex.
|
|
380
|
+
*
|
|
381
|
+
* The order in which the vertices are visited is the decreasing order for < on Vertex_handles,
|
|
382
|
+
* which is consequently
|
|
383
|
+
* equal to \f$(-1)^{\text{dim} \sigma}\f$ the canonical orientation on the simplex.
|
|
384
|
+
*/
|
|
385
|
+
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const {
|
|
386
|
+
GUDHI_CHECK(sh != null_simplex(), "empty simplex");
|
|
387
|
+
return Simplex_vertex_range(Simplex_vertex_iterator(this, sh), Simplex_vertex_iterator(this));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** \brief Returns a range over the simplices of the boundary of a simplex.
|
|
391
|
+
*
|
|
392
|
+
* The boundary of a simplex is the set of codimension \f$1\f$ subsimplices of the simplex.
|
|
393
|
+
* If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation
|
|
394
|
+
* induced by \f$ v_0 < \cdots < v_d \f$, the iterator enumerates the
|
|
395
|
+
* simplices of the boundary in the order:
|
|
396
|
+
* \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from \f$0\f$ to \f$d\f$,
|
|
397
|
+
* where \f$\widehat{v_i}\f$ means that the vertex \f$v_i\f$ is omitted.
|
|
398
|
+
*
|
|
399
|
+
* We note that the alternate sum of the simplices given by the iterator
|
|
400
|
+
* gives \f$(-1)^{\text{dim} \sigma}\f$ the chains corresponding to the boundary
|
|
401
|
+
* of the simplex.
|
|
402
|
+
*
|
|
403
|
+
* @param[in] sh Simplex for which the boundary is computed. */
|
|
404
|
+
template <class SimplexHandle>
|
|
405
|
+
Boundary_simplex_range boundary_simplex_range(SimplexHandle sh) {
|
|
406
|
+
return Boundary_simplex_range(Boundary_simplex_iterator(this, sh), Boundary_simplex_iterator(this));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** \brief Given a simplex, returns a range over the simplices of its boundary and their opposite vertices.
|
|
410
|
+
*
|
|
411
|
+
* The boundary of a simplex is the set of codimension \f$1\f$ subsimplices of the simplex.
|
|
412
|
+
* If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation induced by \f$ v_0 < \cdots < v_d \f$, the
|
|
413
|
+
* iterator enumerates the simplices of the boundary in the order:
|
|
414
|
+
* \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from \f$d\f$ to \f$0\f$, where \f$\widehat{v_i}\f$ means
|
|
415
|
+
* that the vertex \f$v_i\f$, known as the opposite vertex, is omitted from boundary, but returned as the second
|
|
416
|
+
* element of a pair.
|
|
417
|
+
*
|
|
418
|
+
* @param[in] sh Simplex for which the boundary is computed.
|
|
419
|
+
*/
|
|
420
|
+
template <class SimplexHandle>
|
|
421
|
+
Boundary_opposite_vertex_simplex_range boundary_opposite_vertex_simplex_range(SimplexHandle sh) {
|
|
422
|
+
return Boundary_opposite_vertex_simplex_range(Boundary_opposite_vertex_simplex_iterator(this, sh),
|
|
423
|
+
Boundary_opposite_vertex_simplex_iterator(this));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** @} */ // end range and iterator methods
|
|
427
|
+
/** \name Constructor/Destructor
|
|
428
|
+
* @{ */
|
|
429
|
+
|
|
430
|
+
/** \brief Constructs an empty simplex tree. */
|
|
431
|
+
Simplex_tree() : null_vertex_(-1), root_(nullptr, null_vertex_), filtration_vect_(), dimension_(-1) {
|
|
432
|
+
if constexpr (Options::is_multi_parameter) number_of_parameters_ = 2;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* @brief Construct the simplex tree as the copy of a given simplex tree with eventually different template
|
|
437
|
+
* parameters.
|
|
438
|
+
* Therefore, should provide a method converting the filtration values of one tree to the another. All other values
|
|
439
|
+
* are already implicitly convertible if the concept of @ref SimplexTreeOptions is respected (note that there is
|
|
440
|
+
* an eventual loss of precision or an undefined behaviour if a value is converted into a new type too small to
|
|
441
|
+
* contain it). Any extra data (@ref Simplex_data) stored in the simplices are ignored in the copy for now.
|
|
442
|
+
*
|
|
443
|
+
* @tparam OtherSimplexTreeOptions Options of the given simplex tree.
|
|
444
|
+
* @tparam F Method taking an OtherSimplexTreeOptions::Filtration_value as input and returning an
|
|
445
|
+
* Options::Filtration_value.
|
|
446
|
+
* @param complex_source Simplex tree to copy.
|
|
447
|
+
* @param translate_filtration_value Method taking an OtherSimplexTreeOptions::Filtration_value from the source tree
|
|
448
|
+
* as input and returning the corresponding Options::Filtration_value in the new tree.
|
|
449
|
+
*/
|
|
450
|
+
template <typename OtherSimplexTreeOptions, typename F>
|
|
451
|
+
Simplex_tree(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
|
|
452
|
+
#ifdef DEBUG_TRACES
|
|
453
|
+
std::clog << "Simplex_tree custom copy constructor" << std::endl;
|
|
454
|
+
#endif // DEBUG_TRACES
|
|
455
|
+
copy_from(complex_source, translate_filtration_value);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** \brief User-defined copy constructor reproduces the whole tree structure. */
|
|
459
|
+
Simplex_tree(const Simplex_tree& complex_source) {
|
|
460
|
+
#ifdef DEBUG_TRACES
|
|
461
|
+
std::clog << "Simplex_tree copy constructor" << std::endl;
|
|
462
|
+
#endif // DEBUG_TRACES
|
|
463
|
+
copy_from(complex_source);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** \brief User-defined move constructor relocates the whole tree structure.
|
|
467
|
+
* \exception std::invalid_argument In debug mode, if the complex_source is invalid.
|
|
468
|
+
*/
|
|
469
|
+
Simplex_tree(Simplex_tree&& complex_source) : number_of_parameters_(std::move(complex_source.number_of_parameters_)) {
|
|
470
|
+
#ifdef DEBUG_TRACES
|
|
471
|
+
std::clog << "Simplex_tree move constructor" << std::endl;
|
|
472
|
+
#endif // DEBUG_TRACES
|
|
473
|
+
move_from(complex_source);
|
|
474
|
+
|
|
475
|
+
// just need to set dimension_ on source to make it available again
|
|
476
|
+
// (filtration_vect_ and members are already set from the move)
|
|
477
|
+
complex_source.dimension_ = -1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** \brief Destructor; deallocates the whole tree structure. */
|
|
481
|
+
~Simplex_tree() { root_members_recursive_deletion(); }
|
|
482
|
+
|
|
483
|
+
/** \brief User-defined copy assignment reproduces the whole tree structure. */
|
|
484
|
+
Simplex_tree& operator=(const Simplex_tree& complex_source) {
|
|
485
|
+
#ifdef DEBUG_TRACES
|
|
486
|
+
std::clog << "Simplex_tree copy assignment" << std::endl;
|
|
487
|
+
#endif // DEBUG_TRACES
|
|
488
|
+
// Self-assignment detection
|
|
489
|
+
if (&complex_source != this) {
|
|
490
|
+
// We start by deleting root_ if not empty
|
|
491
|
+
root_members_recursive_deletion();
|
|
492
|
+
|
|
493
|
+
copy_from(complex_source);
|
|
494
|
+
}
|
|
495
|
+
return *this;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** \brief User-defined move assignment relocates the whole tree structure.
|
|
499
|
+
* \exception std::invalid_argument In debug mode, if the complex_source is invalid.
|
|
500
|
+
*/
|
|
501
|
+
Simplex_tree& operator=(Simplex_tree&& complex_source) {
|
|
502
|
+
#ifdef DEBUG_TRACES
|
|
503
|
+
std::clog << "Simplex_tree move assignment" << std::endl;
|
|
504
|
+
#endif // DEBUG_TRACES
|
|
505
|
+
// Self-assignment detection
|
|
506
|
+
if (&complex_source != this) {
|
|
507
|
+
// root_ deletion in case it was not empty
|
|
508
|
+
root_members_recursive_deletion();
|
|
509
|
+
|
|
510
|
+
move_from(complex_source);
|
|
511
|
+
}
|
|
512
|
+
return *this;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** @} */ // end constructor/destructor
|
|
516
|
+
|
|
517
|
+
private:
|
|
518
|
+
// Copy from complex_source to "this"
|
|
519
|
+
void copy_from(const Simplex_tree& complex_source) {
|
|
520
|
+
null_vertex_ = complex_source.null_vertex_;
|
|
521
|
+
filtration_vect_.clear();
|
|
522
|
+
dimension_ = complex_source.dimension_;
|
|
523
|
+
auto root_source = complex_source.root_;
|
|
524
|
+
|
|
525
|
+
// root members copy
|
|
526
|
+
root_.members() =
|
|
527
|
+
Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
|
|
528
|
+
// Needs to reassign children
|
|
529
|
+
for (auto& map_el : root_.members()) {
|
|
530
|
+
map_el.second.assign_children(&root_);
|
|
531
|
+
}
|
|
532
|
+
rec_copy<Options::store_key>(
|
|
533
|
+
&root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
|
|
534
|
+
if constexpr (Options::is_multi_parameter) number_of_parameters_ = complex_source.number_of_parameters_;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Copy from complex_source to "this"
|
|
538
|
+
template <typename OtherSimplexTreeOptions, typename F>
|
|
539
|
+
void copy_from(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
|
|
540
|
+
null_vertex_ = complex_source.null_vertex_;
|
|
541
|
+
filtration_vect_.clear();
|
|
542
|
+
dimension_ = complex_source.dimension_;
|
|
543
|
+
auto root_source = complex_source.root_;
|
|
544
|
+
|
|
545
|
+
// root members copy
|
|
546
|
+
if constexpr (!Options::stable_simplex_handles) root_.members().reserve(root_source.size());
|
|
547
|
+
for (auto& p : root_source.members()) {
|
|
548
|
+
if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) {
|
|
549
|
+
auto it = root_.members().try_emplace(
|
|
550
|
+
root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()), p.second.key());
|
|
551
|
+
} else {
|
|
552
|
+
auto it = root_.members().try_emplace(
|
|
553
|
+
root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
rec_copy<OtherSimplexTreeOptions::store_key>(&root_, &root_source, translate_filtration_value);
|
|
558
|
+
if constexpr (Options::is_multi_parameter) number_of_parameters_ = complex_source.number_of_parameters_;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** \brief depth first search, inserts simplices when reaching a leaf. */
|
|
562
|
+
template <bool store_key, typename OtherSiblings, typename F>
|
|
563
|
+
void rec_copy(Siblings* sib, OtherSiblings* sib_source, F&& translate_filtration_value) {
|
|
564
|
+
auto sh_source = sib_source->members().begin();
|
|
565
|
+
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh, ++sh_source) {
|
|
566
|
+
update_simplex_tree_after_node_insertion(sh);
|
|
567
|
+
if (has_children(sh_source)) {
|
|
568
|
+
Siblings* newsib = new Siblings(sib, sh_source->first);
|
|
569
|
+
if constexpr (!Options::stable_simplex_handles) {
|
|
570
|
+
newsib->members_.reserve(sh_source->second.children()->members().size());
|
|
571
|
+
}
|
|
572
|
+
for (auto& child : sh_source->second.children()->members()) {
|
|
573
|
+
if constexpr (store_key && Options::store_key) {
|
|
574
|
+
newsib->members_.emplace_hint(
|
|
575
|
+
newsib->members_.end(),
|
|
576
|
+
child.first,
|
|
577
|
+
Node(newsib, translate_filtration_value(child.second.filtration()), child.second.key()));
|
|
578
|
+
} else {
|
|
579
|
+
newsib->members_.emplace_hint(newsib->members_.end(),
|
|
580
|
+
child.first,
|
|
581
|
+
Node(newsib, translate_filtration_value(child.second.filtration())));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
rec_copy<store_key>(newsib, sh_source->second.children(), translate_filtration_value);
|
|
585
|
+
sh->second.assign_children(newsib);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Move from complex_source to "this"
|
|
591
|
+
void move_from(Simplex_tree& complex_source) {
|
|
592
|
+
null_vertex_ = std::move(complex_source.null_vertex_);
|
|
593
|
+
root_ = std::move(complex_source.root_);
|
|
594
|
+
filtration_vect_ = std::move(complex_source.filtration_vect_);
|
|
595
|
+
dimension_ = complex_source.dimension_;
|
|
596
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
597
|
+
nodes_label_to_list_.swap(complex_source.nodes_label_to_list_);
|
|
598
|
+
}
|
|
599
|
+
// Need to update root members (children->oncles and children need to point on the new root pointer)
|
|
600
|
+
for (auto& map_el : root_.members()) {
|
|
601
|
+
if (map_el.second.children() != &(complex_source.root_)) {
|
|
602
|
+
// reset children->oncles with the moved root_ pointer value
|
|
603
|
+
map_el.second.children()->oncles_ = &root_;
|
|
604
|
+
} else {
|
|
605
|
+
// if simplex is of dimension 0, oncles_ shall be nullptr
|
|
606
|
+
GUDHI_CHECK(map_el.second.children()->oncles_ == nullptr,
|
|
607
|
+
std::invalid_argument("Simplex_tree move constructor from an invalid Simplex_tree"));
|
|
608
|
+
// and children points on root_ - to be moved
|
|
609
|
+
map_el.second.assign_children(&root_);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// delete all root_.members() recursively
|
|
615
|
+
void root_members_recursive_deletion() {
|
|
616
|
+
for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
|
|
617
|
+
if (has_children(sh)) {
|
|
618
|
+
rec_delete(sh->second.children());
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
root_.members().clear();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Recursive deletion
|
|
625
|
+
void rec_delete(Siblings* sib) {
|
|
626
|
+
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
|
|
627
|
+
if (has_children(sh)) {
|
|
628
|
+
rec_delete(sh->second.children());
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
delete sib;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
public:
|
|
635
|
+
template <typename>
|
|
636
|
+
friend class Simplex_tree;
|
|
637
|
+
|
|
638
|
+
/** \brief Checks if two simplex trees are equal. */
|
|
639
|
+
template <class OtherSimplexTreeOptions>
|
|
640
|
+
bool operator==(Simplex_tree<OtherSimplexTreeOptions>& st2) {
|
|
641
|
+
if constexpr (Options::is_multi_parameter) {
|
|
642
|
+
if constexpr (!OtherSimplexTreeOptions::is_multi_parameter) {
|
|
643
|
+
return false;
|
|
644
|
+
} else {
|
|
645
|
+
if (st2.get_number_of_parameters() != this->get_number_of_parameters()) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if ((null_vertex_ != st2.null_vertex_) ||
|
|
651
|
+
(dimension_ != st2.dimension_ && !dimension_to_be_lowered_ && !st2.dimension_to_be_lowered_))
|
|
652
|
+
return false;
|
|
653
|
+
return rec_equal(&root_, &st2.root_);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** \brief Checks if two simplex trees are different. */
|
|
657
|
+
template <class OtherSimplexTreeOptions>
|
|
658
|
+
bool operator!=(Simplex_tree<OtherSimplexTreeOptions>& st2) {
|
|
659
|
+
return (!(*this == st2));
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private:
|
|
663
|
+
/** rec_equal: Checks recursively whether or not two simplex trees are equal, using depth first search. */
|
|
664
|
+
template <class OtherSiblings>
|
|
665
|
+
bool rec_equal(Siblings* s1, OtherSiblings* s2) {
|
|
666
|
+
if (s1->members().size() != s2->members().size()) return false;
|
|
667
|
+
auto sh2 = s2->members().begin();
|
|
668
|
+
for (auto sh1 = s1->members().begin(); (sh1 != s1->members().end() && sh2 != s2->members().end()); ++sh1, ++sh2) {
|
|
669
|
+
if (sh1->first != sh2->first || !(sh1->second.filtration() == sh2->second.filtration())) return false;
|
|
670
|
+
if (has_children(sh1) != has_children(sh2)) return false;
|
|
671
|
+
// Recursivity on children only if both have children
|
|
672
|
+
else if (has_children(sh1))
|
|
673
|
+
if (!rec_equal(sh1->second.children(), sh2->second.children())) return false;
|
|
674
|
+
}
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/** \brief Returns the filtration value of a simplex.
|
|
679
|
+
*
|
|
680
|
+
* Same as `filtration()`, but does not handle `null_simplex()`.
|
|
681
|
+
*/
|
|
682
|
+
static const Filtration_value& filtration_(Simplex_handle sh) {
|
|
683
|
+
GUDHI_CHECK(sh != null_simplex(), "null simplex");
|
|
684
|
+
return sh->second.filtration();
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
public:
|
|
688
|
+
/** \brief Returns the key associated to a simplex.
|
|
689
|
+
*
|
|
690
|
+
* If no key has been assigned, returns `null_key()`.
|
|
691
|
+
* \pre SimplexTreeOptions::store_key
|
|
692
|
+
*/
|
|
693
|
+
static Simplex_key key(Simplex_handle sh) { return sh->second.key(); }
|
|
694
|
+
|
|
695
|
+
/** \brief Returns the simplex that has index idx in the filtration.
|
|
696
|
+
*
|
|
697
|
+
* The filtration must be initialized.
|
|
698
|
+
*/
|
|
699
|
+
Simplex_handle simplex(Simplex_key idx) const { return filtration_vect_[idx]; }
|
|
700
|
+
|
|
701
|
+
/** \brief Returns the filtration value of a simplex.
|
|
702
|
+
*
|
|
703
|
+
* Called on the null_simplex, it returns infinity.
|
|
704
|
+
* If SimplexTreeOptions::store_filtration is false, returns 0.
|
|
705
|
+
*/
|
|
706
|
+
static const Filtration_value& filtration(Simplex_handle sh) {
|
|
707
|
+
if (sh != null_simplex()) {
|
|
708
|
+
return sh->second.filtration();
|
|
709
|
+
} else {
|
|
710
|
+
return Filtration_simplex_base_real::get_infinity();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
static Filtration_value& filtration_mutable(Simplex_handle sh) { return sh->second.filtration(); }
|
|
715
|
+
|
|
716
|
+
/** \brief Sets the filtration value of a simplex.
|
|
717
|
+
* \exception std::invalid_argument In debug mode, if sh is a null_simplex.
|
|
718
|
+
*/
|
|
719
|
+
void assign_filtration(Simplex_handle sh, const Filtration_value& fv) {
|
|
720
|
+
GUDHI_CHECK(sh != null_simplex(),
|
|
721
|
+
std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex"));
|
|
722
|
+
sh->second.assign_filtration(fv);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** \brief Returns a Simplex_handle different from all Simplex_handles
|
|
726
|
+
* associated to the simplices in the simplicial complex.
|
|
727
|
+
*
|
|
728
|
+
* One can call filtration(null_simplex()). */
|
|
729
|
+
static Simplex_handle null_simplex() { return Dictionary_it(); }
|
|
730
|
+
|
|
731
|
+
/** \brief Returns a fixed number not in the interval [0, `num_simplices()`). */
|
|
732
|
+
static Simplex_key null_key() { return -1; }
|
|
733
|
+
|
|
734
|
+
/** \brief Returns the extra data stored in a simplex. */
|
|
735
|
+
static Simplex_data& simplex_data(Simplex_handle sh) {
|
|
736
|
+
GUDHI_CHECK(sh != null_simplex(),
|
|
737
|
+
std::invalid_argument("Simplex_tree::simplex_data - no data associated to null_simplex"));
|
|
738
|
+
return sh->second.data();
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/** \brief Returns a Vertex_handle different from all Vertex_handles associated
|
|
742
|
+
* to the vertices of the simplicial complex. */
|
|
743
|
+
Vertex_handle null_vertex() const { return null_vertex_; }
|
|
744
|
+
|
|
745
|
+
/** \brief Returns the number of vertices in the complex. */
|
|
746
|
+
size_t num_vertices() const { return root_.members_.size(); }
|
|
747
|
+
|
|
748
|
+
/** \brief Returns whether the complex is empty. */
|
|
749
|
+
bool is_empty() const { return root_.members_.empty(); }
|
|
750
|
+
|
|
751
|
+
public:
|
|
752
|
+
/** \brief Returns the number of simplices in the simplex_tree.
|
|
753
|
+
*
|
|
754
|
+
* This function takes time linear in the number of simplices. */
|
|
755
|
+
size_t num_simplices() { return num_simplices(root()); }
|
|
756
|
+
|
|
757
|
+
private:
|
|
758
|
+
/** \brief Returns the number of simplices in the simplex_tree. */
|
|
759
|
+
size_t num_simplices(Siblings* sib) {
|
|
760
|
+
auto sib_begin = sib->members().begin();
|
|
761
|
+
auto sib_end = sib->members().end();
|
|
762
|
+
size_t simplices_number = sib->members().size();
|
|
763
|
+
for (auto sh = sib_begin; sh != sib_end; ++sh) {
|
|
764
|
+
if (has_children(sh)) {
|
|
765
|
+
simplices_number += num_simplices(sh->second.children());
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return simplices_number;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* @brief Returns the dimension of the given sibling simplices.
|
|
773
|
+
*
|
|
774
|
+
* @param curr_sib Pointer to the sibling container.
|
|
775
|
+
* @return Height of the siblings in the tree (root counts as zero to make the height correspond to the dimension).
|
|
776
|
+
*/
|
|
777
|
+
int dimension(Siblings* curr_sib) {
|
|
778
|
+
int dim = -1;
|
|
779
|
+
while (curr_sib != nullptr) {
|
|
780
|
+
++dim;
|
|
781
|
+
curr_sib = curr_sib->oncles();
|
|
782
|
+
}
|
|
783
|
+
return dim;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
public:
|
|
787
|
+
/** \brief Returns the number of simplices of each dimension in the simplex tree. */
|
|
788
|
+
std::vector<size_t> num_simplices_by_dimension() {
|
|
789
|
+
if (is_empty()) return {};
|
|
790
|
+
// std::min in case the upper bound got crazy
|
|
791
|
+
std::vector<size_t> res(std::min(upper_bound_dimension() + 1, max_dimension() + 1));
|
|
792
|
+
auto fun = [&res](Simplex_handle, int dim) -> void { ++res[dim]; };
|
|
793
|
+
for_each_simplex(fun);
|
|
794
|
+
if (dimension_to_be_lowered_) {
|
|
795
|
+
GUDHI_CHECK(res.front() != 0, std::logic_error("Bug in Gudhi: non-empty complex has no vertex"));
|
|
796
|
+
while (res.back() == 0) res.pop_back();
|
|
797
|
+
dimension_ = static_cast<int>(res.size()) - 1;
|
|
798
|
+
dimension_to_be_lowered_ = false;
|
|
799
|
+
} else {
|
|
800
|
+
GUDHI_CHECK(res.back() != 0,
|
|
801
|
+
std::logic_error("Bug in Gudhi: there is no simplex of dimension the dimension of the complex"));
|
|
802
|
+
}
|
|
803
|
+
return res;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/** \brief Returns the dimension of a simplex.
|
|
807
|
+
*
|
|
808
|
+
* Must be different from null_simplex().*/
|
|
809
|
+
int dimension(Simplex_handle sh) { return dimension(self_siblings(sh)); }
|
|
810
|
+
|
|
811
|
+
/** \brief Returns an upper bound on the dimension of the simplicial complex. */
|
|
812
|
+
int upper_bound_dimension() const { return dimension_; }
|
|
813
|
+
|
|
814
|
+
/** \brief Returns the dimension of the simplicial complex.
|
|
815
|
+
\details This function is not constant time because it can recompute dimension if required (can be triggered by
|
|
816
|
+
`remove_maximal_simplex()` or `prune_above_filtration()`).
|
|
817
|
+
*/
|
|
818
|
+
int dimension() {
|
|
819
|
+
if (dimension_to_be_lowered_) lower_upper_bound_dimension();
|
|
820
|
+
return dimension_;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/** \brief Returns true if the node in the simplex tree pointed by
|
|
824
|
+
* the given simplex handle has children.*/
|
|
825
|
+
template <class SimplexHandle>
|
|
826
|
+
bool has_children(SimplexHandle sh) const {
|
|
827
|
+
// Here we rely on the root using null_vertex(), which cannot match any real vertex.
|
|
828
|
+
return (sh->second.children()->parent() == sh->first);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
private:
|
|
832
|
+
friend class Simplex_tree_optimized_star_simplex_iterator<Simplex_tree>;
|
|
833
|
+
|
|
834
|
+
/** \brief Returns the children of the node in the simplex tree pointed by sh.
|
|
835
|
+
* \exception std::invalid_argument In debug mode, if sh has no child.
|
|
836
|
+
*/
|
|
837
|
+
Siblings* children(Simplex_handle sh) const {
|
|
838
|
+
GUDHI_CHECK(has_children(sh), std::invalid_argument("Simplex_tree::children - argument has no child"));
|
|
839
|
+
return sh->second.children();
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
public:
|
|
843
|
+
/** \brief Given a range of Vertex_handles, returns the Simplex_handle
|
|
844
|
+
* of the simplex in the simplicial complex containing the corresponding
|
|
845
|
+
* vertices. Return null_simplex() if the simplex is not in the complex.
|
|
846
|
+
*
|
|
847
|
+
* The type InputVertexRange must be a range of <CODE>Vertex_handle</CODE>
|
|
848
|
+
* on which we can call std::begin() function
|
|
849
|
+
*/
|
|
850
|
+
template <class InputVertexRange = std::initializer_list<Vertex_handle>>
|
|
851
|
+
Simplex_handle find(const InputVertexRange& s) {
|
|
852
|
+
auto first = std::begin(s);
|
|
853
|
+
auto last = std::end(s);
|
|
854
|
+
|
|
855
|
+
if (first == last) return null_simplex(); // ----->>
|
|
856
|
+
|
|
857
|
+
// Copy before sorting
|
|
858
|
+
std::vector<Vertex_handle> copy(first, last);
|
|
859
|
+
std::sort(std::begin(copy), std::end(copy));
|
|
860
|
+
return find_simplex(copy);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
private:
|
|
864
|
+
/** Find function, with a sorted range of vertices. */
|
|
865
|
+
Simplex_handle find_simplex(const std::vector<Vertex_handle>& simplex) {
|
|
866
|
+
Siblings* tmp_sib = &root_;
|
|
867
|
+
Dictionary_it tmp_dit;
|
|
868
|
+
auto vi = simplex.begin();
|
|
869
|
+
if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
|
|
870
|
+
// Equivalent to the first iteration of the normal loop
|
|
871
|
+
GUDHI_CHECK(contiguous_vertices(), "non-contiguous vertices");
|
|
872
|
+
Vertex_handle v = *vi++;
|
|
873
|
+
if (v < 0 || v >= static_cast<Vertex_handle>(root_.members_.size())) return null_simplex();
|
|
874
|
+
tmp_dit = root_.members_.begin() + v;
|
|
875
|
+
if (vi == simplex.end()) return tmp_dit;
|
|
876
|
+
if (!has_children(tmp_dit)) return null_simplex();
|
|
877
|
+
tmp_sib = tmp_dit->second.children();
|
|
878
|
+
}
|
|
879
|
+
for (;;) {
|
|
880
|
+
tmp_dit = tmp_sib->members_.find(*vi++);
|
|
881
|
+
if (tmp_dit == tmp_sib->members_.end()) return null_simplex();
|
|
882
|
+
if (vi == simplex.end()) return tmp_dit;
|
|
883
|
+
if (!has_children(tmp_dit)) return null_simplex();
|
|
884
|
+
tmp_sib = tmp_dit->second.children();
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/** \brief Returns the Simplex_handle corresponding to the 0-simplex
|
|
889
|
+
* representing the vertex with Vertex_handle v. */
|
|
890
|
+
Simplex_handle find_vertex(Vertex_handle v) {
|
|
891
|
+
if constexpr (Options::contiguous_vertices && !Options::stable_simplex_handles) {
|
|
892
|
+
assert(contiguous_vertices());
|
|
893
|
+
return root_.members_.begin() + v;
|
|
894
|
+
} else {
|
|
895
|
+
return root_.members_.find(v);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
public:
|
|
900
|
+
/** \private \brief Test if the vertices have contiguous numbering: 0, 1, etc. */
|
|
901
|
+
bool contiguous_vertices() const {
|
|
902
|
+
if (root_.members_.empty()) return true;
|
|
903
|
+
if (root_.members_.begin()->first != 0) return false;
|
|
904
|
+
if (std::prev(root_.members_.end())->first != static_cast<Vertex_handle>(root_.members_.size() - 1)) return false;
|
|
905
|
+
return true;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
private:
|
|
909
|
+
/**
|
|
910
|
+
* @brief Inserts a Node in the set of siblings nodes. Calls `update_simplex_tree_after_node_insertion`
|
|
911
|
+
* if the insertion succeeded.
|
|
912
|
+
*
|
|
913
|
+
* @tparam update_fil If true, as well as Options::store_filtration, and the node is already present, assigns
|
|
914
|
+
* the "union" of the input filtration_value and the value already present in the node as filtration value.
|
|
915
|
+
* @tparam update_children If true and the node has no children, create a child with sib as uncle.
|
|
916
|
+
* @tparam set_to_null If true and the node is already present, sets the returned simplex handle to null if the
|
|
917
|
+
* filtration value of the simplex was not modified.
|
|
918
|
+
* @tparam Filt Filtration value type.
|
|
919
|
+
* @param sib Sibling in where the node has to be inserted.
|
|
920
|
+
* @param v Label of the node.
|
|
921
|
+
* @param filtration_value Filtration value stored in the node.
|
|
922
|
+
* @return Pair of the iterator to the new node and a boolean indicating if the insertion succeeded.
|
|
923
|
+
*/
|
|
924
|
+
template <bool update_fil, bool update_children, bool set_to_null, class Filt>
|
|
925
|
+
std::pair<Simplex_handle, bool> insert_node_(Siblings* sib, Vertex_handle v, Filt&& filtration_value) {
|
|
926
|
+
std::pair<Simplex_handle, bool> ins = sib->members_.try_emplace(v, sib, std::forward<Filt>(filtration_value));
|
|
927
|
+
|
|
928
|
+
if constexpr (update_children) {
|
|
929
|
+
if (!(has_children(ins.first))) {
|
|
930
|
+
ins.first->second.assign_children(new Siblings(sib, v));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (ins.second) {
|
|
935
|
+
// Only required when insertion is successful
|
|
936
|
+
update_simplex_tree_after_node_insertion(ins.first);
|
|
937
|
+
return ins;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if constexpr (Options::store_filtration && update_fil) {
|
|
941
|
+
if (unify_lifetimes(ins.first->second.filtration(), filtration_value)) return ins;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if constexpr (set_to_null) {
|
|
945
|
+
ins.first = null_simplex();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return ins;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
protected:
|
|
952
|
+
/** \brief Inserts a simplex represented by a range of vertex.
|
|
953
|
+
* @param[in] simplex range of Vertex_handles, representing the vertices of the new simplex. The range must be
|
|
954
|
+
* sorted by increasing vertex handle order, and not empty.
|
|
955
|
+
* @param[in] filtration the filtration value assigned to the new simplex.
|
|
956
|
+
* @return If the new simplex is inserted successfully (i.e. it was not in the
|
|
957
|
+
* simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
|
|
958
|
+
* to the new simplex.
|
|
959
|
+
* If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
|
|
960
|
+
* fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
|
|
961
|
+
* we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
|
|
962
|
+
* output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
|
|
963
|
+
* null_simplex.
|
|
964
|
+
*
|
|
965
|
+
*/
|
|
966
|
+
template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>>
|
|
967
|
+
std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex,
|
|
968
|
+
const Filtration_value& filtration) {
|
|
969
|
+
Siblings* curr_sib = &root_;
|
|
970
|
+
std::pair<Simplex_handle, bool> res_insert;
|
|
971
|
+
auto vi = simplex.begin();
|
|
972
|
+
|
|
973
|
+
for (; vi != std::prev(simplex.end()); ++vi) {
|
|
974
|
+
GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
|
|
975
|
+
res_insert = insert_node_<false, true, false>(curr_sib, *vi, filtration);
|
|
976
|
+
curr_sib = res_insert.first->second.children();
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
GUDHI_CHECK(*vi != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");
|
|
980
|
+
res_insert = insert_node_<true, false, true>(curr_sib, *vi, filtration);
|
|
981
|
+
if (res_insert.second) {
|
|
982
|
+
int dim = static_cast<int>(boost::size(simplex)) - 1;
|
|
983
|
+
if (dim > dimension_) {
|
|
984
|
+
// Update dimension if needed
|
|
985
|
+
dimension_ = dim;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return res_insert;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
public:
|
|
992
|
+
/** \brief Insert a simplex, represented by a range of Vertex_handles, in the simplicial complex.
|
|
993
|
+
*
|
|
994
|
+
* @param[in] simplex range of Vertex_handles, representing the vertices of the new simplex
|
|
995
|
+
* @param[in] filtration the filtration value assigned to the new simplex.
|
|
996
|
+
* @return If the new simplex is inserted successfully (i.e. it was not in the
|
|
997
|
+
* simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
|
|
998
|
+
* to the new simplex.
|
|
999
|
+
* If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
|
|
1000
|
+
* fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
|
|
1001
|
+
* we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
|
|
1002
|
+
* output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
|
|
1003
|
+
* null_simplex.
|
|
1004
|
+
*
|
|
1005
|
+
* All subsimplices do not necessary need to be already in the simplex tree to proceed to an
|
|
1006
|
+
* insertion. However, the property of being a simplicial complex will be violated. This allows
|
|
1007
|
+
* us to insert a stream of simplices contained in a simplicial complex without considering any
|
|
1008
|
+
* order on them.
|
|
1009
|
+
*
|
|
1010
|
+
* The filtration value
|
|
1011
|
+
* assigned to the new simplex must preserve the monotonicity of the filtration.
|
|
1012
|
+
*
|
|
1013
|
+
* The type InputVertexRange must be a range for which .begin() and
|
|
1014
|
+
* .end() return input iterators, with 'value_type' Vertex_handle. */
|
|
1015
|
+
template <class InputVertexRange = std::initializer_list<Vertex_handle>>
|
|
1016
|
+
std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange& simplex,
|
|
1017
|
+
const Filtration_value& filtration = Filtration_value()) {
|
|
1018
|
+
auto first = std::begin(simplex);
|
|
1019
|
+
auto last = std::end(simplex);
|
|
1020
|
+
|
|
1021
|
+
if (first == last) return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
|
|
1022
|
+
|
|
1023
|
+
// Copy before sorting
|
|
1024
|
+
std::vector<Vertex_handle> copy(first, last);
|
|
1025
|
+
std::sort(std::begin(copy), std::end(copy));
|
|
1026
|
+
return insert_simplex_raw(copy, filtration);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** \brief Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of
|
|
1030
|
+
* Vertex_handles, in the simplicial complex.
|
|
1031
|
+
*
|
|
1032
|
+
* @param[in] Nsimplex range of Vertex_handles, representing the vertices of the new N-simplex
|
|
1033
|
+
* @param[in] filtration the filtration value assigned to the new N-simplex.
|
|
1034
|
+
* @return If the new simplex is inserted successfully (i.e. it was not in the
|
|
1035
|
+
* simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
|
|
1036
|
+
* to the new simplex.
|
|
1037
|
+
* If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
|
|
1038
|
+
* fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
|
|
1039
|
+
* we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
|
|
1040
|
+
* output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
|
|
1041
|
+
* null_simplex.
|
|
1042
|
+
*/
|
|
1043
|
+
template <class InputVertexRange = std::initializer_list<Vertex_handle>>
|
|
1044
|
+
std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(const InputVertexRange& Nsimplex,
|
|
1045
|
+
const Filtration_value& filtration = Filtration_value()) {
|
|
1046
|
+
auto first = std::begin(Nsimplex);
|
|
1047
|
+
auto last = std::end(Nsimplex);
|
|
1048
|
+
|
|
1049
|
+
if (first == last) return {null_simplex(), true}; // FIXME: false would make more sense to me.
|
|
1050
|
+
|
|
1051
|
+
thread_local std::vector<Vertex_handle> copy;
|
|
1052
|
+
copy.clear();
|
|
1053
|
+
copy.insert(copy.end(), first, last);
|
|
1054
|
+
std::sort(copy.begin(), copy.end());
|
|
1055
|
+
auto last_unique = std::unique(copy.begin(), copy.end());
|
|
1056
|
+
copy.erase(last_unique, copy.end());
|
|
1057
|
+
GUDHI_CHECK_code(for (Vertex_handle v : copy)
|
|
1058
|
+
GUDHI_CHECK(v != null_vertex(), "cannot use the dummy null_vertex() as a real vertex");)
|
|
1059
|
+
// Update dimension if needed. We could wait to see if the insertion succeeds, but I doubt there is much to
|
|
1060
|
+
// gain.
|
|
1061
|
+
dimension_ = (std::max)(dimension_, static_cast<int>(std::distance(copy.begin(), copy.end())) - 1);
|
|
1062
|
+
|
|
1063
|
+
auto truc = rec_insert_simplex_and_subfaces_sorted(root(), copy.begin(), copy.end(), filtration);
|
|
1064
|
+
if constexpr (SimplexTreeOptions::is_multi_parameter && !Filtration_value::is_multicritical()) {
|
|
1065
|
+
make_subfiltration_non_decreasing(truc.first);
|
|
1066
|
+
}
|
|
1067
|
+
return truc;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
private:
|
|
1071
|
+
// To insert {1,2,3,4}, we insert {2,3,4} twice, once at the root, and once below 1.
|
|
1072
|
+
template <class ForwardVertexIterator>
|
|
1073
|
+
std::pair<Simplex_handle, bool> rec_insert_simplex_and_subfaces_sorted(Siblings* sib,
|
|
1074
|
+
ForwardVertexIterator first,
|
|
1075
|
+
ForwardVertexIterator last,
|
|
1076
|
+
const Filtration_value& filt) {
|
|
1077
|
+
// An alternative strategy would be:
|
|
1078
|
+
// - try to find the complete simplex, if found (and low filtration) exit
|
|
1079
|
+
// - insert all the vertices at once in sib
|
|
1080
|
+
// - loop over those (new or not) simplices, with a recursive call(++first, last)
|
|
1081
|
+
Vertex_handle vertex_one = *first;
|
|
1082
|
+
|
|
1083
|
+
if (++first == last) {
|
|
1084
|
+
if constexpr (SimplexTreeOptions::is_multi_parameter) {
|
|
1085
|
+
return insert_node_<false, false, false>(sib, vertex_one, filt);
|
|
1086
|
+
} else {
|
|
1087
|
+
return insert_node_<true, false, true>(sib, vertex_one, filt);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// TODO: have special code here, we know we are building the whole subtree from scratch.
|
|
1092
|
+
Node node;
|
|
1093
|
+
if constexpr (SimplexTreeOptions::is_multi_parameter) {
|
|
1094
|
+
node = insert_node_<false, true, false>(sib, vertex_one, filt).first->second;
|
|
1095
|
+
} else {
|
|
1096
|
+
node = insert_node_<true, true, false>(sib, vertex_one, filt).first->second;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
auto res = rec_insert_simplex_and_subfaces_sorted(node.children(), first, last, filt);
|
|
1100
|
+
// No need to continue if the full simplex was already there with a low enough filtration value.
|
|
1101
|
+
if (res.first != null_simplex()) rec_insert_simplex_and_subfaces_sorted(sib, first, last, filt);
|
|
1102
|
+
return res;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
public:
|
|
1106
|
+
/** \brief Assign a value 'key' to the key of the simplex
|
|
1107
|
+
* represented by the Simplex_handle 'sh'. */
|
|
1108
|
+
void assign_key(Simplex_handle sh, Simplex_key key) { sh->second.assign_key(key); }
|
|
1109
|
+
|
|
1110
|
+
/** Returns the two Simplex_handle corresponding to the endpoints of
|
|
1111
|
+
* and edge. sh must point to a 1-dimensional simplex. This is an
|
|
1112
|
+
* optimized version of the boundary computation. */
|
|
1113
|
+
std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) {
|
|
1114
|
+
assert(dimension(sh) == 1);
|
|
1115
|
+
return {find_vertex(sh->first), find_vertex(self_siblings(sh)->parent())};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/** Returns the Siblings containing a simplex.*/
|
|
1119
|
+
template <class SimplexHandle>
|
|
1120
|
+
static Siblings* self_siblings(SimplexHandle sh) {
|
|
1121
|
+
if (sh->second.children()->parent() == sh->first)
|
|
1122
|
+
return sh->second.children()->oncles();
|
|
1123
|
+
else
|
|
1124
|
+
return sh->second.children();
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
public:
|
|
1128
|
+
/** Returns a pointer to the root nodes of the simplex tree. */
|
|
1129
|
+
Siblings* root() { return &root_; }
|
|
1130
|
+
|
|
1131
|
+
/** \brief Set a dimension for the simplicial complex.
|
|
1132
|
+
* \details
|
|
1133
|
+
* If `exact` is false, `dimension` is only an upper bound on the dimension of the complex.
|
|
1134
|
+
* This function must be used with caution because it disables or limits the on-demand recomputation of the dimension
|
|
1135
|
+
* (the need for recomputation can be caused by `remove_maximal_simplex()` or `prune_above_filtration()`).
|
|
1136
|
+
*/
|
|
1137
|
+
void set_dimension(int dimension, bool exact = true) {
|
|
1138
|
+
dimension_to_be_lowered_ = !exact;
|
|
1139
|
+
dimension_ = dimension;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
private:
|
|
1143
|
+
/** \brief Returns true iff the list of vertices of sh1
|
|
1144
|
+
* is smaller than the list of vertices of sh2 w.r.t.
|
|
1145
|
+
* lexicographic order on the lists read in reverse.
|
|
1146
|
+
*
|
|
1147
|
+
* It defines a StrictWeakOrdering on simplices. The Simplex_vertex_iterators
|
|
1148
|
+
* must traverse the Vertex_handle in decreasing order. Reverse lexicographic order satisfy
|
|
1149
|
+
* the property that a subsimplex of a simplex is always strictly smaller with this order. */
|
|
1150
|
+
bool reverse_lexicographic_order(Simplex_handle sh1, Simplex_handle sh2) {
|
|
1151
|
+
Simplex_vertex_range rg1 = simplex_vertex_range(sh1);
|
|
1152
|
+
Simplex_vertex_range rg2 = simplex_vertex_range(sh2);
|
|
1153
|
+
Simplex_vertex_iterator it1 = rg1.begin();
|
|
1154
|
+
Simplex_vertex_iterator it2 = rg2.begin();
|
|
1155
|
+
while (it1 != rg1.end() && it2 != rg2.end()) {
|
|
1156
|
+
if (*it1 == *it2) {
|
|
1157
|
+
++it1;
|
|
1158
|
+
++it2;
|
|
1159
|
+
} else {
|
|
1160
|
+
return *it1 < *it2;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return ((it1 == rg1.end()) && (it2 != rg2.end()));
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/** \brief StrictWeakOrdering, for the simplices, defined by the filtration.
|
|
1167
|
+
*
|
|
1168
|
+
* It corresponds to the partial order
|
|
1169
|
+
* induced by the filtration values, with ties resolved using reverse lexicographic order.
|
|
1170
|
+
* Reverse lexicographic order has the property to always consider the subsimplex of a simplex
|
|
1171
|
+
* to be smaller. The filtration function must be monotonic. */
|
|
1172
|
+
struct is_before_in_totally_ordered_filtration {
|
|
1173
|
+
explicit is_before_in_totally_ordered_filtration(Simplex_tree* st) : st_(st) {}
|
|
1174
|
+
|
|
1175
|
+
bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const {
|
|
1176
|
+
// Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
|
|
1177
|
+
if (!(sh1->second.filtration() == sh2->second.filtration())) {
|
|
1178
|
+
return sh1->second.filtration() < sh2->second.filtration();
|
|
1179
|
+
}
|
|
1180
|
+
// is sh1 a proper subface of sh2
|
|
1181
|
+
return st_->reverse_lexicographic_order(sh1, sh2);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
Simplex_tree* st_;
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
public:
|
|
1188
|
+
/** \brief Initializes the filtration cache, i.e. sorts the
|
|
1189
|
+
* simplices according to their order in the filtration.
|
|
1190
|
+
* Assumes that the filtration values have a total order.
|
|
1191
|
+
* If not, please use @ref initialize_filtration(Comparator&&, bool) instead.
|
|
1192
|
+
* Two simplices with same filtration value are ordered by a reverse lexicographic order.
|
|
1193
|
+
*
|
|
1194
|
+
* It always recomputes the cache, even if one already exists.
|
|
1195
|
+
*
|
|
1196
|
+
* Any insertion, deletion or change of filtration value invalidates this cache,
|
|
1197
|
+
* which can be cleared with clear_filtration().
|
|
1198
|
+
*/
|
|
1199
|
+
void initialize_filtration(bool ignore_infinite_values = false) {
|
|
1200
|
+
if (ignore_infinite_values) {
|
|
1201
|
+
initialize_filtration(is_before_in_totally_ordered_filtration(this), [&](Simplex_handle sh) -> bool {
|
|
1202
|
+
return filtration(sh) == Filtration_simplex_base_real::get_infinity();
|
|
1203
|
+
});
|
|
1204
|
+
} else {
|
|
1205
|
+
initialize_filtration(is_before_in_totally_ordered_filtration(this),
|
|
1206
|
+
[](Simplex_handle) -> bool { return false; });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* @brief Initializes the filtration cache, i.e. sorts the simplices according to the specified order.
|
|
1212
|
+
* The given order has to totally order all simplices in the simplex tree such that the resulting order is a valid
|
|
1213
|
+
* 1-parameter filtration.
|
|
1214
|
+
* For example, if the stored filtration is a bi-filtration (or 2-parameter filtration), the given method can
|
|
1215
|
+
* indicate, that the simplices have to be ordered by the first parameter and in case of equality by reverse
|
|
1216
|
+
* lexicographic order. This would generate a valid 1-parameter filtration which can than be interpreted as a line
|
|
1217
|
+
* in the 2-parameter module and used with @ref filtration_simplex_range "". Also, the persistence along this line
|
|
1218
|
+
* can than be computed with @ref Gudhi::persistent_cohomology::Persistent_cohomology "Persistent_cohomology".
|
|
1219
|
+
* Just note that it is important to call @ref initialize_filtration(Comparator&&, bool) explicitly before call
|
|
1220
|
+
* @ref Gudhi::persistent_cohomology::Persistent_cohomology::compute_persistent_cohomology
|
|
1221
|
+
* "Persistent_cohomology::compute_persistent_cohomology" in that case, otherwise, the computation of persistence
|
|
1222
|
+
* will fail (if no call to @ref initialize_filtration() or @ref initialize_filtration(Comparator&&, bool) was made,
|
|
1223
|
+
* it will call it it-self with no arguments and the simplices will be wrongly sorted).
|
|
1224
|
+
*
|
|
1225
|
+
* It always recomputes the cache, even if one already exists.
|
|
1226
|
+
*
|
|
1227
|
+
* Any insertion, deletion or change of filtration value invalidates this cache,
|
|
1228
|
+
* which can be cleared with @ref clear_filtration().
|
|
1229
|
+
*
|
|
1230
|
+
* @tparam Comparator Method type taking two Simplex_handle as input and returns a bool.
|
|
1231
|
+
* @tparam Ignorer Method type taking one Simplex_handle as input and returns a bool.
|
|
1232
|
+
* @param is_before_in_filtration Method used to compare two simplices with respect to their position in the
|
|
1233
|
+
* wanted filtration. Takes two Simplex_handle as input and returns true if and only if the first simplex appears
|
|
1234
|
+
* strictly before the second simplex in the resulting 1-parameter filtration.
|
|
1235
|
+
* @param ignore_simplex Method taking a simplex handle as input and returns true if and only if the corresponding
|
|
1236
|
+
* simplex should be ignored in the filtration and therefore not be cached. But note that it is important that
|
|
1237
|
+
* the resulting filtration is still a valid filtration, that is all proper faces of a not ignored simplex
|
|
1238
|
+
* should also not be ignored and have to appear before this simplex. There will be no tests to ensure that, so
|
|
1239
|
+
* it is of the users responsibility.
|
|
1240
|
+
*/
|
|
1241
|
+
template <typename Comparator, typename Ignorer>
|
|
1242
|
+
void initialize_filtration(Comparator&& is_before_in_filtration, Ignorer&& ignore_simplex) {
|
|
1243
|
+
filtration_vect_.clear();
|
|
1244
|
+
filtration_vect_.reserve(num_simplices());
|
|
1245
|
+
for (Simplex_handle sh : complex_simplex_range()) {
|
|
1246
|
+
if (ignore_simplex(sh)) continue;
|
|
1247
|
+
filtration_vect_.push_back(sh);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/* We use stable_sort here because with libstdc++ it is faster than sort.
|
|
1251
|
+
* is_before_in_filtration is now a total order, but we used to call
|
|
1252
|
+
* stable_sort for the following heuristic:
|
|
1253
|
+
* The use of a depth-first traversal of the simplex tree, provided by
|
|
1254
|
+
* complex_simplex_range(), combined with a stable sort is meant to
|
|
1255
|
+
* optimize the order of simplices with same filtration value. The
|
|
1256
|
+
* heuristic consists in inserting the cofaces of a simplex as soon as
|
|
1257
|
+
* possible.
|
|
1258
|
+
*/
|
|
1259
|
+
#ifdef GUDHI_USE_TBB
|
|
1260
|
+
tbb::parallel_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
|
|
1261
|
+
#else
|
|
1262
|
+
std::stable_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration);
|
|
1263
|
+
#endif
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/** \brief Initializes the filtration cache if it isn't initialized yet.
|
|
1267
|
+
*
|
|
1268
|
+
* Automatically called by filtration_simplex_range(). */
|
|
1269
|
+
void maybe_initialize_filtration() {
|
|
1270
|
+
if (filtration_vect_.empty()) {
|
|
1271
|
+
initialize_filtration();
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/** \brief Clears the filtration cache produced by initialize_filtration().
|
|
1276
|
+
*
|
|
1277
|
+
* Useful when initialize_filtration() has already been called and we perform an operation
|
|
1278
|
+
* (say an insertion) that invalidates the cache. */
|
|
1279
|
+
void clear_filtration() { filtration_vect_.clear(); }
|
|
1280
|
+
|
|
1281
|
+
private:
|
|
1282
|
+
/** Recursive search of cofaces
|
|
1283
|
+
* This function uses DFS
|
|
1284
|
+
*\param vertices contains a list of vertices, which represent the vertices of the simplex not found yet.
|
|
1285
|
+
*\param curr_sib pointer to the siblings to iterate over for this iteration of the recursion.
|
|
1286
|
+
*\param curr_nbVertices represents the number of vertices of the simplex we reached by going through the tree.
|
|
1287
|
+
*\param cofaces contains a list of Simplex_handle, representing all the cofaces asked.
|
|
1288
|
+
*\param star true if we need the star of the simplex
|
|
1289
|
+
*\param nbVertices number of vertices of the cofaces we search
|
|
1290
|
+
* Prefix actions : When the bottom vertex matches with the current vertex in the tree, we remove the bottom vertex
|
|
1291
|
+
*from vertices. Infix actions : Then we call or not the recursion. Postfix actions : Finally, we add back the removed
|
|
1292
|
+
*vertex into vertices, and remove this vertex from curr_nbVertices so that we didn't change the parameters. If the
|
|
1293
|
+
*vertices list is empty, we need to check if curr_nbVertices matches with the dimension of the cofaces asked.
|
|
1294
|
+
*/
|
|
1295
|
+
void rec_coface(std::vector<Vertex_handle>& vertices,
|
|
1296
|
+
Siblings* curr_sib,
|
|
1297
|
+
int curr_nbVertices,
|
|
1298
|
+
std::vector<Simplex_handle>& cofaces,
|
|
1299
|
+
bool star,
|
|
1300
|
+
int nbVertices) {
|
|
1301
|
+
if (!(star || curr_nbVertices <= nbVertices)) // dimension of actual simplex <= nbVertices
|
|
1302
|
+
return;
|
|
1303
|
+
for (Simplex_handle simplex = curr_sib->members().begin(); simplex != curr_sib->members().end(); ++simplex) {
|
|
1304
|
+
if (vertices.empty()) {
|
|
1305
|
+
// If we reached the end of the vertices, and the simplex has more vertices than the given simplex
|
|
1306
|
+
// => we found a coface
|
|
1307
|
+
|
|
1308
|
+
// Add a coface if we want the star or if the number of vertices of the current simplex matches with nbVertices
|
|
1309
|
+
bool addCoface = (star || curr_nbVertices == nbVertices);
|
|
1310
|
+
if (addCoface) cofaces.push_back(simplex);
|
|
1311
|
+
if ((!addCoface || star) && has_children(simplex)) // Rec call
|
|
1312
|
+
rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
|
|
1313
|
+
} else {
|
|
1314
|
+
if (simplex->first == vertices.back()) {
|
|
1315
|
+
// If curr_sib matches with the top vertex
|
|
1316
|
+
bool equalDim = (star || curr_nbVertices == nbVertices); // dimension of actual simplex == nbVertices
|
|
1317
|
+
bool addCoface = vertices.size() == 1 && equalDim;
|
|
1318
|
+
if (addCoface) cofaces.push_back(simplex);
|
|
1319
|
+
if ((!addCoface || star) && has_children(simplex)) {
|
|
1320
|
+
// Rec call
|
|
1321
|
+
Vertex_handle tmp = vertices.back();
|
|
1322
|
+
vertices.pop_back();
|
|
1323
|
+
rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
|
|
1324
|
+
vertices.push_back(tmp);
|
|
1325
|
+
}
|
|
1326
|
+
} else if (simplex->first > vertices.back()) {
|
|
1327
|
+
return;
|
|
1328
|
+
} else {
|
|
1329
|
+
// (simplex->first < vertices.back()
|
|
1330
|
+
if (has_children(simplex))
|
|
1331
|
+
rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
public:
|
|
1338
|
+
/** \brief Compute the star of a n simplex
|
|
1339
|
+
* \param simplex represent the simplex of which we search the star
|
|
1340
|
+
* \return Vector of Simplex_tree::Simplex_handle (empty vector if no star found) when
|
|
1341
|
+
* SimplexTreeOptions::link_nodes_by_label is false.
|
|
1342
|
+
*
|
|
1343
|
+
* Simplex_tree::Simplex_handle range for an optimized search for the star of a simplex when
|
|
1344
|
+
* SimplexTreeOptions::link_nodes_by_label is true.
|
|
1345
|
+
*/
|
|
1346
|
+
Cofaces_simplex_range star_simplex_range(const Simplex_handle simplex) { return cofaces_simplex_range(simplex, 0); }
|
|
1347
|
+
|
|
1348
|
+
/** \brief Compute the cofaces of a n simplex
|
|
1349
|
+
* \param simplex represent the n-simplex of which we search the n+codimension cofaces
|
|
1350
|
+
* \param codimension The function returns the n+codimension-cofaces of the n-simplex. If codimension = 0, return all
|
|
1351
|
+
* cofaces (equivalent of star function)
|
|
1352
|
+
* \return Vector of Simplex_tree::Simplex_handle (empty vector if no cofaces found) when
|
|
1353
|
+
* SimplexTreeOptions::link_nodes_by_label is false.
|
|
1354
|
+
*
|
|
1355
|
+
* Simplex_tree::Simplex_handle range for an optimized search for the coface of a simplex when
|
|
1356
|
+
* SimplexTreeOptions::link_nodes_by_label is true.
|
|
1357
|
+
*/
|
|
1358
|
+
Cofaces_simplex_range cofaces_simplex_range(const Simplex_handle simplex, int codimension) {
|
|
1359
|
+
// codimension must be positive or null integer
|
|
1360
|
+
assert(codimension >= 0);
|
|
1361
|
+
|
|
1362
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
1363
|
+
Simplex_vertex_range rg = simplex_vertex_range(simplex);
|
|
1364
|
+
Static_vertex_vector simp(rg.begin(), rg.end());
|
|
1365
|
+
// must be sorted in decreasing order
|
|
1366
|
+
assert(std::is_sorted(simp.begin(), simp.end(), std::greater<Vertex_handle>()));
|
|
1367
|
+
auto range = Optimized_star_simplex_range(Optimized_star_simplex_iterator(this, std::move(simp)),
|
|
1368
|
+
Optimized_star_simplex_iterator());
|
|
1369
|
+
// Lazy filtered range
|
|
1370
|
+
Fast_cofaces_predicate select(this, codimension, this->dimension(simplex));
|
|
1371
|
+
return boost::adaptors::filter(range, select);
|
|
1372
|
+
} else {
|
|
1373
|
+
Cofaces_simplex_range cofaces;
|
|
1374
|
+
Simplex_vertex_range rg = simplex_vertex_range(simplex);
|
|
1375
|
+
std::vector<Vertex_handle> copy(rg.begin(), rg.end());
|
|
1376
|
+
if (codimension + static_cast<int>(copy.size()) > dimension_ + 1 ||
|
|
1377
|
+
(codimension == 0 && static_cast<int>(copy.size()) > dimension_)) // n+codimension greater than dimension_
|
|
1378
|
+
return cofaces;
|
|
1379
|
+
// must be sorted in decreasing order
|
|
1380
|
+
assert(std::is_sorted(copy.begin(), copy.end(), std::greater<Vertex_handle>()));
|
|
1381
|
+
bool star = codimension == 0;
|
|
1382
|
+
rec_coface(copy, &root_, 1, cofaces, star, codimension + static_cast<int>(copy.size()));
|
|
1383
|
+
return cofaces;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
public:
|
|
1388
|
+
/** \brief Inserts a 1-skeleton in an empty Simplex_tree.
|
|
1389
|
+
*
|
|
1390
|
+
* The Simplex_tree must contain no simplex when the method is
|
|
1391
|
+
* called.
|
|
1392
|
+
*
|
|
1393
|
+
* Inserts all vertices and edges given by a OneSkeletonGraph.
|
|
1394
|
+
* OneSkeletonGraph must be a model of
|
|
1395
|
+
* <a
|
|
1396
|
+
* href="https://www.boost.org/doc/libs/release/libs/graph/doc/VertexAndEdgeListGraph.html">boost::VertexAndEdgeListGraph</a>
|
|
1397
|
+
* and <a href="https://www.boost.org/doc/libs/release/libs/graph/doc/PropertyGraph.html">boost::PropertyGraph</a>.
|
|
1398
|
+
*
|
|
1399
|
+
* The vertex filtration value is accessible through the property tag
|
|
1400
|
+
* vertex_filtration_t.
|
|
1401
|
+
* The edge filtration value is accessible through the property tag
|
|
1402
|
+
* edge_filtration_t.
|
|
1403
|
+
*
|
|
1404
|
+
* boost::graph_traits<OneSkeletonGraph>::vertex_descriptor
|
|
1405
|
+
* must be Vertex_handle.
|
|
1406
|
+
* boost::graph_traits<OneSkeletonGraph>::directed_category
|
|
1407
|
+
* can be directed_tag (the fastest, the least RAM use), undirected_tag or even
|
|
1408
|
+
* bidirected_tag.
|
|
1409
|
+
*
|
|
1410
|
+
* If an edge appears with multiplicity, the function will arbitrarily pick
|
|
1411
|
+
* one representative to read the filtration value. */
|
|
1412
|
+
template <class OneSkeletonGraph>
|
|
1413
|
+
void insert_graph(const OneSkeletonGraph& skel_graph) {
|
|
1414
|
+
// the simplex tree must be empty
|
|
1415
|
+
assert(num_simplices() == 0);
|
|
1416
|
+
|
|
1417
|
+
// is there a better way to let the compiler know that we don't mean Simplex_tree::num_vertices?
|
|
1418
|
+
using boost::num_vertices;
|
|
1419
|
+
|
|
1420
|
+
if (num_vertices(skel_graph) == 0) {
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
if (num_edges(skel_graph) == 0) {
|
|
1424
|
+
dimension_ = 0;
|
|
1425
|
+
} else {
|
|
1426
|
+
dimension_ = 1;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
if constexpr (!Options::stable_simplex_handles)
|
|
1430
|
+
root_.members_.reserve(num_vertices(skel_graph)); // probably useless in most cases
|
|
1431
|
+
auto verts = vertices(skel_graph) | boost::adaptors::transformed([&](auto v) {
|
|
1432
|
+
return Dit_value_t(v, Node(&root_, get(vertex_filtration_t(), skel_graph, v)));
|
|
1433
|
+
});
|
|
1434
|
+
root_.members_.insert(boost::begin(verts), boost::end(verts));
|
|
1435
|
+
// This automatically sorts the vertices, the graph concept doesn't guarantee the order in which we iterate.
|
|
1436
|
+
|
|
1437
|
+
for (Dictionary_it it = boost::begin(root_.members_); it != boost::end(root_.members_); it++) {
|
|
1438
|
+
update_simplex_tree_after_node_insertion(it);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
std::pair<typename boost::graph_traits<OneSkeletonGraph>::edge_iterator,
|
|
1442
|
+
typename boost::graph_traits<OneSkeletonGraph>::edge_iterator>
|
|
1443
|
+
boost_edges = edges(skel_graph);
|
|
1444
|
+
// boost_edges.first is the equivalent to boost_edges.begin()
|
|
1445
|
+
// boost_edges.second is the equivalent to boost_edges.end()
|
|
1446
|
+
for (; boost_edges.first != boost_edges.second; boost_edges.first++) {
|
|
1447
|
+
auto edge = *(boost_edges.first);
|
|
1448
|
+
auto u = source(edge, skel_graph);
|
|
1449
|
+
auto v = target(edge, skel_graph);
|
|
1450
|
+
if (u == v) throw std::invalid_argument("Self-loops are not simplicial");
|
|
1451
|
+
// We cannot skip edges with the wrong orientation and expect them to
|
|
1452
|
+
// come a second time with the right orientation, that does not always
|
|
1453
|
+
// happen in practice. emplace() should be a NOP when an element with the
|
|
1454
|
+
// same key is already there, so seeing the same edge multiple times is
|
|
1455
|
+
// ok.
|
|
1456
|
+
// Should we actually forbid multiple edges? That would be consistent
|
|
1457
|
+
// with rejecting self-loops.
|
|
1458
|
+
if (v < u) std::swap(u, v);
|
|
1459
|
+
auto sh = find_vertex(u);
|
|
1460
|
+
if (!has_children(sh)) {
|
|
1461
|
+
sh->second.assign_children(new Siblings(&root_, sh->first));
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
insert_node_<false, false, false>(sh->second.children(), v, get(edge_filtration_t(), skel_graph, edge));
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/** \brief Inserts several vertices.
|
|
1469
|
+
* @param[in] vertices A range of Vertex_handle
|
|
1470
|
+
* @param[in] filt filtration value of the new vertices (the same for all)
|
|
1471
|
+
*
|
|
1472
|
+
* This may be faster than inserting the vertices one by one, especially in a random order.
|
|
1473
|
+
* The complex does not need to be empty before calling this function. However, if a vertex is
|
|
1474
|
+
* already present, its filtration value is not modified, unlike with other insertion functions. */
|
|
1475
|
+
template <class VertexRange>
|
|
1476
|
+
void insert_batch_vertices(VertexRange const& vertices, const Filtration_value& filt = Filtration_value()) {
|
|
1477
|
+
auto verts = vertices | boost::adaptors::transformed([&](auto v) { return Dit_value_t(v, Node(&root_, filt)); });
|
|
1478
|
+
root_.members_.insert(boost::begin(verts), boost::end(verts));
|
|
1479
|
+
if (dimension_ < 0 && !root_.members_.empty()) dimension_ = 0;
|
|
1480
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
1481
|
+
for (auto sh = root_.members().begin(); sh != root_.members().end(); sh++) {
|
|
1482
|
+
// update newly inserted simplex (the one that are not linked)
|
|
1483
|
+
if (!sh->second.list_max_vertex_hook_.is_linked()) update_simplex_tree_after_node_insertion(sh);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/** \brief Expands the Simplex_tree containing only its one skeleton
|
|
1489
|
+
* until dimension max_dim.
|
|
1490
|
+
*
|
|
1491
|
+
* The expanded simplicial complex until dimension \f$d\f$
|
|
1492
|
+
* attached to a graph \f$G\f$ is the maximal simplicial complex of
|
|
1493
|
+
* dimension at most \f$d\f$ admitting the graph \f$G\f$ as \f$1\f$-skeleton.
|
|
1494
|
+
* The filtration value assigned to a simplex is the maximal filtration
|
|
1495
|
+
* value of one of its edges.
|
|
1496
|
+
*
|
|
1497
|
+
* The Simplex_tree must contain no simplex of dimension bigger than
|
|
1498
|
+
* 1 when calling the method. */
|
|
1499
|
+
void expansion(int max_dim) {
|
|
1500
|
+
if (max_dim <= 1) return;
|
|
1501
|
+
clear_filtration(); // Drop the cache.
|
|
1502
|
+
dimension_ = max_dim;
|
|
1503
|
+
for (Dictionary_it root_it = root_.members_.begin(); root_it != root_.members_.end(); ++root_it) {
|
|
1504
|
+
if (has_children(root_it)) {
|
|
1505
|
+
siblings_expansion(root_it->second.children(), max_dim - 1);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
dimension_ = max_dim - dimension_;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* @brief Adds a new vertex or a new edge in a flag complex, as well as all
|
|
1513
|
+
* simplices of its star, defined to maintain the property
|
|
1514
|
+
* of the complex to be a flag complex, truncated at dimension dim_max.
|
|
1515
|
+
* To insert a new edge, the two given vertex handles have to correspond
|
|
1516
|
+
* to the two end points of the edge. To insert a new vertex, the handles
|
|
1517
|
+
* have to be twice the same and correspond to the number you want assigned
|
|
1518
|
+
* to it. I.e., to insert vertex \f$i\f$, give \f$u = v = i\f$.
|
|
1519
|
+
* The method assumes that the given edge was not already contained in
|
|
1520
|
+
* the simplex tree, so the behaviour is undefined if called on an existing
|
|
1521
|
+
* edge. Also, the vertices of an edge have to be inserted before the edge.
|
|
1522
|
+
*
|
|
1523
|
+
* @param[in] u,v Vertex_handle representing the new edge
|
|
1524
|
+
* (@p v != @p u) or the new vertex (@p v == @p u).
|
|
1525
|
+
* @param[in] fil Filtration value of the edge.
|
|
1526
|
+
* @param[in] dim_max Maximal dimension of the expansion.
|
|
1527
|
+
* If set to -1, the expansion goes as far as possible.
|
|
1528
|
+
* @param[out] added_simplices Contains at the end all new
|
|
1529
|
+
* simplices induced by the insertion of the edge.
|
|
1530
|
+
* The container is not emptied and new simplices are
|
|
1531
|
+
* appended at the end.
|
|
1532
|
+
*
|
|
1533
|
+
* @pre `SimplexTreeOptions::link_nodes_by_label` must be true.
|
|
1534
|
+
* @pre When inserting the edge `[u,v]`, the vertices @p u and @p v have to be
|
|
1535
|
+
* already inserted in the simplex tree.
|
|
1536
|
+
*
|
|
1537
|
+
* @warning If the edges and vertices are not inserted in the order of their
|
|
1538
|
+
* filtration values, the method `make_filtration_non_decreasing()` has to be
|
|
1539
|
+
* called at the end of the insertions to restore the intended filtration.
|
|
1540
|
+
* Note that even then, an edge has to be inserted after its vertices.
|
|
1541
|
+
* @warning The method assumes that the given edge or vertex was not already
|
|
1542
|
+
* contained in the simplex tree, so the behaviour is undefined if called on
|
|
1543
|
+
* an existing simplex.
|
|
1544
|
+
*/
|
|
1545
|
+
void insert_edge_as_flag(Vertex_handle u,
|
|
1546
|
+
Vertex_handle v,
|
|
1547
|
+
const Filtration_value& fil,
|
|
1548
|
+
int dim_max,
|
|
1549
|
+
std::vector<Simplex_handle>& added_simplices) {
|
|
1550
|
+
/**
|
|
1551
|
+
* In term of edges in the graph, inserting edge `[u,v]` only affects
|
|
1552
|
+
* the subtree rooted at @p u.
|
|
1553
|
+
*
|
|
1554
|
+
* For a new node with label @p v, we first do a local expansion for
|
|
1555
|
+
* computing the children of this new node, and then a standard expansion
|
|
1556
|
+
* for its children.
|
|
1557
|
+
* Nodes with label @p v (and their subtrees) already in the tree
|
|
1558
|
+
* do not get affected.
|
|
1559
|
+
*
|
|
1560
|
+
* Nodes with label @p u get affected only if a Node with label @p v is in their same
|
|
1561
|
+
* siblings set.
|
|
1562
|
+
* We then try to insert "punctually" @p v all over the subtree rooted
|
|
1563
|
+
* at `Node(u)`. Each insertion of a Node with @p v label induces a local
|
|
1564
|
+
* expansion at this Node (as explained above) and a sequence of "punctual"
|
|
1565
|
+
* insertion of `Node(v)` in the subtree rooted at sibling nodes of the new node,
|
|
1566
|
+
* on its left.
|
|
1567
|
+
*/
|
|
1568
|
+
|
|
1569
|
+
static_assert(Options::link_nodes_by_label, "Options::link_nodes_by_label must be true");
|
|
1570
|
+
|
|
1571
|
+
if (u == v) { // Are we inserting a vertex?
|
|
1572
|
+
auto res_ins = insert_node_<false, false, false>(&root_, u, fil);
|
|
1573
|
+
if (res_ins.second) { // if the vertex is not in the complex, insert it
|
|
1574
|
+
added_simplices.push_back(res_ins.first); // no more insert in root_.members()
|
|
1575
|
+
if (dimension_ == -1) dimension_ = 0;
|
|
1576
|
+
}
|
|
1577
|
+
return; // because the vertex is isolated, no more insertions.
|
|
1578
|
+
}
|
|
1579
|
+
// else, we are inserting an edge: ensure that u < v
|
|
1580
|
+
if (v < u) {
|
|
1581
|
+
std::swap(u, v);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Note that we copy Simplex_handle (aka map iterators) in added_simplices
|
|
1585
|
+
// while we are still modifying the Simplex_tree. Insertions in siblings may
|
|
1586
|
+
// invalidate Simplex_handles; we take care of this fact by first doing all
|
|
1587
|
+
// insertion in a Sibling, then inserting all handles in added_simplices.
|
|
1588
|
+
|
|
1589
|
+
#ifdef GUDHI_DEBUG
|
|
1590
|
+
// check whether vertices u and v are in the tree. If not, return an error.
|
|
1591
|
+
auto sh_u = root_.members().find(u);
|
|
1592
|
+
GUDHI_CHECK(sh_u != root_.members().end() && root_.members().find(v) != root_.members().end(),
|
|
1593
|
+
std::invalid_argument(
|
|
1594
|
+
"Simplex_tree::insert_edge_as_flag - inserts an edge whose vertices are not in the complex"));
|
|
1595
|
+
GUDHI_CHECK(
|
|
1596
|
+
!has_children(sh_u) || sh_u->second.children()->members().find(v) == sh_u->second.children()->members().end(),
|
|
1597
|
+
std::invalid_argument("Simplex_tree::insert_edge_as_flag - inserts an already existing edge"));
|
|
1598
|
+
#endif
|
|
1599
|
+
|
|
1600
|
+
// to update dimension
|
|
1601
|
+
const auto tmp_dim = dimension_;
|
|
1602
|
+
auto tmp_max_dim = dimension_;
|
|
1603
|
+
|
|
1604
|
+
// for all siblings containing a Node labeled with u (including the root), run
|
|
1605
|
+
// compute_punctual_expansion
|
|
1606
|
+
// todo parallelize
|
|
1607
|
+
List_max_vertex* nodes_with_label_u = nodes_by_label(u); // all Nodes with u label
|
|
1608
|
+
|
|
1609
|
+
GUDHI_CHECK(nodes_with_label_u != nullptr,
|
|
1610
|
+
"Simplex_tree::insert_edge_as_flag - cannot find the list of Nodes with label u");
|
|
1611
|
+
|
|
1612
|
+
for (auto&& node_as_hook : *nodes_with_label_u) {
|
|
1613
|
+
Node& node_u = static_cast<Node&>(node_as_hook); // corresponding node, has label u
|
|
1614
|
+
Simplex_handle sh_u = simplex_handle_from_node(node_u);
|
|
1615
|
+
Siblings* sib_u = self_siblings(sh_u);
|
|
1616
|
+
if (sib_u->members().find(v) != sib_u->members().end()) { // v is the label of a sibling of node_u
|
|
1617
|
+
int curr_dim = dimension(sib_u);
|
|
1618
|
+
if (dim_max == -1 || curr_dim < dim_max) {
|
|
1619
|
+
if (!has_children(sh_u)) {
|
|
1620
|
+
// then node_u was a leaf and now has a new child Node labeled v
|
|
1621
|
+
// the child v is created in compute_punctual_expansion
|
|
1622
|
+
node_u.assign_children(new Siblings(sib_u, u));
|
|
1623
|
+
}
|
|
1624
|
+
dimension_ = dim_max - curr_dim - 1;
|
|
1625
|
+
compute_punctual_expansion(v,
|
|
1626
|
+
node_u.children(),
|
|
1627
|
+
fil,
|
|
1628
|
+
dim_max - curr_dim - 1, //>= 0 if dim_max >= 0, <0 otherwise
|
|
1629
|
+
added_simplices);
|
|
1630
|
+
dimension_ = dim_max - dimension_;
|
|
1631
|
+
if (dimension_ > tmp_max_dim) tmp_max_dim = dimension_;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (tmp_dim <= tmp_max_dim) {
|
|
1636
|
+
dimension_ = tmp_max_dim;
|
|
1637
|
+
dimension_to_be_lowered_ = false;
|
|
1638
|
+
} else {
|
|
1639
|
+
dimension_ = tmp_dim;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
private:
|
|
1644
|
+
/** \brief Inserts a Node with label @p v in the set of siblings sib, and percolate the
|
|
1645
|
+
* expansion on the subtree rooted at sib. Sibling sib must not contain
|
|
1646
|
+
* @p v.
|
|
1647
|
+
* The percolation of the expansion is twofold:
|
|
1648
|
+
* 1- the newly inserted Node labeled @p v in sib has a subtree computed
|
|
1649
|
+
* via create_local_expansion.
|
|
1650
|
+
* 2- All Node in the members of sib, with label @p x and @p x < @p v,
|
|
1651
|
+
* need in turn a local_expansion by @p v iff N^+(x) contains @p v.
|
|
1652
|
+
*/
|
|
1653
|
+
void compute_punctual_expansion(
|
|
1654
|
+
Vertex_handle v,
|
|
1655
|
+
Siblings* sib,
|
|
1656
|
+
const Filtration_value& fil,
|
|
1657
|
+
int k // k == dim_max - dimension simplices in sib
|
|
1658
|
+
,
|
|
1659
|
+
std::vector<Simplex_handle>&
|
|
1660
|
+
added_simplices) { // insertion always succeeds because the edge {u,v} used to not be here.
|
|
1661
|
+
auto res_ins_v = sib->members().emplace(v, Node(sib, fil));
|
|
1662
|
+
added_simplices.push_back(res_ins_v.first); // no more insertion in sib
|
|
1663
|
+
update_simplex_tree_after_node_insertion(res_ins_v.first);
|
|
1664
|
+
|
|
1665
|
+
if (k == 0) { // reached the maximal dimension. if max_dim == -1, k is never equal to 0.
|
|
1666
|
+
dimension_ = 0; // to keep track of the max height of the recursion tree
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// create the subtree of new Node(v)
|
|
1671
|
+
create_local_expansion(res_ins_v.first, sib, fil, k, added_simplices);
|
|
1672
|
+
|
|
1673
|
+
// punctual expansion in nodes on the left of v, i.e. with label x < v
|
|
1674
|
+
for (auto sh = sib->members().begin(); sh != res_ins_v.first; ++sh) { // if v belongs to N^+(x), punctual expansion
|
|
1675
|
+
Simplex_handle root_sh = find_vertex(sh->first); // Node(x), x < v
|
|
1676
|
+
if (has_children(root_sh) && root_sh->second.children()->members().find(v) !=
|
|
1677
|
+
root_sh->second.children()->members().end()) { // edge {x,v} is in the complex
|
|
1678
|
+
if (!has_children(sh)) {
|
|
1679
|
+
sh->second.assign_children(new Siblings(sib, sh->first));
|
|
1680
|
+
}
|
|
1681
|
+
// insert v in the children of sh, and expand.
|
|
1682
|
+
compute_punctual_expansion(v, sh->second.children(), fil, k - 1, added_simplices);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/** \brief After the insertion of edge `{u,v}`, expansion of a subtree rooted at @p v, where the
|
|
1688
|
+
* Node with label @p v has just been inserted, and its parent is a Node labeled with
|
|
1689
|
+
* @p u. sh has no children here.
|
|
1690
|
+
*
|
|
1691
|
+
* k must be > 0
|
|
1692
|
+
*/
|
|
1693
|
+
void create_local_expansion(Simplex_handle sh_v // Node with label v which has just been inserted
|
|
1694
|
+
,
|
|
1695
|
+
Siblings* curr_sib // Siblings containing the node sh_v
|
|
1696
|
+
,
|
|
1697
|
+
const Filtration_value& fil_uv // Fil value of the edge uv in the zz filtration
|
|
1698
|
+
,
|
|
1699
|
+
int k // Stopping condition for recursion based on max dim
|
|
1700
|
+
,
|
|
1701
|
+
std::vector<Simplex_handle>& added_simplices) // range of all new simplices
|
|
1702
|
+
{ // pick N^+(v)
|
|
1703
|
+
// intersect N^+(v) with labels y > v in curr_sib
|
|
1704
|
+
Simplex_handle next_it = sh_v;
|
|
1705
|
+
++next_it;
|
|
1706
|
+
|
|
1707
|
+
if (dimension_ > k) {
|
|
1708
|
+
dimension_ = k; // to keep track of the max height of the recursion tree
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
create_expansion<true>(curr_sib, sh_v, next_it, fil_uv, k, &added_simplices);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// TODO boost::container::ordered_unique_range_t in the creation of a Siblings
|
|
1715
|
+
|
|
1716
|
+
/** \brief Global expansion of a subtree in the simplex tree.
|
|
1717
|
+
*
|
|
1718
|
+
* The filtration value is absolute and defined by `Filtration_value fil`.
|
|
1719
|
+
* The new Node are also connected appropriately in the coface
|
|
1720
|
+
* data structure.
|
|
1721
|
+
*
|
|
1722
|
+
* Only called in the case of `void insert_edge_as_flag(...)`.
|
|
1723
|
+
*/
|
|
1724
|
+
void siblings_expansion(Siblings* siblings // must contain elements
|
|
1725
|
+
,
|
|
1726
|
+
const Filtration_value& fil,
|
|
1727
|
+
int k // == max_dim expansion - dimension curr siblings
|
|
1728
|
+
,
|
|
1729
|
+
std::vector<Simplex_handle>& added_simplices) {
|
|
1730
|
+
if (dimension_ > k) {
|
|
1731
|
+
dimension_ = k; // to keep track of the max height of the recursion tree
|
|
1732
|
+
}
|
|
1733
|
+
if (k == 0) {
|
|
1734
|
+
return;
|
|
1735
|
+
} // max dimension
|
|
1736
|
+
Dictionary_it next = ++(siblings->members().begin());
|
|
1737
|
+
|
|
1738
|
+
for (Dictionary_it s_h = siblings->members().begin(); next != siblings->members().end();
|
|
1739
|
+
++s_h, ++next) { // find N^+(s_h)
|
|
1740
|
+
create_expansion<true>(siblings, s_h, next, fil, k, &added_simplices);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/** \brief Recursive expansion of the simplex tree.
|
|
1745
|
+
* Only called in the case of `void expansion(int max_dim)`. */
|
|
1746
|
+
void siblings_expansion(Siblings* siblings, // must contain elements
|
|
1747
|
+
int k) {
|
|
1748
|
+
if (k >= 0 && dimension_ > k) {
|
|
1749
|
+
dimension_ = k;
|
|
1750
|
+
}
|
|
1751
|
+
if (k == 0) return;
|
|
1752
|
+
Dictionary_it next = siblings->members().begin();
|
|
1753
|
+
++next;
|
|
1754
|
+
|
|
1755
|
+
for (Dictionary_it s_h = siblings->members().begin(); s_h != siblings->members().end(); ++s_h, ++next) {
|
|
1756
|
+
create_expansion<false>(siblings, s_h, next, s_h->second.filtration(), k);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/** \brief Recursive expansion of the simplex tree.
|
|
1761
|
+
* The method is used with `force_filtration_value == true` by `void insert_edge_as_flag(...)` and with
|
|
1762
|
+
* `force_filtration_value == false` by `void expansion(int max_dim)`. Therefore, `added_simplices` is assumed
|
|
1763
|
+
* to bon non-null in the first case and null in the second.*/
|
|
1764
|
+
template <bool force_filtration_value>
|
|
1765
|
+
void create_expansion(Siblings* siblings,
|
|
1766
|
+
Dictionary_it& s_h,
|
|
1767
|
+
Dictionary_it& next,
|
|
1768
|
+
const Filtration_value& fil,
|
|
1769
|
+
int k,
|
|
1770
|
+
std::vector<Simplex_handle>* added_simplices = nullptr) {
|
|
1771
|
+
Simplex_handle root_sh = find_vertex(s_h->first);
|
|
1772
|
+
thread_local std::vector<std::pair<Vertex_handle, Node>> inter;
|
|
1773
|
+
|
|
1774
|
+
if (!has_children(root_sh)) return;
|
|
1775
|
+
|
|
1776
|
+
intersection<force_filtration_value>(inter, // output intersection
|
|
1777
|
+
next, // begin
|
|
1778
|
+
siblings->members().end(), // end
|
|
1779
|
+
root_sh->second.children()->members().begin(),
|
|
1780
|
+
root_sh->second.children()->members().end(),
|
|
1781
|
+
fil);
|
|
1782
|
+
if (inter.size() != 0) {
|
|
1783
|
+
Siblings* new_sib = new Siblings(siblings, // oncles
|
|
1784
|
+
s_h->first, // parent
|
|
1785
|
+
inter); // boost::container::ordered_unique_range_t
|
|
1786
|
+
for (auto it = new_sib->members().begin(); it != new_sib->members().end(); ++it) {
|
|
1787
|
+
update_simplex_tree_after_node_insertion(it);
|
|
1788
|
+
if constexpr (force_filtration_value) {
|
|
1789
|
+
// the way create_expansion is used, added_simplices != nullptr when force_filtration_value == true
|
|
1790
|
+
added_simplices->push_back(it);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
inter.clear();
|
|
1794
|
+
s_h->second.assign_children(new_sib);
|
|
1795
|
+
if constexpr (force_filtration_value) {
|
|
1796
|
+
siblings_expansion(new_sib, fil, k - 1, *added_simplices);
|
|
1797
|
+
} else {
|
|
1798
|
+
siblings_expansion(new_sib, k - 1);
|
|
1799
|
+
}
|
|
1800
|
+
} else {
|
|
1801
|
+
// ensure the children property
|
|
1802
|
+
s_h->second.assign_children(siblings);
|
|
1803
|
+
inter.clear();
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/** \brief Intersects Dictionary 1 [begin1;end1) with Dictionary 2 [begin2,end2)
|
|
1808
|
+
* and assigns the maximal possible Filtration_value to the Nodes. */
|
|
1809
|
+
template <bool force_filtration_value = false>
|
|
1810
|
+
static void intersection(std::vector<std::pair<Vertex_handle, Node>>& intersection,
|
|
1811
|
+
Dictionary_it begin1,
|
|
1812
|
+
Dictionary_it end1,
|
|
1813
|
+
Dictionary_it begin2,
|
|
1814
|
+
Dictionary_it end2,
|
|
1815
|
+
const Filtration_value& filtration_) {
|
|
1816
|
+
if (begin1 == end1 || begin2 == end2) return; // ----->>
|
|
1817
|
+
while (true) {
|
|
1818
|
+
if (begin1->first == begin2->first) {
|
|
1819
|
+
if constexpr (force_filtration_value) {
|
|
1820
|
+
intersection.emplace_back(begin1->first, Node(nullptr, filtration_));
|
|
1821
|
+
} else {
|
|
1822
|
+
Filtration_value filt = begin1->second.filtration();
|
|
1823
|
+
intersect_lifetimes(filt, begin2->second.filtration());
|
|
1824
|
+
intersect_lifetimes(filt, filtration_);
|
|
1825
|
+
intersection.emplace_back(begin1->first, Node(nullptr, filt));
|
|
1826
|
+
}
|
|
1827
|
+
if (++begin1 == end1 || ++begin2 == end2) return; // ----->>
|
|
1828
|
+
} else if (begin1->first < begin2->first) {
|
|
1829
|
+
if (++begin1 == end1) return;
|
|
1830
|
+
} else /* begin1->first > begin2->first */ {
|
|
1831
|
+
if (++begin2 == end2) return; // ----->>
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
public:
|
|
1837
|
+
/** \brief Expands a simplex tree containing only a graph. Simplices corresponding to cliques in the graph are added
|
|
1838
|
+
* incrementally, faces before cofaces, unless the simplex has dimension larger than `max_dim` or `block_simplex`
|
|
1839
|
+
* returns true for this simplex.
|
|
1840
|
+
*
|
|
1841
|
+
* @param[in] max_dim Expansion maximal dimension value.
|
|
1842
|
+
* @param[in] block_simplex Blocker oracle. Its concept is <CODE>bool block_simplex(Simplex_handle sh)</CODE>
|
|
1843
|
+
*
|
|
1844
|
+
* The function identifies a candidate simplex whose faces are all already in the complex, inserts
|
|
1845
|
+
* it with a filtration value corresponding to the maximum of the filtration values of the faces, then calls
|
|
1846
|
+
* `block_simplex` on a `Simplex_handle` for this new simplex. If `block_simplex` returns true, the simplex is
|
|
1847
|
+
* removed, otherwise it is kept. Note that the evaluation of `block_simplex` is a good time to update the
|
|
1848
|
+
* filtration value of the simplex if you want a customized value. The algorithm then proceeds with the next
|
|
1849
|
+
* candidate.
|
|
1850
|
+
*
|
|
1851
|
+
* @warning several candidates of the same dimension may be inserted simultaneously before calling `block_simplex`,
|
|
1852
|
+
* so if you examine the complex in `block_simplex`, you may hit a few simplices of the same dimension that have not
|
|
1853
|
+
* been vetted by `block_simplex` yet, or have already been rejected but not yet removed.
|
|
1854
|
+
*/
|
|
1855
|
+
template <typename Blocker>
|
|
1856
|
+
void expansion_with_blockers(int max_dim, Blocker block_simplex) {
|
|
1857
|
+
// Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
|
|
1858
|
+
for (auto& simplex : boost::adaptors::reverse(root_.members())) {
|
|
1859
|
+
if (has_children(&simplex)) {
|
|
1860
|
+
siblings_expansion_with_blockers(simplex.second.children(), max_dim, max_dim - 1, block_simplex);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
private:
|
|
1866
|
+
/** \brief Recursive expansion with blockers of the simplex tree.*/
|
|
1867
|
+
template <typename Blocker>
|
|
1868
|
+
void siblings_expansion_with_blockers(Siblings* siblings, int max_dim, int k, Blocker block_simplex) {
|
|
1869
|
+
if (dimension_ < max_dim - k) {
|
|
1870
|
+
dimension_ = max_dim - k;
|
|
1871
|
+
}
|
|
1872
|
+
if (k == 0) return;
|
|
1873
|
+
// No need to go deeper
|
|
1874
|
+
if (siblings->members().size() < 2) return;
|
|
1875
|
+
// Reverse loop starting before the last one for 'next' to be the last one
|
|
1876
|
+
for (auto simplex = std::next(siblings->members().rbegin()); simplex != siblings->members().rend(); simplex++) {
|
|
1877
|
+
std::vector<std::pair<Vertex_handle, Node>> intersection;
|
|
1878
|
+
for (auto next = siblings->members().rbegin(); next != simplex; next++) {
|
|
1879
|
+
bool to_be_inserted = true;
|
|
1880
|
+
Filtration_value filt = simplex->second.filtration();
|
|
1881
|
+
// If all the boundaries are present, 'next' needs to be inserted
|
|
1882
|
+
for (Simplex_handle border : boundary_simplex_range(simplex)) {
|
|
1883
|
+
Simplex_handle border_child = find_child(border, next->first);
|
|
1884
|
+
if (border_child == null_simplex()) {
|
|
1885
|
+
to_be_inserted = false;
|
|
1886
|
+
break;
|
|
1887
|
+
}
|
|
1888
|
+
intersect_lifetimes(filt, filtration(border_child));
|
|
1889
|
+
}
|
|
1890
|
+
if (to_be_inserted) {
|
|
1891
|
+
intersection.emplace_back(next->first, Node(nullptr, filt));
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (intersection.size() != 0) {
|
|
1895
|
+
// Reverse the order to insert
|
|
1896
|
+
Siblings* new_sib =
|
|
1897
|
+
new Siblings(siblings, // oncles
|
|
1898
|
+
simplex->first, // parent
|
|
1899
|
+
boost::adaptors::reverse(intersection)); // boost::container::ordered_unique_range_t
|
|
1900
|
+
simplex->second.assign_children(new_sib);
|
|
1901
|
+
std::vector<Vertex_handle> blocked_new_sib_vertex_list;
|
|
1902
|
+
// As all intersections are inserted, we can call the blocker function on all new_sib members
|
|
1903
|
+
for (auto new_sib_member = new_sib->members().begin(); new_sib_member != new_sib->members().end();
|
|
1904
|
+
new_sib_member++) {
|
|
1905
|
+
update_simplex_tree_after_node_insertion(new_sib_member);
|
|
1906
|
+
bool blocker_result = block_simplex(new_sib_member);
|
|
1907
|
+
// new_sib member has been blocked by the blocker function
|
|
1908
|
+
// add it to the list to be removed - do not perform it while looping on it
|
|
1909
|
+
if (blocker_result) {
|
|
1910
|
+
blocked_new_sib_vertex_list.push_back(new_sib_member->first);
|
|
1911
|
+
// update data structures for all deleted simplices
|
|
1912
|
+
// can be done in the loop as part of another data structure
|
|
1913
|
+
update_simplex_tree_before_node_removal(new_sib_member);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
if (blocked_new_sib_vertex_list.size() == new_sib->members().size()) {
|
|
1917
|
+
// Specific case where all have to be deleted
|
|
1918
|
+
delete new_sib;
|
|
1919
|
+
// ensure the children property
|
|
1920
|
+
simplex->second.assign_children(siblings);
|
|
1921
|
+
} else {
|
|
1922
|
+
for (auto& blocked_new_sib_member : blocked_new_sib_vertex_list) {
|
|
1923
|
+
new_sib->members().erase(blocked_new_sib_member);
|
|
1924
|
+
}
|
|
1925
|
+
// ensure recursive call
|
|
1926
|
+
siblings_expansion_with_blockers(new_sib, max_dim, k - 1, block_simplex);
|
|
1927
|
+
}
|
|
1928
|
+
} else {
|
|
1929
|
+
// ensure the children property
|
|
1930
|
+
simplex->second.assign_children(siblings);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
/** \private Returns the Simplex_handle composed of the vertex list (from the Simplex_handle), plus the given
|
|
1936
|
+
* Vertex_handle if the Vertex_handle is found in the Simplex_handle children list.
|
|
1937
|
+
* Returns null_simplex() if it does not exist
|
|
1938
|
+
*/
|
|
1939
|
+
Simplex_handle find_child(Simplex_handle sh, Vertex_handle vh) const {
|
|
1940
|
+
if (!has_children(sh)) return null_simplex();
|
|
1941
|
+
|
|
1942
|
+
Simplex_handle child = sh->second.children()->find(vh);
|
|
1943
|
+
// Specific case of boost::flat_map does not find, returns boost::flat_map::end()
|
|
1944
|
+
// in simplex tree we want a null_simplex()
|
|
1945
|
+
if (child == sh->second.children()->members().end()) return null_simplex();
|
|
1946
|
+
|
|
1947
|
+
return child;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
public:
|
|
1951
|
+
/** \brief Write the hasse diagram of the simplicial complex in os.
|
|
1952
|
+
*
|
|
1953
|
+
* Each row in the file correspond to a simplex. A line is written:
|
|
1954
|
+
* dim idx_1 ... idx_k fil where dim is the dimension of the simplex,
|
|
1955
|
+
* idx_1 ... idx_k are the row index (starting from 0) of the simplices of the boundary
|
|
1956
|
+
* of the simplex, and fil is its filtration value. */
|
|
1957
|
+
void print_hasse(std::ostream& os) {
|
|
1958
|
+
os << num_simplices() << " " << std::endl;
|
|
1959
|
+
for (auto sh : filtration_simplex_range()) {
|
|
1960
|
+
os << dimension(sh) << " ";
|
|
1961
|
+
for (auto b_sh : boundary_simplex_range(sh)) {
|
|
1962
|
+
os << key(b_sh) << " ";
|
|
1963
|
+
}
|
|
1964
|
+
os << filtration(sh) << " \n";
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
public:
|
|
1969
|
+
/** Calls a function on each simplex. The order ensures that faces are visited before cofaces.
|
|
1970
|
+
* While it is fine to modify the data of a simplex (filtration, key) in the function, modifying
|
|
1971
|
+
* the structure itself (insertion, removal) is not supported.
|
|
1972
|
+
*
|
|
1973
|
+
* @param[in] fun Function that takes as argument a Simplex_handle and an int (representing the dimension of this
|
|
1974
|
+
* simplex). It may return void or bool, and in the second case returning true means that the iteration will skip
|
|
1975
|
+
* the children of this simplex (a subset of the cofaces).
|
|
1976
|
+
*/
|
|
1977
|
+
template <class Fun>
|
|
1978
|
+
void for_each_simplex(Fun&& fun) {
|
|
1979
|
+
// Wrap callback so it always returns bool
|
|
1980
|
+
auto f = [&fun](Simplex_handle sh, int dim) -> bool {
|
|
1981
|
+
if constexpr (std::is_same_v<void, decltype(fun(sh, dim))>) {
|
|
1982
|
+
fun(sh, dim);
|
|
1983
|
+
return false;
|
|
1984
|
+
} else {
|
|
1985
|
+
return fun(sh, dim);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
if (!is_empty()) rec_for_each_simplex(root(), 0, f);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
private:
|
|
1992
|
+
template <class Fun>
|
|
1993
|
+
void rec_for_each_simplex(Siblings* sib, int dim, Fun&& fun) {
|
|
1994
|
+
Simplex_handle sh = sib->members().end();
|
|
1995
|
+
GUDHI_CHECK(sh != sib->members().begin(), "Bug in Gudhi: only the root siblings may be empty");
|
|
1996
|
+
do {
|
|
1997
|
+
--sh;
|
|
1998
|
+
if (!fun(sh, dim) && has_children(sh)) {
|
|
1999
|
+
rec_for_each_simplex(sh->second.children(), dim + 1, fun);
|
|
2000
|
+
}
|
|
2001
|
+
// We could skip checking has_children for the first element of the iteration, we know it returns false.
|
|
2002
|
+
} while (sh != sib->members().begin());
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
public:
|
|
2006
|
+
/** \brief This function ensures that each simplex has a higher filtration value than its faces by increasing the
|
|
2007
|
+
* filtration values.
|
|
2008
|
+
* @return True if any filtration value was modified, false if the filtration was already non-decreasing.
|
|
2009
|
+
*
|
|
2010
|
+
* If a simplex has a `NaN` filtration value, it is considered lower than any other defined filtration value.
|
|
2011
|
+
*/
|
|
2012
|
+
bool make_filtration_non_decreasing() {
|
|
2013
|
+
bool modified = false;
|
|
2014
|
+
auto fun = [&modified, this](Simplex_handle sh, int dim) -> void {
|
|
2015
|
+
if (dim == 0) return;
|
|
2016
|
+
|
|
2017
|
+
Filtration_value& current_filt = sh->second.filtration();
|
|
2018
|
+
|
|
2019
|
+
// Find the maximum filtration value in the border and assigns it if it is greater than the current
|
|
2020
|
+
for (Simplex_handle b : boundary_simplex_range(sh)) {
|
|
2021
|
+
// considers NaN as the lowest possible value.
|
|
2022
|
+
// stores the filtration modification information
|
|
2023
|
+
modified |= intersect_lifetimes(current_filt, b->second.filtration());
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
// Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
|
|
2027
|
+
for_each_simplex(fun);
|
|
2028
|
+
|
|
2029
|
+
if (modified) clear_filtration(); // Drop the cache.
|
|
2030
|
+
return modified;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
public:
|
|
2034
|
+
/** \brief Remove all the simplices, leaving an empty complex. */
|
|
2035
|
+
void clear() {
|
|
2036
|
+
root_members_recursive_deletion();
|
|
2037
|
+
clear_filtration();
|
|
2038
|
+
dimension_ = -1;
|
|
2039
|
+
dimension_to_be_lowered_ = false;
|
|
2040
|
+
if constexpr (Options::link_nodes_by_label) nodes_label_to_list_.clear();
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
/** \brief Prune above filtration value given as parameter. That is: if \f$ f \f$ is the given filtration value
|
|
2044
|
+
* and \f$ f_s \f$ the filtration value associated to the simplex \f$ s \f$ contained in the tree, \f$ s \f$ is
|
|
2045
|
+
* removed if and only if \f$ f < f_s \f$ or \f$ f_s = NaN \f$, where \f$ < \f$ is the
|
|
2046
|
+
* @ref FiltrationValue::operator< "operator<" defined for the type
|
|
2047
|
+
* @ref SimplexTreeOptions::Filtration_value "Simplex_tree::Filtration_value".
|
|
2048
|
+
* @param[in] filtration Maximum threshold value.
|
|
2049
|
+
* @return True if any simplex was removed, false if all simplices already had a value below the threshold.
|
|
2050
|
+
* \post Note that the dimension of the simplicial complex may be lower after calling `prune_above_filtration()`
|
|
2051
|
+
* than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper
|
|
2052
|
+
* bound. If you care, you can call `dimension()` to recompute the exact dimension.
|
|
2053
|
+
*/
|
|
2054
|
+
bool prune_above_filtration(const Filtration_value& filtration) {
|
|
2055
|
+
if (filtration == Filtration_simplex_base_real::get_infinity()) return false; // ---->>
|
|
2056
|
+
bool modified = rec_prune_above_filtration(root(), filtration);
|
|
2057
|
+
if (modified) clear_filtration(); // Drop the cache.
|
|
2058
|
+
return modified;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
private:
|
|
2062
|
+
void make_subfiltration_non_decreasing(Simplex_handle sh) {
|
|
2063
|
+
static_assert(!Filtration_value::is_multicritical(), "Not implemented yet.");
|
|
2064
|
+
Filtration_value& stuff = filtration_mutable(sh);
|
|
2065
|
+
for (auto sh_gosse : boundary_simplex_range(sh)) {
|
|
2066
|
+
make_subfiltration_non_decreasing(sh_gosse);
|
|
2067
|
+
intersect_lifetimes(stuff, filtration(sh_gosse));
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
bool rec_prune_above_filtration(Siblings* sib, const Filtration_value& filt) {
|
|
2072
|
+
auto&& list = sib->members();
|
|
2073
|
+
bool modified = false;
|
|
2074
|
+
bool emptied = false;
|
|
2075
|
+
Simplex_handle last;
|
|
2076
|
+
|
|
2077
|
+
auto to_remove = [this, filt](Dit_value_t& simplex) {
|
|
2078
|
+
// if filt and simplex.second.filtration() are non comparable, should return false.
|
|
2079
|
+
// if simplex.second.filtration() is NaN, should return true.
|
|
2080
|
+
if (filt < simplex.second.filtration() || !(simplex.second.filtration() == simplex.second.filtration())) {
|
|
2081
|
+
if (has_children(&simplex)) rec_delete(simplex.second.children());
|
|
2082
|
+
// dimension may need to be lowered
|
|
2083
|
+
dimension_to_be_lowered_ = true;
|
|
2084
|
+
return true;
|
|
2085
|
+
}
|
|
2086
|
+
return false;
|
|
2087
|
+
};
|
|
2088
|
+
|
|
2089
|
+
// TODO: `if constexpr` replaceable by `std::erase_if` in C++20? Has a risk of additional runtime,
|
|
2090
|
+
// so to benchmark first.
|
|
2091
|
+
if constexpr (Options::stable_simplex_handles) {
|
|
2092
|
+
modified = false;
|
|
2093
|
+
for (auto sh = list.begin(); sh != list.end();) {
|
|
2094
|
+
if (to_remove(*sh)) {
|
|
2095
|
+
sh = list.erase(sh);
|
|
2096
|
+
modified = true;
|
|
2097
|
+
} else {
|
|
2098
|
+
++sh;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
emptied = (list.empty() && sib != root());
|
|
2102
|
+
} else {
|
|
2103
|
+
last = std::remove_if(list.begin(), list.end(), to_remove);
|
|
2104
|
+
modified = (last != list.end());
|
|
2105
|
+
emptied = (last == list.begin() && sib != root());
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
if (emptied) {
|
|
2109
|
+
// Removing the whole siblings, parent becomes a leaf.
|
|
2110
|
+
sib->oncles()->members()[sib->parent()].assign_children(sib->oncles());
|
|
2111
|
+
delete sib;
|
|
2112
|
+
// dimension may need to be lowered
|
|
2113
|
+
dimension_to_be_lowered_ = true;
|
|
2114
|
+
return true;
|
|
2115
|
+
} else {
|
|
2116
|
+
// Keeping some elements of siblings. Remove the others, and recurse in the remaining ones.
|
|
2117
|
+
if constexpr (!Options::stable_simplex_handles) list.erase(last, list.end());
|
|
2118
|
+
for (auto&& simplex : list)
|
|
2119
|
+
if (has_children(&simplex)) modified |= rec_prune_above_filtration(simplex.second.children(), filt);
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
return modified;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
public:
|
|
2126
|
+
/** \brief Remove all simplices of dimension greater than a given value.
|
|
2127
|
+
* @param[in] dimension Maximum dimension value.
|
|
2128
|
+
* @return True if any simplex was removed, false if all simplices already had a value below the dimension.
|
|
2129
|
+
*/
|
|
2130
|
+
bool prune_above_dimension(int dimension) {
|
|
2131
|
+
if (dimension >= dimension_) return false;
|
|
2132
|
+
|
|
2133
|
+
bool modified = false;
|
|
2134
|
+
if (dimension < 0) {
|
|
2135
|
+
if (num_vertices() > 0) {
|
|
2136
|
+
root_members_recursive_deletion();
|
|
2137
|
+
modified = true;
|
|
2138
|
+
}
|
|
2139
|
+
// Force dimension to -1, in case user calls `prune_above_dimension(-10)`
|
|
2140
|
+
dimension = -1;
|
|
2141
|
+
} else {
|
|
2142
|
+
modified = rec_prune_above_dimension(root(), dimension, 0);
|
|
2143
|
+
}
|
|
2144
|
+
if (modified) {
|
|
2145
|
+
// Thanks to `if (dimension >= dimension_)` and dimension forced to -1 `if (dimension < 0)`, we know the new
|
|
2146
|
+
// dimension
|
|
2147
|
+
dimension_ = dimension;
|
|
2148
|
+
clear_filtration(); // Drop the cache.
|
|
2149
|
+
}
|
|
2150
|
+
return modified;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
private:
|
|
2154
|
+
bool rec_prune_above_dimension(Siblings* sib, int dim, int actual_dim) {
|
|
2155
|
+
bool modified = false;
|
|
2156
|
+
auto&& list = sib->members();
|
|
2157
|
+
|
|
2158
|
+
for (auto&& simplex : list)
|
|
2159
|
+
if (has_children(&simplex)) {
|
|
2160
|
+
if (actual_dim >= dim) {
|
|
2161
|
+
rec_delete(simplex.second.children());
|
|
2162
|
+
simplex.second.assign_children(sib);
|
|
2163
|
+
modified = true;
|
|
2164
|
+
} else {
|
|
2165
|
+
modified |= rec_prune_above_dimension(simplex.second.children(), dim, actual_dim + 1);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
return modified;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
private:
|
|
2173
|
+
/** \brief Deep search simplex tree dimension recompute.
|
|
2174
|
+
* @return True if the dimension was modified, false otherwise.
|
|
2175
|
+
* \pre Be sure the simplex tree has not a too low dimension value as the deep search stops when the former dimension
|
|
2176
|
+
* has been reached (cf. `upper_bound_dimension()` and `set_dimension()` methods).
|
|
2177
|
+
*/
|
|
2178
|
+
bool lower_upper_bound_dimension() {
|
|
2179
|
+
// reset automatic detection to recompute
|
|
2180
|
+
dimension_to_be_lowered_ = false;
|
|
2181
|
+
int new_dimension = -1;
|
|
2182
|
+
// Browse the tree from the left to the right as higher dimension cells are more likely on the left part of the tree
|
|
2183
|
+
for (Simplex_handle sh : complex_simplex_range()) {
|
|
2184
|
+
#ifdef DEBUG_TRACES
|
|
2185
|
+
for (auto vertex : simplex_vertex_range(sh)) {
|
|
2186
|
+
std::clog << " " << vertex;
|
|
2187
|
+
}
|
|
2188
|
+
std::clog << std::endl;
|
|
2189
|
+
#endif // DEBUG_TRACES
|
|
2190
|
+
|
|
2191
|
+
int sh_dimension = dimension(sh);
|
|
2192
|
+
if (sh_dimension >= dimension_)
|
|
2193
|
+
// Stop browsing as soon as the dimension is reached, no need to go further
|
|
2194
|
+
return false;
|
|
2195
|
+
new_dimension = (std::max)(new_dimension, sh_dimension);
|
|
2196
|
+
}
|
|
2197
|
+
dimension_ = new_dimension;
|
|
2198
|
+
return true;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
public:
|
|
2202
|
+
/** \brief Remove a maximal simplex.
|
|
2203
|
+
* @param[in] sh Simplex handle on the maximal simplex to remove.
|
|
2204
|
+
* \pre Please check the simplex has no coface before removing it.
|
|
2205
|
+
* \exception std::invalid_argument In debug mode, if sh has children.
|
|
2206
|
+
* \post Note that the dimension of the simplicial complex may be lower after calling `remove_maximal_simplex()`
|
|
2207
|
+
* than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper
|
|
2208
|
+
* bound. If you care, you can call `dimension()` to recompute the exact dimension.
|
|
2209
|
+
*/
|
|
2210
|
+
void remove_maximal_simplex(Simplex_handle sh) {
|
|
2211
|
+
// Guarantee the simplex has no children
|
|
2212
|
+
GUDHI_CHECK(!has_children(sh),
|
|
2213
|
+
std::invalid_argument("Simplex_tree::remove_maximal_simplex - argument has children"));
|
|
2214
|
+
|
|
2215
|
+
update_simplex_tree_before_node_removal(sh);
|
|
2216
|
+
|
|
2217
|
+
// Simplex is a leaf, it means the child is the Siblings owning the leaf
|
|
2218
|
+
Siblings* child = sh->second.children();
|
|
2219
|
+
|
|
2220
|
+
if ((child->size() > 1) || (child == root())) {
|
|
2221
|
+
// Not alone, just remove it from members
|
|
2222
|
+
// Special case when child is the root of the simplex tree, just remove it from members
|
|
2223
|
+
child->erase(sh);
|
|
2224
|
+
} else {
|
|
2225
|
+
// Sibling is emptied : must be deleted, and its parent must point on his own Sibling
|
|
2226
|
+
child->oncles()->members().at(child->parent()).assign_children(child->oncles());
|
|
2227
|
+
delete child;
|
|
2228
|
+
// dimension may need to be lowered
|
|
2229
|
+
dimension_to_be_lowered_ = true;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/** \brief Retrieve the original filtration value for a given simplex in the Simplex_tree. Since the
|
|
2234
|
+
* computation of extended persistence requires modifying the filtration values, this function can be used
|
|
2235
|
+
* to recover the original values. Moreover, computing extended persistence requires adding new simplices
|
|
2236
|
+
* in the Simplex_tree. Hence, this function also outputs the type of each simplex. It can be either UP (which means
|
|
2237
|
+
* that the simplex was present originally, and is thus part of the ascending extended filtration), DOWN (which means
|
|
2238
|
+
* that the simplex is the cone of an original simplex, and is thus part of the descending extended filtration) or
|
|
2239
|
+
* EXTRA (which means the simplex is the cone point). See the definition of Extended_simplex_type. Note that if the
|
|
2240
|
+
* simplex type is DOWN, the original filtration value is set to be the original filtration value of the corresponding
|
|
2241
|
+
* (not coned) original simplex.
|
|
2242
|
+
* \pre This function should be called only if `extend_filtration()` has been called first!
|
|
2243
|
+
* \post The output filtration value is supposed to be the same, but might be a little different, than the
|
|
2244
|
+
* original filtration value, due to the internal transformation (scaling to [-2,-1]) that is
|
|
2245
|
+
* performed on the original filtration values during the computation of extended persistence.
|
|
2246
|
+
* @param[in] f Filtration value of the simplex in the extended (i.e., modified) filtration.
|
|
2247
|
+
* @param[in] efd Structure containing the minimum and maximum values of the original filtration. This the output of
|
|
2248
|
+
* `extend_filtration()`.
|
|
2249
|
+
* @return A pair containing the original filtration value of the simplex as well as the simplex type.
|
|
2250
|
+
*/
|
|
2251
|
+
std::pair<Filtration_value, Extended_simplex_type> decode_extended_filtration(const Filtration_value& f,
|
|
2252
|
+
const Extended_filtration_data& efd) {
|
|
2253
|
+
std::pair<Filtration_value, Extended_simplex_type> p;
|
|
2254
|
+
const Filtration_value& minval = efd.minval;
|
|
2255
|
+
const Filtration_value& maxval = efd.maxval;
|
|
2256
|
+
if (f >= -2 && f <= -1) {
|
|
2257
|
+
p.first = minval + (maxval - minval) * (f + 2);
|
|
2258
|
+
p.second = Extended_simplex_type::UP;
|
|
2259
|
+
} else if (f >= 1 && f <= 2) {
|
|
2260
|
+
p.first = minval - (maxval - minval) * (f - 2);
|
|
2261
|
+
p.second = Extended_simplex_type::DOWN;
|
|
2262
|
+
} else {
|
|
2263
|
+
p.first = std::numeric_limits<Filtration_value>::quiet_NaN();
|
|
2264
|
+
p.second = Extended_simplex_type::EXTRA;
|
|
2265
|
+
}
|
|
2266
|
+
return p;
|
|
2267
|
+
};
|
|
2268
|
+
|
|
2269
|
+
// TODO: externalize this method and `decode_extended_filtration`
|
|
2270
|
+
/** \brief Extend filtration for computing extended persistence.
|
|
2271
|
+
* This function only uses the filtration values at the 0-dimensional simplices,
|
|
2272
|
+
* and computes the extended persistence diagram induced by the lower-star filtration
|
|
2273
|
+
* computed with these values.
|
|
2274
|
+
* \post Note that after calling this function, the filtration
|
|
2275
|
+
* values are actually modified. The function `decode_extended_filtration()`
|
|
2276
|
+
* retrieves the original values and outputs the extended simplex type.
|
|
2277
|
+
*
|
|
2278
|
+
* @warning Currently only works for @ref SimplexTreeOptions::Filtration_value which are
|
|
2279
|
+
* float types like `float` or `double`.
|
|
2280
|
+
*
|
|
2281
|
+
* @exception std::invalid_argument In debug mode if the Simplex tree contains a vertex with the largest
|
|
2282
|
+
* Vertex_handle, as this method requires to create an extra vertex internally.
|
|
2283
|
+
* @return A data structure containing the maximum and minimum values of the original filtration.
|
|
2284
|
+
* It is meant to be provided as input to `decode_extended_filtration()` in order to retrieve
|
|
2285
|
+
* the original filtration values for each simplex.
|
|
2286
|
+
*/
|
|
2287
|
+
Extended_filtration_data extend_filtration() {
|
|
2288
|
+
clear_filtration(); // Drop the cache.
|
|
2289
|
+
|
|
2290
|
+
// Compute maximum and minimum of filtration values
|
|
2291
|
+
Vertex_handle maxvert = std::numeric_limits<Vertex_handle>::min();
|
|
2292
|
+
Filtration_value minval = Filtration_simplex_base_real::get_infinity();
|
|
2293
|
+
Filtration_value maxval = -Filtration_simplex_base_real::get_infinity();
|
|
2294
|
+
for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
|
|
2295
|
+
const Filtration_value& f = this->filtration(sh);
|
|
2296
|
+
minval = std::min(minval, f);
|
|
2297
|
+
maxval = std::max(maxval, f);
|
|
2298
|
+
maxvert = std::max(sh->first, maxvert);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
GUDHI_CHECK(maxvert < std::numeric_limits<Vertex_handle>::max(),
|
|
2302
|
+
std::invalid_argument("Simplex_tree contains a vertex with the largest Vertex_handle"));
|
|
2303
|
+
maxvert++;
|
|
2304
|
+
|
|
2305
|
+
Simplex_tree st_copy = *this;
|
|
2306
|
+
|
|
2307
|
+
// Add point for coning the simplicial complex
|
|
2308
|
+
this->insert_simplex_raw({maxvert}, -3);
|
|
2309
|
+
|
|
2310
|
+
Filtration_value scale = maxval - minval;
|
|
2311
|
+
if (scale != 0) scale = 1 / scale;
|
|
2312
|
+
|
|
2313
|
+
// For each simplex
|
|
2314
|
+
std::vector<Vertex_handle> vr;
|
|
2315
|
+
for (auto sh_copy : st_copy.complex_simplex_range()) {
|
|
2316
|
+
auto&& simplex_range = st_copy.simplex_vertex_range(sh_copy);
|
|
2317
|
+
vr.assign(simplex_range.begin(), simplex_range.end());
|
|
2318
|
+
auto sh = this->find(vr);
|
|
2319
|
+
|
|
2320
|
+
// Create cone on simplex
|
|
2321
|
+
vr.push_back(maxvert);
|
|
2322
|
+
if (this->dimension(sh) == 0) {
|
|
2323
|
+
const Filtration_value& v = this->filtration(sh);
|
|
2324
|
+
Filtration_value scaled_v = (v - minval) * scale;
|
|
2325
|
+
// Assign ascending value between -2 and -1 to vertex
|
|
2326
|
+
this->assign_filtration(sh, -2 + scaled_v);
|
|
2327
|
+
// Assign descending value between 1 and 2 to cone on vertex
|
|
2328
|
+
this->insert_simplex(vr, 2 - scaled_v);
|
|
2329
|
+
} else {
|
|
2330
|
+
// Assign value -3 to simplex and cone on simplex
|
|
2331
|
+
this->assign_filtration(sh, -3);
|
|
2332
|
+
this->insert_simplex(vr, -3);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
// Automatically assign good values for simplices
|
|
2337
|
+
this->make_filtration_non_decreasing();
|
|
2338
|
+
|
|
2339
|
+
// Return the filtration data
|
|
2340
|
+
return Extended_filtration_data(minval, maxval);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/** \brief Returns a vertex of `sh` that has the same filtration value as `sh` if it exists, and `null_vertex()`
|
|
2344
|
+
* otherwise.
|
|
2345
|
+
*
|
|
2346
|
+
* For a lower-star filtration built with `make_filtration_non_decreasing()`, this is a way to invert the process and
|
|
2347
|
+
* find out which vertex had its filtration value propagated to `sh`. If several vertices have the same filtration
|
|
2348
|
+
* value, the one it returns is arbitrary. */
|
|
2349
|
+
Vertex_handle vertex_with_same_filtration(Simplex_handle sh) {
|
|
2350
|
+
auto filt = filtration_(sh);
|
|
2351
|
+
for (auto v : simplex_vertex_range(sh))
|
|
2352
|
+
if (filtration_(find_vertex(v)) == filt) return v;
|
|
2353
|
+
return null_vertex();
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
/** \brief Returns an edge of `sh` that has the same filtration value as `sh` if it exists, and `null_simplex()`
|
|
2357
|
+
* otherwise.
|
|
2358
|
+
*
|
|
2359
|
+
* For a flag-complex built with `expansion()`, this is a way to invert the process and find out which edge had its
|
|
2360
|
+
* filtration value propagated to `sh`. If several edges have the same filtration value, the one it returns is
|
|
2361
|
+
* arbitrary.
|
|
2362
|
+
*
|
|
2363
|
+
* \pre `sh` must have dimension at least 1. */
|
|
2364
|
+
Simplex_handle edge_with_same_filtration(Simplex_handle sh) {
|
|
2365
|
+
// See issue #251 for potential speed improvements.
|
|
2366
|
+
auto&& vertices = simplex_vertex_range(sh); // vertices in decreasing order
|
|
2367
|
+
auto end = std::end(vertices);
|
|
2368
|
+
auto vi = std::begin(vertices);
|
|
2369
|
+
GUDHI_CHECK(vi != end, "empty simplex");
|
|
2370
|
+
auto v0 = *vi;
|
|
2371
|
+
++vi;
|
|
2372
|
+
GUDHI_CHECK(vi != end, "simplex of dimension 0");
|
|
2373
|
+
if (std::next(vi) == end) return sh; // shortcut for dimension 1
|
|
2374
|
+
Static_vertex_vector suffix;
|
|
2375
|
+
suffix.push_back(v0);
|
|
2376
|
+
auto filt = filtration_(sh);
|
|
2377
|
+
do {
|
|
2378
|
+
Vertex_handle v = *vi;
|
|
2379
|
+
auto&& children1 = find_vertex(v)->second.children()->members_;
|
|
2380
|
+
for (auto w : suffix) {
|
|
2381
|
+
// Can we take advantage of the fact that suffix is ordered?
|
|
2382
|
+
Simplex_handle s = children1.find(w);
|
|
2383
|
+
if (filtration_(s) == filt) return s;
|
|
2384
|
+
}
|
|
2385
|
+
suffix.push_back(v);
|
|
2386
|
+
} while (++vi != end);
|
|
2387
|
+
return null_simplex();
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
/** \brief Returns a minimal face of `sh` that has the same filtration value as `sh`.
|
|
2391
|
+
*
|
|
2392
|
+
* For a filtration built with `make_filtration_non_decreasing()`, this is a way to invert the process and find out
|
|
2393
|
+
* which simplex had its filtration value propagated to `sh`. If several minimal (for inclusion) simplices have the
|
|
2394
|
+
* same filtration value, the one it returns is arbitrary, and it is not guaranteed to be the one with smallest
|
|
2395
|
+
* dimension. */
|
|
2396
|
+
Simplex_handle minimal_simplex_with_same_filtration(Simplex_handle sh) {
|
|
2397
|
+
auto filt = filtration_(sh);
|
|
2398
|
+
// Naive implementation, it can be sped up.
|
|
2399
|
+
for (auto b : boundary_simplex_range(sh))
|
|
2400
|
+
if (filtration_(b) == filt) return minimal_simplex_with_same_filtration(b);
|
|
2401
|
+
return sh; // None of its faces has the same filtration.
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
public:
|
|
2405
|
+
// intrusive list of Nodes with same label using the hooks
|
|
2406
|
+
typedef boost::intrusive::member_hook<Hooks_simplex_base_link_nodes,
|
|
2407
|
+
typename Hooks_simplex_base_link_nodes::Member_hook_t,
|
|
2408
|
+
&Hooks_simplex_base_link_nodes::list_max_vertex_hook_>
|
|
2409
|
+
List_member_hook_t;
|
|
2410
|
+
// auto_unlink in Member_hook_t is incompatible with constant time size
|
|
2411
|
+
typedef boost::intrusive::
|
|
2412
|
+
list<Hooks_simplex_base_link_nodes, List_member_hook_t, boost::intrusive::constant_time_size<false>>
|
|
2413
|
+
List_max_vertex;
|
|
2414
|
+
// type of hooks stored in each Node, Node inherits from Hooks_simplex_base
|
|
2415
|
+
typedef typename std::conditional<Options::link_nodes_by_label,
|
|
2416
|
+
Hooks_simplex_base_link_nodes,
|
|
2417
|
+
Hooks_simplex_base_dummy>::type Hooks_simplex_base;
|
|
2418
|
+
|
|
2419
|
+
/** Data structure to access all Nodes with a given label u. Can be used for faster
|
|
2420
|
+
* computation. */
|
|
2421
|
+
private:
|
|
2422
|
+
// if Options::link_nodes_by_label is true, store the lists of Nodes with same label, empty otherwise.
|
|
2423
|
+
// unordered_map Vertex_handle v -> list of all Nodes with label v.
|
|
2424
|
+
std::unordered_map<Vertex_handle, List_max_vertex> nodes_label_to_list_;
|
|
2425
|
+
|
|
2426
|
+
List_max_vertex* nodes_by_label(Vertex_handle v) {
|
|
2427
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
2428
|
+
auto it_v = nodes_label_to_list_.find(v);
|
|
2429
|
+
if (it_v != nodes_label_to_list_.end()) {
|
|
2430
|
+
return &(it_v->second);
|
|
2431
|
+
} else {
|
|
2432
|
+
return nullptr;
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return nullptr;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/** \brief Helper method that returns the corresponding Simplex_handle from a member element defined by a node.
|
|
2439
|
+
*/
|
|
2440
|
+
static Simplex_handle simplex_handle_from_node(Node& node) {
|
|
2441
|
+
if constexpr (Options::stable_simplex_handles) {
|
|
2442
|
+
// Relies on the Dictionary type to be boost::container::map<Vertex_handle, Node>.
|
|
2443
|
+
// If the type changes or boost fundamentally changes something on the structure of their map,
|
|
2444
|
+
// a safer/more general but much slower version is:
|
|
2445
|
+
// if (node.children()->parent() == label) { // verifies if node is a leaf
|
|
2446
|
+
// return children->oncles()->find(label);
|
|
2447
|
+
// } else {
|
|
2448
|
+
// return children->members().find(label);
|
|
2449
|
+
// }
|
|
2450
|
+
// Requires an additional parameter "Vertex_handle label" which is the label of the node.
|
|
2451
|
+
|
|
2452
|
+
Dictionary_it testIt = node.children()->members().begin();
|
|
2453
|
+
Node* testNode = &testIt->second;
|
|
2454
|
+
auto testIIt = testIt.get();
|
|
2455
|
+
auto testPtr = testIIt.pointed_node();
|
|
2456
|
+
// distance between node and pointer to map pair in memory
|
|
2457
|
+
auto shift = (const char*)(testNode) - (const char*)(testPtr);
|
|
2458
|
+
|
|
2459
|
+
// decltype(testPtr) = boost::intrusive::compact_rbtree_node<void*>*
|
|
2460
|
+
decltype(testPtr) sh_ptr = decltype(testPtr)((const char*)(&node) - shift); // shifts from node to pointer
|
|
2461
|
+
// decltype(testIIt) =
|
|
2462
|
+
// boost::intrusive::tree_iterator<
|
|
2463
|
+
// boost::intrusive::bhtraits<
|
|
2464
|
+
// boost::container::base_node<
|
|
2465
|
+
// std::pair<const int, Simplex_tree_node_explicit_storage<Simplex_tree>>,
|
|
2466
|
+
// boost::container::dtl::intrusive_tree_hook<void*, boost::container::red_black_tree, true>, true>,
|
|
2467
|
+
// boost::intrusive::rbtree_node_traits<void*, true>,
|
|
2468
|
+
// boost::intrusive::normal_link,
|
|
2469
|
+
// boost::intrusive::dft_tag,
|
|
2470
|
+
// 3>,
|
|
2471
|
+
// false>
|
|
2472
|
+
decltype(testIIt) sh_ii;
|
|
2473
|
+
sh_ii = sh_ptr; // creates ``subiterator'' from pointer
|
|
2474
|
+
Dictionary_it sh(sh_ii); // creates iterator from subiterator
|
|
2475
|
+
|
|
2476
|
+
return sh;
|
|
2477
|
+
} else {
|
|
2478
|
+
return (Simplex_handle)(boost::intrusive::get_parent_from_member<Dit_value_t>(&node, &Dit_value_t::second));
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// Give access to Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator and keep nodes_by_label and
|
|
2483
|
+
// simplex_handle_from_node private
|
|
2484
|
+
friend class Simplex_tree_optimized_cofaces_rooted_subtrees_simplex_iterator<Simplex_tree>;
|
|
2485
|
+
|
|
2486
|
+
private:
|
|
2487
|
+
// update all extra data structures in the Simplex_tree. Must be called after all
|
|
2488
|
+
// simplex insertions.
|
|
2489
|
+
void update_simplex_tree_after_node_insertion(Simplex_handle sh) {
|
|
2490
|
+
#ifdef DEBUG_TRACES
|
|
2491
|
+
std::clog << "update_simplex_tree_after_node_insertion" << std::endl;
|
|
2492
|
+
#endif // DEBUG_TRACES
|
|
2493
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
2494
|
+
// Creates an entry with sh->first if not already in the map and insert sh->second at the end of the list
|
|
2495
|
+
nodes_label_to_list_[sh->first].push_back(sh->second);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// update all extra data structures in the Simplex_tree. Must be called before
|
|
2500
|
+
// all simplex removals
|
|
2501
|
+
void update_simplex_tree_before_node_removal(Simplex_handle sh) {
|
|
2502
|
+
#ifdef DEBUG_TRACES
|
|
2503
|
+
std::clog << "update_simplex_tree_before_node_removal" << std::endl;
|
|
2504
|
+
#endif // DEBUG_TRACES
|
|
2505
|
+
if constexpr (Options::link_nodes_by_label) {
|
|
2506
|
+
sh->second.unlink_hooks(); // remove from lists of same label Nodes
|
|
2507
|
+
if (nodes_label_to_list_[sh->first].empty()) nodes_label_to_list_.erase(sh->first);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
public:
|
|
2512
|
+
/** \brief This function resets the filtration value of all the simplices of dimension at least min_dim. Resets all
|
|
2513
|
+
* the Simplex_tree when `min_dim = 0`.
|
|
2514
|
+
* `reset_filtration` may break the filtration property with `min_dim > 0`, and it is the user's responsibility to
|
|
2515
|
+
* make it a valid filtration (using a large enough `filt_value`, or calling `make_filtration_non_decreasing`
|
|
2516
|
+
* afterwards for instance).
|
|
2517
|
+
* @param[in] filt_value The new filtration value.
|
|
2518
|
+
* @param[in] min_dim The minimal dimension. Default value is 0.
|
|
2519
|
+
*/
|
|
2520
|
+
void reset_filtration(const Filtration_value& filt_value, int min_dim = 0) {
|
|
2521
|
+
rec_reset_filtration(&root_, filt_value, min_dim);
|
|
2522
|
+
clear_filtration(); // Drop the cache.
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
private:
|
|
2526
|
+
/** \brief Recursively resets filtration value when minimal depth <= 0.
|
|
2527
|
+
* @param[in] sib Siblings to be parsed.
|
|
2528
|
+
* @param[in] filt_value The new filtration value.
|
|
2529
|
+
* @param[in] min_depth The minimal depth.
|
|
2530
|
+
*/
|
|
2531
|
+
void rec_reset_filtration(Siblings* sib, const Filtration_value& filt_value, int min_depth) {
|
|
2532
|
+
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
|
|
2533
|
+
if (min_depth <= 0) {
|
|
2534
|
+
sh->second.assign_filtration(filt_value);
|
|
2535
|
+
}
|
|
2536
|
+
if (has_children(sh)) {
|
|
2537
|
+
rec_reset_filtration(sh->second.children(), filt_value, min_depth - 1);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
std::size_t num_simplices_and_filtration_size(Siblings* sib, std::size_t& fv_byte_size) const {
|
|
2543
|
+
using namespace Gudhi::simplex_tree;
|
|
2544
|
+
|
|
2545
|
+
auto sib_begin = sib->members().begin();
|
|
2546
|
+
auto sib_end = sib->members().end();
|
|
2547
|
+
size_t simplices_number = sib->members().size();
|
|
2548
|
+
for (auto sh = sib_begin; sh != sib_end; ++sh) {
|
|
2549
|
+
if constexpr (SimplexTreeOptions::store_filtration)
|
|
2550
|
+
fv_byte_size += get_serialization_size_of(sh->second.filtration());
|
|
2551
|
+
if (has_children(sh)) {
|
|
2552
|
+
simplices_number += num_simplices_and_filtration_size(sh->second.children(), fv_byte_size);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
return simplices_number;
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
public:
|
|
2559
|
+
/** @private @brief Returns the serialization required buffer size.
|
|
2560
|
+
*
|
|
2561
|
+
* @return The exact serialization required size in number of bytes.
|
|
2562
|
+
*
|
|
2563
|
+
* @warning It is meant to return the same size with the same SimplexTreeOptions and on a computer with the same
|
|
2564
|
+
* architecture.
|
|
2565
|
+
*/
|
|
2566
|
+
std::size_t get_serialization_size() {
|
|
2567
|
+
using namespace Gudhi::simplex_tree;
|
|
2568
|
+
|
|
2569
|
+
const std::size_t num_param_byte_size = sizeof(decltype(number_of_parameters_));
|
|
2570
|
+
const std::size_t vh_byte_size = sizeof(Vertex_handle);
|
|
2571
|
+
std::size_t fv_byte_size = 0;
|
|
2572
|
+
const std::size_t tree_size = num_simplices_and_filtration_size(&root_, fv_byte_size);
|
|
2573
|
+
const std::size_t buffer_byte_size =
|
|
2574
|
+
num_param_byte_size + vh_byte_size + fv_byte_size + tree_size * 2 * vh_byte_size;
|
|
2575
|
+
#ifdef DEBUG_TRACES
|
|
2576
|
+
std::clog << "Gudhi::simplex_tree::get_serialization_size - buffer size = " << buffer_byte_size << std::endl;
|
|
2577
|
+
#endif // DEBUG_TRACES
|
|
2578
|
+
return buffer_byte_size;
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
/** @private @brief Serialize the Simplex tree - Flatten it in a user given array of char
|
|
2582
|
+
*
|
|
2583
|
+
* @param[in] buffer An array of char allocated with enough space (cf. Gudhi::simplex_tree::get_serialization_size)
|
|
2584
|
+
* @param[in] buffer_size The buffer size.
|
|
2585
|
+
*
|
|
2586
|
+
* @exception std::invalid_argument If serialization does not match exactly the buffer_size value.
|
|
2587
|
+
*
|
|
2588
|
+
* @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
|
|
2589
|
+
* SimplexTreeOptions and on a computer with the same architecture.
|
|
2590
|
+
*/
|
|
2591
|
+
/* Let's take the following simplicial complex as example: */
|
|
2592
|
+
/* (vertices are represented as letters to ease the understanding) */
|
|
2593
|
+
/* o---o---o */
|
|
2594
|
+
/* a b\X/c */
|
|
2595
|
+
/* o */
|
|
2596
|
+
/* d */
|
|
2597
|
+
/* The simplex tree is: */
|
|
2598
|
+
/* a o b o c o d o */
|
|
2599
|
+
/* | |\ | */
|
|
2600
|
+
/* b o c o o d o d */
|
|
2601
|
+
/* | */
|
|
2602
|
+
/* d o */
|
|
2603
|
+
/* The serialization is (without filtration values that comes right after vertex handle value): */
|
|
2604
|
+
/* 04(number of vertices)0a 0b 0c 0d(list of vertices)01(number of [a] children)0b([a,b] simplex) */
|
|
2605
|
+
/* 00(number of [a,b] children)02(number of [b] children)0c 0d(list of [b] children)01(number of [b,c] children) */
|
|
2606
|
+
/* 0d(list of [b,c] children)00(number of [b,c,d] children)00(number of [b,d] children)01(number of [c] children) */
|
|
2607
|
+
/* 0d(list of [c] children)00(number of [b,d] children)00(number of [d] children) */
|
|
2608
|
+
/* Without explanation and with filtration values: */
|
|
2609
|
+
/* 04 0a F(a) 0b F(b) 0c F(c) 0d F(d) 01 0b F(a,b) 00 02 0c F(b,c) 0d F(b,d) 01 0d F(b,c,d) 00 00 01 0d F(c,d) 00 00
|
|
2610
|
+
*/
|
|
2611
|
+
void serialize(char* buffer, const std::size_t buffer_size) {
|
|
2612
|
+
char* buffer_end = buffer;
|
|
2613
|
+
if constexpr (Options::is_multi_parameter) {
|
|
2614
|
+
buffer_end = simplex_tree::serialize_trivial(number_of_parameters_, buffer_end);
|
|
2615
|
+
}
|
|
2616
|
+
buffer_end = rec_serialize(&root_, buffer_end);
|
|
2617
|
+
if (static_cast<std::size_t>(buffer_end - buffer) != buffer_size)
|
|
2618
|
+
throw std::invalid_argument("Serialization does not match end of buffer");
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
private:
|
|
2622
|
+
/** \brief Serialize each element of the sibling and recursively call serialization. */
|
|
2623
|
+
char* rec_serialize(Siblings* sib, char* buffer) {
|
|
2624
|
+
using namespace Gudhi::simplex_tree;
|
|
2625
|
+
char* ptr = buffer;
|
|
2626
|
+
ptr = serialize_trivial(static_cast<Vertex_handle>(sib->members().size()), ptr);
|
|
2627
|
+
#ifdef DEBUG_TRACES
|
|
2628
|
+
std::clog << "\n" << sib->members().size() << " : ";
|
|
2629
|
+
#endif // DEBUG_TRACES
|
|
2630
|
+
for (auto& map_el : sib->members()) {
|
|
2631
|
+
ptr = serialize_trivial(map_el.first, ptr); // Vertex
|
|
2632
|
+
if (Options::store_filtration) ptr = serialize_trivial(map_el.second.filtration(), ptr); // Filtration
|
|
2633
|
+
#ifdef DEBUG_TRACES
|
|
2634
|
+
std::clog << " [ " << map_el.first << " | " << map_el.second.filtration() << " ] ";
|
|
2635
|
+
#endif // DEBUG_TRACES
|
|
2636
|
+
}
|
|
2637
|
+
for (auto& map_el : sib->members()) {
|
|
2638
|
+
if (has_children(&map_el)) {
|
|
2639
|
+
ptr = rec_serialize(map_el.second.children(), ptr);
|
|
2640
|
+
} else {
|
|
2641
|
+
ptr = serialize_trivial(static_cast<Vertex_handle>(0), ptr);
|
|
2642
|
+
#ifdef DEBUG_TRACES
|
|
2643
|
+
std::cout << "\n0 : ";
|
|
2644
|
+
#endif // DEBUG_TRACES
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
return ptr;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
public:
|
|
2651
|
+
/** @private @brief Deserialize the array of char (flatten version of the tree) to initialize a Simplex tree.
|
|
2652
|
+
* It is the user's responsibility to provide an 'empty' Simplex_tree, there is no guarantee otherwise.
|
|
2653
|
+
*
|
|
2654
|
+
* @param[in] buffer A pointer on a buffer that contains a serialized Simplex_tree.
|
|
2655
|
+
* @param[in] buffer_size The size of the buffer.
|
|
2656
|
+
*
|
|
2657
|
+
* @exception std::invalid_argument In case the deserialization does not finish at the correct buffer_size.
|
|
2658
|
+
* @exception std::logic_error In debug mode, if the Simplex_tree is not 'empty'.
|
|
2659
|
+
*
|
|
2660
|
+
* @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
|
|
2661
|
+
* SimplexTreeOptions and on a computer with the same architecture.
|
|
2662
|
+
*
|
|
2663
|
+
*/
|
|
2664
|
+
void deserialize(const char* buffer, const std::size_t buffer_size) {
|
|
2665
|
+
using namespace Gudhi::simplex_tree;
|
|
2666
|
+
GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty"));
|
|
2667
|
+
const char* ptr = buffer;
|
|
2668
|
+
if constexpr (Options::is_multi_parameter) {
|
|
2669
|
+
decltype(number_of_parameters_) num_params;
|
|
2670
|
+
ptr = deserialize_trivial(num_params, ptr);
|
|
2671
|
+
this->set_number_of_parameters(num_params);
|
|
2672
|
+
}
|
|
2673
|
+
// Needs to read size before recursivity to manage new siblings for children
|
|
2674
|
+
Vertex_handle members_size;
|
|
2675
|
+
ptr = deserialize_trivial(members_size, ptr);
|
|
2676
|
+
ptr = rec_deserialize(&root_, members_size, ptr, 0);
|
|
2677
|
+
if (static_cast<std::size_t>(ptr - buffer) != buffer_size) {
|
|
2678
|
+
throw std::invalid_argument("Deserialization does not match end of buffer");
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
private:
|
|
2683
|
+
/** \brief Serialize each element of the sibling and recursively call serialization. */
|
|
2684
|
+
const char* rec_deserialize(Siblings* sib, Vertex_handle members_size, const char* ptr, int dim) {
|
|
2685
|
+
using namespace Gudhi::simplex_tree;
|
|
2686
|
+
// In case buffer is just a 0 char
|
|
2687
|
+
if (members_size > 0) {
|
|
2688
|
+
if constexpr (!Options::stable_simplex_handles) sib->members_.reserve(members_size);
|
|
2689
|
+
Vertex_handle vertex;
|
|
2690
|
+
Filtration_value filtration(0);
|
|
2691
|
+
for (Vertex_handle idx = 0; idx < members_size; idx++) {
|
|
2692
|
+
ptr = deserialize_trivial(vertex, ptr);
|
|
2693
|
+
if (Options::store_filtration) {
|
|
2694
|
+
ptr = deserialize_trivial(filtration, ptr);
|
|
2695
|
+
}
|
|
2696
|
+
// Default is no children
|
|
2697
|
+
// If store_filtration is false, `filtration` is ignored.
|
|
2698
|
+
sib->members_.emplace_hint(sib->members_.end(), vertex, Node(sib, filtration));
|
|
2699
|
+
}
|
|
2700
|
+
Vertex_handle child_size;
|
|
2701
|
+
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
|
|
2702
|
+
update_simplex_tree_after_node_insertion(sh);
|
|
2703
|
+
ptr = deserialize_trivial(child_size, ptr);
|
|
2704
|
+
if (child_size > 0) {
|
|
2705
|
+
Siblings* child = new Siblings(sib, sh->first);
|
|
2706
|
+
sh->second.assign_children(child);
|
|
2707
|
+
ptr = rec_deserialize(child, child_size, ptr, dim + 1);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (dim > dimension_) {
|
|
2711
|
+
// Update dimension if needed
|
|
2712
|
+
dimension_ = dim;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return ptr;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
private:
|
|
2719
|
+
Vertex_handle null_vertex_;
|
|
2720
|
+
/** \brief Total number of simplices in the complex, without the empty simplex.*/
|
|
2721
|
+
/** \brief Set of simplex tree Nodes representing the vertices.*/
|
|
2722
|
+
Siblings root_;
|
|
2723
|
+
/** \brief Simplices ordered according to a filtration.*/
|
|
2724
|
+
std::vector<Simplex_handle> filtration_vect_;
|
|
2725
|
+
/** \brief Upper bound on the dimension of the simplicial complex.*/
|
|
2726
|
+
int dimension_;
|
|
2727
|
+
bool dimension_to_be_lowered_ = false;
|
|
2728
|
+
|
|
2729
|
+
// MULTIPERS STUFF
|
|
2730
|
+
public:
|
|
2731
|
+
/**
|
|
2732
|
+
* \brief Sets the number of parameters of the filtrations if SimplexTreeOptions::is_multi_parameter.
|
|
2733
|
+
* */
|
|
2734
|
+
void set_number_of_parameters(int num) {
|
|
2735
|
+
static_assert(SimplexTreeOptions::is_multi_parameter,
|
|
2736
|
+
"Cannot set number of parameters of 1-parameter simplextree.");
|
|
2737
|
+
number_of_parameters_ = num;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
/**
|
|
2741
|
+
* \brief Gets the number of parameters of the filtrations if SimplexTreeOptions::is_multi_parameter.
|
|
2742
|
+
* */
|
|
2743
|
+
int get_number_of_parameters() const {
|
|
2744
|
+
if constexpr (SimplexTreeOptions::is_multi_parameter)
|
|
2745
|
+
return number_of_parameters_;
|
|
2746
|
+
else
|
|
2747
|
+
return 1;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
private:
|
|
2751
|
+
int number_of_parameters_; /**< Number of parameters of the multi-filtrations when
|
|
2752
|
+
SimplexTreeOptions::is_multi_parameter.-*/
|
|
2753
|
+
};
|
|
2754
|
+
|
|
2755
|
+
// Print a Simplex_tree in os.
|
|
2756
|
+
template <typename... T>
|
|
2757
|
+
std::ostream& operator<<(std::ostream& os, Simplex_tree<T...>& st) {
|
|
2758
|
+
for (auto sh : st.filtration_simplex_range()) {
|
|
2759
|
+
os << st.dimension(sh) << " ";
|
|
2760
|
+
for (auto v : st.simplex_vertex_range(sh)) {
|
|
2761
|
+
os << v << " ";
|
|
2762
|
+
}
|
|
2763
|
+
os << st.filtration(sh) << "\n"; // TODO(VR): why adding the key ?? not read ?? << " " << st.key(sh) << " \n";
|
|
2764
|
+
}
|
|
2765
|
+
return os;
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
template <typename... T>
|
|
2769
|
+
std::istream& operator>>(std::istream& is, Simplex_tree<T...>& st) {
|
|
2770
|
+
typedef Simplex_tree<T...> ST;
|
|
2771
|
+
std::vector<typename ST::Vertex_handle> simplex;
|
|
2772
|
+
typename ST::Filtration_value fil;
|
|
2773
|
+
int max_dim = -1;
|
|
2774
|
+
while (read_simplex(is, simplex, fil)) {
|
|
2775
|
+
// read all simplices in the file as a list of vertices
|
|
2776
|
+
// Warning : simplex_size needs to be casted in int - Can be 0
|
|
2777
|
+
int dim = static_cast<int>(simplex.size() - 1);
|
|
2778
|
+
if (max_dim < dim) {
|
|
2779
|
+
max_dim = dim;
|
|
2780
|
+
}
|
|
2781
|
+
// insert every simplex in the simplex tree
|
|
2782
|
+
st.insert_simplex(simplex, fil);
|
|
2783
|
+
simplex.clear();
|
|
2784
|
+
}
|
|
2785
|
+
st.set_dimension(max_dim);
|
|
2786
|
+
|
|
2787
|
+
return is;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
/** @}*/ // end addtogroup simplex_tree
|
|
2791
|
+
|
|
2792
|
+
} // namespace Gudhi
|
|
2793
|
+
|
|
2794
|
+
#endif // SIMPLEX_TREE_H_
|