-
Notifications
You must be signed in to change notification settings - Fork 67
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 #817
base: master
Are you sure you want to change the base?
Changes from all commits
6f3aa21
8abadb5
8ab7de0
8e6ef26
9525fe7
3ab6d2c
87821b9
7ac6fab
a603ef7
0b0cd34
a4ea7d0
a7d1609
ff0c9b9
44c89a4
3129205
cb5eb3c
18054a3
a67628c
3b5734b
44abf0d
dcfc551
79a42b4
79c52cd
962f6e3
b73577f
5bb12cc
4f89234
6267d41
f4b355a
32a6a49
36de642
82ce7d0
8fd1ccd
b212c16
655fd08
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 |
---|---|---|
|
@@ -154,16 +154,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_; } | ||
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"); } | ||
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. This would probably be more part of another PR, but now that we use C++ standard 17, we could stop calling |
||
}; | ||
typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real, | ||
Filtration_simplex_base_dummy>::type Filtration_simplex_base; | ||
|
@@ -586,7 +588,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(); | ||
} | ||
|
@@ -614,18 +616,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){ | ||
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 am wondering if we could not just keep the version with the non |
||
if (sh != null_simplex()) { | ||
return sh->second.filtration(); | ||
} else { | ||
return inf_; | ||
} | ||
} | ||
|
||
/** \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); | ||
|
@@ -848,7 +857,7 @@ class Simplex_tree { | |
*/ | ||
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(); | ||
|
@@ -914,7 +923,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); | ||
|
||
|
@@ -943,7 +952,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) { | ||
const Filtration_value& filtration = {}) { | ||
auto first = std::begin(Nsimplex); | ||
auto last = std::end(Nsimplex); | ||
|
||
|
@@ -972,7 +981,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 | ||
|
@@ -989,8 +998,20 @@ 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); | ||
if (!(filtration(simplex_one) <= filt)) { | ||
if constexpr (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. This section seems to be work in progress? 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. Not really, as multiparameter filtrations have to be done carefully, I don't want the insertion to change the filtration values of the other simplices. We could define a behavior based on if the filtration value is comparable to the previous filtration value and create a multicritical filtration / change the values of the other simplices (that's what the |
||
// By default, does nothing and assumes that the user is smart. | ||
if (filt < filtration(simplex_one)){ | ||
// placeholder for comparable filtrations | ||
} | ||
else{ | ||
// placeholder for incomparable filtrations | ||
} | ||
} | ||
else{ // non-multiparameter | ||
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(); | ||
|
@@ -1335,7 +1356,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)); | ||
|
@@ -1689,7 +1710,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) { | ||
|
@@ -1901,12 +1922,22 @@ 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), | ||
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. | ||
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 super specific and should be mentioned in the documentation and the concepts. I would also move 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. It's a possibility, but I'm only using python, and I will not maintain / deal with a c++ interface that I don't use. 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. It is not about making a C++ interface. Just move the file, include it in struct Simplex_tree_options_multi_parameter {
typedef linear_indexing_tag Indexing_tag;
typedef int Vertex_handle;
typedef Finitely_critical_multi_filtration<float> Filtration_value;
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 = false;
static const bool stable_simplex_handles = false;
static const bool is_multi_parameter = true;
}; at the end of the file with the other options instead of having it in The difference is that it will make the tree usable for C++ users without you having to change much or to maintain anything. 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. @hschreiber IIUC you are suggesting defining this Simplex_tree_options_multi_parameter in (a file included by) Simplex_tree.h? We need to have a conversation about #905, but in my opinion we should define very few of those by default and Simplex_tree_options_fast_persistence may already have been a mistake. There is no particular reason why someone using multiparameters should particularly want float vs double for instance. Having it in python, as "what we use for SimplexTreeMulti", makes sense to me. On the other hand, maybe more functions could be implemented more generically, to work with any option that has 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. @mglisse Not really, it was just to illustrate how we would use it. If Filtration_value needs a special method like |
||
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); | ||
}); | ||
|
||
Filtration_value max_filt_border_value = filtration(*max_border); | ||
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)) { | ||
|
@@ -1941,7 +1972,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); | ||
|
@@ -1951,7 +1982,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(); | ||
bool modified = false; | ||
bool emptied = false; | ||
|
@@ -2383,7 +2414,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. | ||
} | ||
|
@@ -2394,7 +2425,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); | ||
|
@@ -2560,6 +2591,18 @@ class Simplex_tree { | |
/** \brief Upper bound on the dimension of the simplicial complex.*/ | ||
int dimension_; | ||
bool dimension_to_be_lowered_ = false; | ||
|
||
//MULTIPERS STUFF | ||
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. To be consistent with the option strategy of the simplex tree so far, this (except for the |
||
public: | ||
void set_number_of_parameters(int num){ | ||
number_of_parameters_ = num; | ||
} | ||
int get_number_of_parameters() const{ | ||
return number_of_parameters_; | ||
} | ||
inline static Filtration_value inf_ = std::numeric_limits<Filtration_value>::infinity(); | ||
private: | ||
int number_of_parameters_; | ||
}; | ||
|
||
// Print a Simplex_tree in os. | ||
|
@@ -2611,6 +2654,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; | ||
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. Should it not be 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. either |
||
}; | ||
|
||
/** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe | ||
|
@@ -2628,6 +2672,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 | ||
|
@@ -2645,6 +2690,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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ struct Simplex_tree_options_stable_simplex_handles { | |
static const bool contiguous_vertices = false; | ||
static const bool link_nodes_by_label = true; | ||
static const bool stable_simplex_handles = true; | ||
static const bool is_multi_parameter = false; | ||
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. It would be nice to also add some tests with |
||
}; | ||
|
||
typedef boost::mpl::list<Simplex_tree<>, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from tqdm import tqdm | ||
from filtration_domination import remove_strongly_filtration_dominated, remove_filtration_dominated | ||
|
||
def _collapse_edge_list(edges, num:int=0, full:bool=False, strong:bool=False, progress:bool=False): | ||
""" | ||
Given an edge list defining a 1 critical 2 parameter 1 dimensional simplicial complex, simplificates this filtered simplicial complex, using filtration-domination's edge collapser. | ||
""" | ||
n = len(edges) | ||
if full: | ||
num = 100 | ||
with tqdm(range(num), total=num, desc="Removing edges", disable=not(progress)) as I: | ||
for i in I: | ||
if strong: | ||
edges = remove_strongly_filtration_dominated(edges) # nogil ? | ||
else: | ||
edges = remove_filtration_dominated(edges) | ||
# Prevents doing useless collapses | ||
if len(edges) >= n: | ||
if full and strong: | ||
strong = False | ||
n = len(edges) | ||
# n = edges.size() # len(edges) | ||
else : | ||
break | ||
else: | ||
n = len(edges) | ||
# n = edges.size() | ||
return edges | ||
|
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.
The use of the attribute has to be described here like the others (the concepts are used for documentation).