-
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?
Conversation
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.
This is not small. Could you give more overview, to help review this? List the files modified explaining what happened to each. If a file is mostly a copy-paste of another file, say it and explain what differs and why we cannot easily share the code.
Random minor comments while trying to find where to start reading.
* Maximum number of simplices to compute persistence is <CODE>std::numeric_limits<std::uint32_t>::max()</CODE> | ||
* (about 4 billions of simplices). */ | ||
|
||
using value_type = double; |
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.
Can we avoid this?
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.
Yes, this could be set in the SimplexTree Options.
|
||
using option_multi = Simplex_tree_options_multidimensional_filtration; | ||
using option_std = Simplex_tree_options_full_featured; | ||
bool operator<(const std::vector<value_type>& v1, const std::vector<value_type>& v2) |
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.
It isn't legal to redefine operator<
for vector<double>
. Maybe we could define a wrapper to vector<double>
for the multi_filtrations, which could then have whatever semantic we want?
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'm not sure what illegal means here; but I can do a "multi_filtration" class which would make that legal.
That will also help for non-finitely-critical filtrations (which cannot be encoded in a vector).
bool operator<(const std::vector<value_type>& v1, const std::vector<value_type>& v2) | ||
{ | ||
bool isSame = true; | ||
if (v1.size() != v2.size()) isSame = false; |
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.
Do we want to allow comparing filtrations of different size? How is that defined?
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.
It may be useful when comparing a projection of a filtration onto a lower-dimensional multi-filtration, with another lower-dimensional filtration, without doing the projection, but that would be very niche, and wouldn't be implemented like that. We can remove this indeed.
Simplex_tree<option_std> &st = *(Gudhi::Simplex_tree<option_std>*)(splxptr); | ||
Simplex_tree<option_multi> &st_multi = *(Gudhi::Simplex_tree<option_multi>*)(newsplxptr);; | ||
if (dimension <= 0) | ||
{std::cout << "Empty filtration\n"; return ;} |
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.
probably cerr/clog. Is it a valid use case, in which case the message may be annoying, or an error, in which case we could throw an exception?
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.
That is an error. But we don't want to crash the C++ otherwise it will crash the python kernel. I will change that to a cerr.
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.
Throwing an exception does not crash anything (but you need to add except +
in the .pyx so the exception is converted properly). Or you can return something special and check that in python. Or you can check the dimension in python before calling the C++ function. Or...
|
||
euler_chars_type out(point_list.size(), 0.); | ||
|
||
auto is_greater = [nparameters](const point_type &a, const point_type &b){ //french greater |
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.
"French" in the sense is_greater_equal?
return simplex_list; | ||
} | ||
|
||
euler_chars_type euler_char(const std::vector<std::vector<double>> &point_list){ // TODO multi-critical |
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.
IIRC it can be computed more efficiently when the points are on a grid, which seems like a common use case, to be discussed with Vadim & Olympio.
return number_of_parameters_; | ||
} | ||
private: | ||
unsigned int number_of_parameters_; |
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.
One extra integer is acceptable, but I wonder if we could make it exist or not depending on the template parameter?
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 didn't find anywhere else to put it. Its not possible on the SimplexTree options (static). I can put it in the python class, but that would imply passing this integer to c++ each time a c++ function is called.
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.
One possibility would be [[no_unique_address]] std::conditional_t<Options::something, int, SomeEmptyType> number_of_parameters_;
so that depending on the option, it would not take any space. Or have Simplex_tree derive from Maybe_multi_base<SimplexTreeOptions::plouf>
which depending on plouf can be empty or contain an int (and provide 2 methods).
Again, that's not fundamental, I just wanted to mention this possibility.
Simplex_tree_node_explicit_storage(Siblings * sib = nullptr, | ||
Filtration_value filtration = 0) | ||
Simplex_tree_node_explicit_storage(Siblings * sib, | ||
Filtration_value filtration) //MULTIPERS : init to 0 not possible |
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.
Would Filtration_value filtration = {}
work?
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'm not sure it will compile with the standard simplextree (Filtration_value
is a double)
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.
double d = {};
compiles just fine.
cimport cython | ||
from gudhi import SimplexTree ## Small hack for typing | ||
|
||
from filtration_domination import remove_strongly_filtration_dominated, remove_filtration_dominated |
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.
This dependency needs to be documented. Does the whole file need to depend on it, or could we make it so only one function depends on it?
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.
This dependence is only needed in the collapse_edge
method. I can put this dependence in this method indeed
I also have a question : Would you think another class for "coordinate simplextrees" would be useful ? i.e. I have specific functions for these kinds of simplextrees (with the coordinate stored in |
I assume the question is mostly for Python, since in C++ using |
strong:bool | ||
Whether to use strong collapses or collapses (slower, but may remove more edges) | ||
full:bool | ||
Collapses the maximum number of edges if true, i.e., will do at most 100 strong collapses and 100 non-strong collapses afterward. |
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 don't understand what that means.
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 full
flag is meant to do all possible collapses, i.e., doing all possible strong collapses, and then all "usual" collapses (definitions here, definition 2.1). As multiparameter persistence algorithms tend to be very slow, doing all possible collapses can be worth it.
Another reason is that calling this method calls the method get_edge_list
, and if the user wants to do strong and weak collapses, get_edge_list
will get called only once.
print(f"The file {path} already exists. Use the `overwrite` flag if you want to overwrite.") | ||
return |
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.
Raising an exception is a nice way to signal a problem in Python.
|
||
|
||
// ######################## MULTIPERS STUFF | ||
void reset_keys(){ |
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.
"reset" sounds like it is going to give all of them their default value, 0. Maybe use a name that reflects the fact that you enumerate/number the simplices?
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.
This is related to a gudhi bug (if it isn't already fixed); the keys of the simplicies of a python simplextree are all 0, so I have to re-initialize them using this function.
I didn't find a nice name, but set_keys_to_enumerate
be OK ?
I will remove this functions once this "gudhi python bug" is fixed.
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.
That's not a bug. The name "key" is misleading, it is arbitrary data. The persistence algorithm does start by assigning an index as key, but then changes it during its execution.
Yes, set_keys_to_enumerate
is much clearer, thanks.
simplex_list.push_back(simplex); | ||
} | ||
} | ||
simplex_list.shrink_to_fit(); |
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.
What is the point of shrinking here? This will be copied to a Python object and destroyed quickly.
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 supposed that the copy to python was maybe smaller this way. If it's not necessary, I can remove that.
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.
From what I can see, when converting a std::vector to python, cython creates a list with PyList_New(<Py_ssize_t> v.size())
, it never even looks at the capacity of the vector.
std::vector<double> new_filtration_value = Base::filtration(SimplexHandle); | ||
new_filtration_value.resize(num); | ||
Base::assign_filtration(SimplexHandle, new_filtration_value); |
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.
Looks like it would be useful to be able to get a reference to the filtration, so we could modify it in place instead of copying twice. The interface was designed with double
in mind, not vector...
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.
Yes, I didn't wanted to change already existing files too much, and keep it "simple", so I preferred to do this copy for now. I will have to change that at some point
void expansion(int max_dim) nogil except + | ||
void remove_maximal_simplex(simplex_type simplex) nogil | ||
bool prune_above_filtration(filtration_type filtration) nogil | ||
# bool make_filtration_non_decreasing() nogil |
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 am surprised how few modifications there are in Simplex_tree.h. Is make_filtration_non_decreasing
almost the only function that does not do what you want? Does expansion
compute the max of filtration vectors properly?
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.
Yes, all function that are not related to either persistence, or relying on filtration comparison, usually work without any issue. I'm not sure the prune_above_filtration
works properly. The expansion
works for the simplex part, but it calls the make_filtration_non_decreasing
afterwards, that is rewritten in the simplex_tree_multi.pyx
.
The code for make_filtration_non_decreasing
is in python for the moment, so it's super slow.
} | ||
|
||
euler_chars_type euler_char(const std::vector<std::vector<double>> &point_list){ // TODO multi-critical | ||
const unsigned int npts = point_list.size(); |
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.
(picking one place randomly)
Using an unsigned type to encode the fact that a number cannot be negative is not super popular these days, except to match an existing interface. For npts
it would be natural to use std::size_t
, what size()
returns. For nparameters
which is known to be small, either size_t
because that's what size returns, or int
which is the default type for small integers. unsigned int
tends to be only used when doing bit operations, both because its arithmetic is more error-prone than the corresponding signed type, and because it is often harder to optimize for compilers. Although if you like unsigned int
we can live with it, we already have quite a few in Gudhi, I just wanted to make sure you were aware of this.
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.
OK, I will change that to an int; with all of the other occurrences of unsigned int, as I'm not doing bit operations.
I don't know about the rank invariant, but for the Euler characteristic surface, I don't think you want to actually store the complex with rounded filtration values, you compute the rounded values on the fly and forget them. One algorithm I imagine is that you have a table of 0s matching the grid, then for each simplex you find the corresponding cell in the array (round the filtration values up) and add ±1 to this cell depending on the dimension, and finally you compute a cumulative sum in each dimension on the array. |
simplex_list.push_back(simplex); | ||
} | ||
} | ||
simplex_list.shrink_to_fit(); |
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.
From what I can see, when converting a std::vector to python, cython creates a list with PyList_New(<Py_ssize_t> v.size())
, it never even looks at the capacity of the vector.
def get_simplices_of_dimension(self, dim:int): | ||
return self.get_ptr().get_simplices_of_dimension(dim) |
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.
This is going to return a List[List[int]]. I think there are 2 nicer choices:
- a generator, like get_simplices, so it does not use any memory
- a numpy array, if we want to store it explicitly, since all the simplices have the same dimension
:param simplex: The N-simplex, represented by a list of vertex. | ||
:type simplex: list of int | ||
:returns: The simplicial complex filtration value. | ||
:rtype: float |
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.
Er, no, not a float, that's the whole point of this class 😜
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.
Yes I have to update the doc 😉
# filtration_vector = self.get_ptr().simplex_filtration(simplex) | ||
# filtrations = np.array_split(filtration_vector, len(filtration_vector) // self.num_parameters) | ||
# return filtrations.squeeze() # If 1 critical returns only 1 filtration | ||
return self.get_ptr().simplex_filtration(simplex) # Same output type, better |
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 like reshape((-1, num_parameters))
, it makes it clear that there are multiple critical points.
squeeze
has the problem that you end up having to test the size of the output for every call. If the whole filtration is 1-critical, it may be convenient to avoid the extra dimension, but I don't think it is worth the resulting inconsistency.
Algorithmica, pages 1–22, 2014. | ||
|
||
This class is a filtered, with keys, and non contiguous vertices version | ||
of the 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 don't think this doc gets compiled by sphinx currently, and it does not always reflect the multifiltration.
Thanks Marc for the review,
|
…ion overwrite (mutlicritical filtrations are not well supported yet), optimizations
Changes since last update :
A notebook showcasing the interface can be found here. Also note that the gudhi 1-parameter simplextrees are retrieved from python using this kind of trick ( template<class simplextreeinterface>
simplextreeinterface& get_simplextree_from_pointer(const uintptr_t splxptr){ //DANGER
simplextreeinterface &st = *(simplextreeinterface*)(splxptr);
return st;
} so if the python version wasn't compiled with the same version of the EDIT : notebook link update |
This kind of change should really be handled as a separate PR, otherwise it just makes everything harder to review. |
These changes should all be behind a |
After re-reading the code in the PR, I noticed that my previous statements were false :
|
I had to add a static variable I'm also not sure that the python interface will be able to run with |
@@ -31,5 +31,7 @@ struct SimplexTreeOptions { | |||
static const bool link_nodes_by_label; | |||
/// If true, Simplex_handle will not be invalidated after insertions or removals. | |||
static const bool stable_simplex_handles; | |||
|
|||
static const bool is_multi_parameter; |
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).
Filtration_value filtration() const { return filt_; } | ||
Filtration_simplex_base_real() : filt_{} {} | ||
void assign_filtration(const Filtration_value& f) { filt_ = f; } | ||
// Filtration_value filtration() const { return filt_; } |
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.
It would be clearer if you remove all the unused code you commented (not only here, there is commented code a bit everywhere).
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 comment
The 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 filtration()
with the dummy and use if constexpr
instead. This makes conceptually more sense for me and it would allow to clean out the dummy completely (and therefore make it a real dummy). For this PR, it would mean that we don't need to add the null_value
to get around the problem that dummy::filtration()
is literally called and therefore has to return something.
@mglisse @VincentRouvreau, what do you think? Should I make an issue?
return inf_; | ||
} | ||
} | ||
static Filtration_value& filtration_mutable(Simplex_handle sh){ |
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 am wondering if we could not just keep the version with the non const
, as the validity of the filtration in the simplex tree is already responsibility of the user. This would avoid having to find a different name when it does exactly the same thing than filtration()
. Or could there be performance issues when using non const references for numerical values (i.e., when the simplex tree is not multi)?
If it is the case, an ugly way to solve this would be to return a std::conditional
to switch between Filtration_value&
and Filtration_value
depending on the value of is_multi_parameter
.
if (filtration(simplex_one) > filt) { | ||
assign_filtration(simplex_one, filt); | ||
if (!(filtration(simplex_one) <= filt)) { // TODO : For multipersistence, it's not clear what should be the default, especially for multicritical filtrations | ||
if constexpr (SimplexTreeOptions::is_multi_parameter){ |
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.
This section seems to be work in progress?
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.
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 if
is doing), but I'm not sure that someone would want to deal with this complicated behavior; doing this by hand seems fine. I thus assume here that the user is smart. I will just remove these comments.
@@ -1603,12 +1633,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), | |||
typename SimplexTreeOptions::Filtration_value max_filt_border_value; |
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.
typename SimplexTreeOptions::Filtration_value
can simply be written as Filtration_value
. Again at line 1639.
Boundary_simplex_iterator max_border = std::max_element(std::begin(boundary), std::end(boundary), | ||
typename SimplexTreeOptions::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 comment
The 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 finitely_critical_filtrations.h
to here (inside include/gudhi/Simplex_tree
folder) and mention that it realizes the concept for the multi simplex tree filtration values to make it accessible.
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.
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 comment
The 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 Simplex_tree.h
and add something like:
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 /python/include/Simplex_tree_multi.h
. That is the only change. Then for the documentation, in the concept of the options (concept/SimplexTreeOptions.h) you mention the need of push_to
and that you can find an example of implementation as Finitely_critical_multi_filtration<T>
.
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 comment
The 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 is_multi_parameter==true
, and those could end up in the C++ part, although that's more work.
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.
@mglisse Not really, it was just to illustrate how we would use it. If Filtration_value needs a special method like push_to
, we cannot just let it hang there without an explanation for the user, in particular if code was already written. We can also put this just in the tests or in an example, but at least in the concept, the implementation of Finitely_critical_multi_filtration<T>
should be mentioned and not hidden away in the python folder.
@@ -2239,6 +2279,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 comment
The 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 inf_
) should be put in a mixin with a dummy like struct Filtration_simplex_base_real
and struct Filtration_simplex_base_dummy
.
@@ -2290,6 +2342,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 comment
The reason will be displayed to describe this comment to others. Learn more.
Should it not be has_multi_parameter
?
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.
either has_multiple_parameters
or is_multi_parameter
. I prefer the second one.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to also add some tests with is_multi_parameter
set to true
.
This is a basic implementation of multiparameter simplex trees (for python), via a new (python) class : SimplexTreeMulti.
This code is a cleaned version of my multipers repository.
It is basically the same simplextree as the standard python one, but with
Filtration_value
being a vector instead of a real number.Key points:
The attached jupyter notebook showcases the main functions (sorry for the format, github doesn't allow .ipynb files).
EDIT: Forgot the descriptions of files.
New file description and file modifications:
Simplextree.h
Simplex_tree_node_explicit_storage.h
=0
which is not possible with vector-filtrationssimplex_tree_multi.pyx
simplex_tree.pyx
. Cannot be shared with this file, unless we merge the two classes (which needs work as theSimplexTree
class would hold two different c++ classes).simplex_tree_multi.pyd
simplex_tree.pxd
, with more multi-specific functionsSimplex_tree_multi.h
SimplexTree.h
Simplex_tree_interface_multi.h
Simplex_tree_interface.h
. Stores the template of simplextree for python of SimplexTreeMultiutils
folder