-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiparameter simplextree, C++ part. #976
base: master
Are you sure you want to change the base?
Changes from 17 commits
60b7325
1fa3aa9
04d8d8c
2cab388
cba6c6f
ddd26fe
9d3b51b
685ae13
1164e28
38c6064
40fe370
f7965a8
c79fec6
aab5c34
6046f8d
b1e7b99
fd0a492
3498a8c
ebdbfda
1433e8e
f4cc240
e9668e5
e4bc012
c832b5f
e0ae582
37fda79
10f990d
dc2f912
d61ad0b
267dd43
b85205f
114327f
04ce9e2
3f4dc40
d070bf1
96056a3
6d5ad8b
a89ff98
a6d756a
6fa20cf
f90cce7
f03df17
bc58007
dcdd710
e5123d6
26d78ab
1c0e1df
0ef649f
b448c05
89dfd6b
cf41588
4ecdd32
49ee642
9de0252
5b7e276
8c797f2
8b303c2
27ebaf6
c7cd3d3
214ebc6
afde822
039415d
6c27892
882bf8c
a15d72c
460e1a0
9b7612e
e5eead4
3344c17
1e8fc4f
03cf667
6be37e7
21d95cb
6338fc7
3d480b6
8eac4b9
ac575db
065a8b9
959a3d3
a42608e
893fbf6
ffaf004
be3ba48
30397ba
01d4a30
62bd472
4d6dbbf
6d7d5f6
a882194
777a766
3ae5312
92ce37c
ff4d970
9258390
7d66c00
5a80760
59f99b0
15324a3
d3625c4
ebafc31
bac48dc
bc64d15
8385ac4
00ad5a5
361134a
76390f4
4190ed7
19aba94
69986c3
7925e87
c5263dc
732c2c3
1e856ec
0dd084c
d2a2168
9ec506a
02cb463
f6d5b7e
17d1caa
b752394
1b24266
bf9ecf6
d4b4e19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. | ||
* See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. | ||
* Author(s): David Loiseaux | ||
* | ||
* Copyright (C) 2023 Inria | ||
* | ||
* Modification(s): | ||
* - YYYY/MM Author: Description of the modification | ||
*/ | ||
|
||
#include <gudhi/Simplex_tree.h> | ||
#include <gudhi/Simplex_tree/Simplex_tree_multi.h> | ||
|
||
#include <iostream> | ||
#include <initializer_list> | ||
|
||
struct ST_MULTI { | ||
public: | ||
DavidLapous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
typedef Gudhi::linear_indexing_tag Indexing_tag; | ||
typedef int Vertex_handle; | ||
typedef float value_type; | ||
using Filtration_value = Gudhi::multiparameter::multi_filtrations::Finitely_critical_multi_filtration<value_type>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The nested multiparameter::multi_filtrations feel a bit redundant, but I guess it isn't that bad. |
||
typedef std::uint32_t Simplex_key; | ||
static const bool store_key = true; | ||
static const bool store_filtration = true; | ||
static const bool contiguous_vertices = false; | ||
static const bool link_nodes_by_label = true; | ||
static const bool stable_simplex_handles = false; | ||
static const bool is_multi_parameter = true; | ||
}; | ||
|
||
using ST = Gudhi::Simplex_tree<ST_MULTI>; | ||
|
||
|
||
int main() { | ||
ST st; | ||
|
||
/* Complex to build. */ | ||
/* 1 */ | ||
/* o */ | ||
/* /X\ */ | ||
/* o---o---o */ | ||
/* 2 0 3 */ | ||
|
||
auto triangle012 = {0, 1, 2}; | ||
auto edge03 = {0, 3}; | ||
st.insert_simplex_and_subfaces(triangle012, {1,2,3}); // {1,2,3} can be any array-like vector-like | ||
st.insert_simplex_and_subfaces(edge03, {4,5,6}); | ||
|
||
auto edge02 = {0, 2}; | ||
ST::Simplex_handle e = st.find(edge02); | ||
// Finitely_critical_multi_filtration has an operator<< | ||
std::cout << st.filtration(e) << std::endl; | ||
assert(st.filtration(st.find(edge03)) == std::vector<float>({4,5,6})); | ||
DavidLapous marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,16 +151,18 @@ class Simplex_tree { | |
Key_simplex_base; | ||
|
||
struct Filtration_simplex_base_real { | ||
Filtration_simplex_base_real() : filt_(0) {} | ||
void assign_filtration(Filtration_value f) { filt_ = f; } | ||
Filtration_value filtration() const { return filt_; } | ||
Filtration_simplex_base_real() : filt_{} {} | ||
void assign_filtration(const Filtration_value& f) { filt_ = f; } | ||
const Filtration_value& filtration() const { return filt_; } | ||
Filtration_value& filtration() { return filt_; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBD, but I am not sure we should add this method. |
||
private: | ||
Filtration_value filt_; | ||
}; | ||
struct Filtration_simplex_base_dummy { | ||
Filtration_simplex_base_dummy() {} | ||
void assign_filtration(Filtration_value GUDHI_CHECK_code(f)) { GUDHI_CHECK(f == 0, "filtration value specified for a complex that does not store them"); } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this test |
||
Filtration_value filtration() const { return 0; } | ||
const Filtration_value& filtration() const { return null_value; } | ||
static constexpr Filtration_value null_value={}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we say: struct MyOptions : Simplex_tree_options_multidimensional_filtration {
// is_multi_parameter but not store_filtration
static const bool store_filtration = false;
};
Simplex_tree<MyOptions> stree; We are facing the error:
But maybe this error is normal ? I was wondering to see if we should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A simple fix is to do a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you meant |
||
}; | ||
typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real, | ||
Filtration_simplex_base_dummy>::type Filtration_simplex_base; | ||
|
@@ -576,7 +578,7 @@ class Simplex_tree { | |
* | ||
* Same as `filtration()`, but does not handle `null_simplex()`. | ||
*/ | ||
static Filtration_value filtration_(Simplex_handle sh) { | ||
static const Filtration_value& filtration_(Simplex_handle sh) { | ||
GUDHI_CHECK (sh != null_simplex(), "null simplex"); | ||
return sh->second.filtration(); | ||
} | ||
|
@@ -604,18 +606,25 @@ class Simplex_tree { | |
* Called on the null_simplex, it returns infinity. | ||
* If SimplexTreeOptions::store_filtration is false, returns 0. | ||
*/ | ||
static Filtration_value filtration(Simplex_handle sh) { | ||
static const Filtration_value& filtration(Simplex_handle sh){ | ||
if (sh != null_simplex()) { | ||
return sh->second.filtration(); | ||
} else { | ||
return std::numeric_limits<Filtration_value>::infinity(); | ||
return inf_; | ||
} | ||
} | ||
static Filtration_value& filtration_mutable(Simplex_handle sh){ | ||
if (sh != null_simplex()) { | ||
return sh->second.filtration(); | ||
} else { | ||
return inf_; | ||
Comment on lines
+690
to
+701
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already from the previous PR:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a fixed point of view on this. So its only a matter of what's the gudhi style of accessing a filtration value by reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If they really do the same thing, I am ok with |
||
} | ||
} | ||
|
||
/** \brief Sets the filtration value of a simplex. | ||
* \exception std::invalid_argument In debug mode, if sh is a null_simplex. | ||
*/ | ||
void assign_filtration(Simplex_handle sh, Filtration_value fv) { | ||
void assign_filtration(Simplex_handle sh, const Filtration_value& fv) { | ||
GUDHI_CHECK(sh != null_simplex(), | ||
std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex")); | ||
sh->second.assign_filtration(fv); | ||
|
@@ -822,14 +831,16 @@ class Simplex_tree { | |
* to the new simplex. | ||
* If the insertion fails (the simplex is already there), the bool is set to false. If the insertion | ||
* fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration', | ||
* and the simplex tree is not multi parameter (`SimplexTreeOptions::is_multi_parameter == false`), | ||
* we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the | ||
* output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to | ||
* null_simplex. | ||
* output pair to the Simplex_handle of the simplex. When the simplex tree is multi parameter, | ||
* the existing filtration values are not updated. If the insertion fails for other reasons, | ||
Comment on lines
+933
to
+934
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* we set the Simplex_handle part to `null_simplex`. | ||
* | ||
*/ | ||
template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>> | ||
std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex, | ||
Filtration_value filtration) { | ||
const Filtration_value& filtration) { | ||
Siblings * curr_sib = &root_; | ||
std::pair<Simplex_handle, bool> res_insert; | ||
auto vi = simplex.begin(); | ||
|
@@ -895,7 +906,7 @@ class Simplex_tree { | |
* .end() return input iterators, with 'value_type' Vertex_handle. */ | ||
template<class InputVertexRange = std::initializer_list<Vertex_handle>> | ||
std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange & simplex, | ||
Filtration_value filtration = 0) { | ||
const Filtration_value& filtration = {}) { | ||
auto first = std::begin(simplex); | ||
auto last = std::end(simplex); | ||
|
||
|
@@ -924,7 +935,7 @@ class Simplex_tree { | |
*/ | ||
template<class InputVertexRange = std::initializer_list<Vertex_handle>> | ||
std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(const InputVertexRange& Nsimplex, | ||
Filtration_value filtration = 0) { | ||
VincentRouvreau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const Filtration_value& filtration = {}) { | ||
auto first = std::begin(Nsimplex); | ||
auto last = std::end(Nsimplex); | ||
|
||
|
@@ -953,7 +964,7 @@ class Simplex_tree { | |
std::pair<Simplex_handle, bool> rec_insert_simplex_and_subfaces_sorted(Siblings* sib, | ||
ForwardVertexIterator first, | ||
ForwardVertexIterator last, | ||
Filtration_value filt) { | ||
const Filtration_value& filt) { | ||
// An alternative strategy would be: | ||
// - try to find the complete simplex, if found (and low filtration) exit | ||
// - insert all the vertices at once in sib | ||
|
@@ -969,14 +980,17 @@ class Simplex_tree { | |
|
||
Simplex_handle simplex_one = insertion_result.first; | ||
bool one_is_new = insertion_result.second; | ||
if (!one_is_new) { | ||
if (filtration(simplex_one) > filt) { | ||
assign_filtration(simplex_one, filt); | ||
} else { | ||
// FIXME: this interface makes no sense, and it doesn't seem to be tested. | ||
insertion_result.first = null_simplex(); | ||
if constexpr (!SimplexTreeOptions::is_multi_parameter){ // Ignores the assign part for multiparameter filtrations. | ||
if (!one_is_new) { | ||
if (filtration(simplex_one) > filt){ | ||
assign_filtration(simplex_one, filt); | ||
} else { | ||
// FIXME: this interface makes no sense, and it doesn't seem to be tested. | ||
insertion_result.first = null_simplex(); | ||
} | ||
} | ||
} | ||
|
||
if (++first == last) return insertion_result; | ||
if (!has_children(simplex_one)) | ||
// TODO: have special code here, we know we are building the whole subtree from scratch. | ||
|
@@ -1316,7 +1330,7 @@ class Simplex_tree { | |
* The complex does not need to be empty before calling this function. However, if a vertex is | ||
* already present, its filtration value is not modified, unlike with other insertion functions. */ | ||
template <class VertexRange> | ||
void insert_batch_vertices(VertexRange const& vertices, Filtration_value filt = 0) { | ||
void insert_batch_vertices(VertexRange const& vertices, const Filtration_value& filt ={}) { | ||
auto verts = vertices | boost::adaptors::transformed([&](auto v){ | ||
return Dit_value_t(v, Node(&root_, filt)); }); | ||
root_.members_.insert(boost::begin(verts), boost::end(verts)); | ||
|
@@ -1403,7 +1417,7 @@ class Simplex_tree { | |
static void intersection(std::vector<std::pair<Vertex_handle, Node> >& intersection, | ||
Dictionary_it begin1, Dictionary_it end1, | ||
Dictionary_it begin2, Dictionary_it end2, | ||
Filtration_value filtration_) { | ||
const Filtration_value& filtration_) { | ||
if (begin1 == end1 || begin2 == end2) | ||
return; // ----->> | ||
while (true) { | ||
|
@@ -1610,12 +1624,21 @@ class Simplex_tree { | |
if (dim == 0) return; | ||
// Find the maximum filtration value in the border | ||
Boundary_simplex_range&& boundary = boundary_simplex_range(sh); | ||
Boundary_simplex_iterator max_border = std::max_element(std::begin(boundary), std::end(boundary), | ||
[](Simplex_handle sh1, Simplex_handle sh2) { | ||
return filtration(sh1) < filtration(sh2); | ||
}); | ||
Filtration_value max_filt_border_value; | ||
if constexpr (SimplexTreeOptions::is_multi_parameter) { | ||
// in that case, we assume that Filtration_value has a `push_to` member to handle this. | ||
VincentRouvreau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
max_filt_border_value = Filtration_value(this->number_of_parameters_); | ||
for (auto& face_sh : boundary) { | ||
max_filt_border_value.push_to( | ||
filtration(face_sh)); // pushes the value of max_filt_border_value to reach simplex' filtration | ||
} | ||
} else { | ||
Boundary_simplex_iterator max_border = | ||
std::max_element(std::begin(boundary), std::end(boundary), | ||
[](Simplex_handle sh1, Simplex_handle sh2) { return filtration(sh1) < filtration(sh2); }); | ||
max_filt_border_value = filtration(*max_border); | ||
} | ||
|
||
Filtration_value max_filt_border_value = filtration(*max_border); | ||
// Replacing if(f<max) with if(!(f>=max)) would mean that if f is NaN, we replace it with the max of the children. | ||
// That seems more useful than keeping NaN. | ||
if (!(sh->second.filtration() >= max_filt_border_value)) { | ||
|
@@ -1650,7 +1673,7 @@ class Simplex_tree { | |
* than it was before. However, `upper_bound_dimension()` will return the old value, which remains a valid upper | ||
* bound. If you care, you can call `dimension()` to recompute the exact dimension. | ||
*/ | ||
bool prune_above_filtration(Filtration_value filtration) { | ||
bool prune_above_filtration(const Filtration_value& filtration) { | ||
if (std::numeric_limits<Filtration_value>::has_infinity && filtration == std::numeric_limits<Filtration_value>::infinity()) | ||
return false; // ---->> | ||
bool modified = rec_prune_above_filtration(root(), filtration); | ||
|
@@ -1660,7 +1683,7 @@ class Simplex_tree { | |
} | ||
|
||
private: | ||
bool rec_prune_above_filtration(Siblings* sib, Filtration_value filt) { | ||
bool rec_prune_above_filtration(Siblings* sib, const Filtration_value& filt) { | ||
auto&& list = sib->members(); | ||
auto last = std::remove_if(list.begin(), list.end(), [this,filt](Dit_value_t& simplex) { | ||
if (simplex.second.filtration() <= filt) return false; | ||
|
@@ -2070,7 +2093,7 @@ class Simplex_tree { | |
* @param[in] filt_value The new filtration value. | ||
* @param[in] min_dim The minimal dimension. Default value is 0. | ||
*/ | ||
void reset_filtration(Filtration_value filt_value, int min_dim = 0) { | ||
void reset_filtration(const Filtration_value& filt_value, int min_dim = 0) { | ||
rec_reset_filtration(&root_, filt_value, min_dim); | ||
clear_filtration(); // Drop the cache. | ||
} | ||
|
@@ -2081,7 +2104,7 @@ class Simplex_tree { | |
* @param[in] filt_value The new filtration value. | ||
* @param[in] min_depth The minimal depth. | ||
*/ | ||
void rec_reset_filtration(Siblings * sib, Filtration_value filt_value, int min_depth) { | ||
void rec_reset_filtration(Siblings * sib, const Filtration_value& filt_value, int min_depth) { | ||
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) { | ||
if (min_depth <= 0) { | ||
sh->second.assign_filtration(filt_value); | ||
|
@@ -2246,6 +2269,24 @@ class Simplex_tree { | |
/** \brief Upper bound on the dimension of the simplicial complex.*/ | ||
int dimension_; | ||
bool dimension_to_be_lowered_ = false; | ||
|
||
// MULTIPERS STUFF | ||
public: | ||
/** | ||
* \brief Sets the number of parameters of the filtrations if SimplexTreeOptions::is_multi_parameter. | ||
* */ | ||
void set_number_of_parameters(int num) { number_of_parameters_ = num; } | ||
VincentRouvreau marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** | ||
* \brief Gets the number of parameters of the filtrations if SimplexTreeOptions::is_multi_parameter. | ||
* */ | ||
int get_number_of_parameters() const { return number_of_parameters_; } | ||
|
||
inline static Filtration_value inf_ = std::numeric_limits<Filtration_value>::has_infinity ? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an issue with |
||
std::numeric_limits<Filtration_value>::infinity() | ||
: std::numeric_limits<Filtration_value>::max(); /**< Default infinite value. */ | ||
|
||
private: | ||
int number_of_parameters_; /**< Number of parameters of the multi-filtrations when SimplexTreeOptions::is_multi_parameter.-*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, why not, as the get number of parameters will always be 1 for non-multiparam simplextrees, this default value seems good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OTOH, a "broken" value like 0 (or even -1) forces the user to be explicit (which can be seen as good or bad). |
||
}; | ||
|
||
// Print a Simplex_tree in os. | ||
|
@@ -2297,6 +2338,7 @@ struct Simplex_tree_options_full_featured { | |
static const bool contiguous_vertices = false; | ||
static const bool link_nodes_by_label = false; | ||
static const bool stable_simplex_handles = false; | ||
static const bool is_multi_parameter = false; | ||
}; | ||
|
||
/** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe | ||
|
@@ -2314,6 +2356,7 @@ struct Simplex_tree_options_fast_persistence { | |
static const bool contiguous_vertices = true; | ||
static const bool link_nodes_by_label = false; | ||
static const bool stable_simplex_handles = false; | ||
static const bool is_multi_parameter = false; | ||
}; | ||
|
||
/** Model of SimplexTreeOptions, faster cofaces than `Simplex_tree_options_full_featured`, note the | ||
|
@@ -2331,6 +2374,8 @@ struct Simplex_tree_options_fast_cofaces { | |
static const bool contiguous_vertices = false; | ||
static const bool link_nodes_by_label = true; | ||
static const bool stable_simplex_handles = false; | ||
static const bool is_multi_parameter = false; | ||
|
||
}; | ||
|
||
/** @}*/ // end addtogroup simplex_tree | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I already commented somewhere that we should remove this line (and the same in several places) because is_multi_parameter is already false in the base class.