Skip to content
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

[Simplex tree] Additional copy constructor for Simplex_tree #1121

Merged
102 changes: 85 additions & 17 deletions src/Simplex_tree/include/gudhi/Simplex_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* - 2023/05 Clément Maria: Edge insertion method for flag complexes
* - 2023/05 Hannah Schreiber: Factorization of expansion methods
* - 2023/08 Hannah Schreiber (& Clément Maria): Add possibility of stable simplex handles.
* - 2024/08 Hannah Schreiber: Addition of customizable copy constructor.
* - YYYY/MM Author: Description of the modification
*/

Expand Down Expand Up @@ -132,6 +133,7 @@ class Simplex_tree {

struct Key_simplex_base_real {
Key_simplex_base_real() : key_(-1) {}
Key_simplex_base_real(Simplex_key k) : key_(k) {}
void assign_key(Simplex_key k) { key_ = k; }
Simplex_key key() const { return key_; }
private:
Expand All @@ -143,6 +145,7 @@ class Simplex_tree {
void assign_key(Simplex_key);
Simplex_key key() const;
};

struct Extended_filtration_data {
Filtration_value minval;
Filtration_value maxval;
Expand All @@ -154,14 +157,20 @@ class Simplex_tree {

struct Filtration_simplex_base_real {
Filtration_simplex_base_real() : filt_(0) {}
Filtration_simplex_base_real(Filtration_value f) : filt_(f) {}
void assign_filtration(Filtration_value f) { filt_ = f; }
Filtration_value filtration() const { 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_simplex_base_dummy(Filtration_value GUDHI_CHECK_code(f)) {
GUDHI_CHECK(f == 0, "filtration value specified for a complex that does not store them");
}
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; }
};
typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real,
Expand Down Expand Up @@ -245,7 +254,7 @@ class Simplex_tree {
typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
/** \brief Range over the cofaces of a simplex. */
typedef typename std::conditional<Options::link_nodes_by_label,
Optimized_cofaces_simplex_filtered_range, // faster implem
Optimized_cofaces_simplex_filtered_range, // faster implementation
std::vector<Simplex_handle>>::type Cofaces_simplex_range;

/** \private
Expand Down Expand Up @@ -345,7 +354,7 @@ class Simplex_tree {
/** \brief Returns a range over the vertices of a simplex.
*
* The order in which the vertices are visited is the decreasing order for < on Vertex_handles,
* which is consequenlty
* which is consequently
* equal to \f$(-1)^{\text{dim} \sigma}\f$ the canonical orientation on the simplex.
*/
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const {
Expand Down Expand Up @@ -402,6 +411,29 @@ class Simplex_tree {
filtration_vect_(),
dimension_(-1) { }

/**
* @brief Construct the simplex tree as the copy of a given simplex tree with eventually different template
* parameters.
* Therefore, should provide a method converting the filtration values of one tree to the another. All other values
* are already implicitly convertible if the concept of @ref SimplexTreeOptions is respected (note that there is
* an eventual loss of precision or an undefined behaviour if a value is converted into a new type too small to
* contain it). Any extra data (@ref Simplex_data) stored in the simplices are ignored in the copy for now.
*
* @tparam OtherSimplexTreeOptions Options of the given simplex tree.
* @tparam F Method taking an OtherSimplexTreeOptions::Filtration_value as input and returning an
* Options::Filtration_value.
* @param complex_source Simplex tree to copy.
* @param translate_filtration_value Method taking an OtherSimplexTreeOptions::Filtration_value from the source tree
* as input and returning the corresponding Options::Filtration_value in the new tree.
*/
template<typename OtherSimplexTreeOptions, typename F>
Simplex_tree(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
#ifdef DEBUG_TRACES
std::clog << "Simplex_tree custom copy constructor" << std::endl;
#endif // DEBUG_TRACES
copy_from(complex_source, translate_filtration_value);
}

/** \brief User-defined copy constructor reproduces the whole tree structure. */
Simplex_tree(const Simplex_tree& complex_source) {
#ifdef DEBUG_TRACES
Expand Down Expand Up @@ -471,27 +503,63 @@ class Simplex_tree {
auto root_source = complex_source.root_;

// root members copy
root_.members() = Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
root_.members() =
Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
// Needs to reassign children
for (auto& map_el : root_.members()) {
map_el.second.assign_children(&root_);
}
rec_copy(&root_, &root_source);
rec_copy<Options::store_key>(
&root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
}

// Copy from complex_source to "this"
template<typename OtherSimplexTreeOptions, typename F>
void copy_from(const Simplex_tree<OtherSimplexTreeOptions>& complex_source, F&& translate_filtration_value) {
null_vertex_ = complex_source.null_vertex_;
filtration_vect_.clear();
dimension_ = complex_source.dimension_;
auto root_source = complex_source.root_;

// root members copy
if constexpr (!Options::stable_simplex_handles) root_.members().reserve(root_source.size());
for (auto& p : root_source.members()){
if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) {
auto it = root_.members().try_emplace(
root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()), p.second.key());
} else {
auto it = root_.members().try_emplace(
root_.members().end(), p.first, &root_, translate_filtration_value(p.second.filtration()));
}
}

rec_copy<OtherSimplexTreeOptions::store_key>(&root_, &root_source, translate_filtration_value);
hschreiber marked this conversation as resolved.
Show resolved Hide resolved
}

/** \brief depth first search, inserts simplices when reaching a leaf. */
void rec_copy(Siblings *sib, Siblings *sib_source) {
for (auto sh = sib->members().begin(), sh_source = sib_source->members().begin();
sh != sib->members().end(); ++sh, ++sh_source) {
template<bool store_key, typename OtherSiblings, typename F>
void rec_copy(Siblings *sib, OtherSiblings *sib_source, F&& translate_filtration_value) {
auto sh_source = sib_source->members().begin();
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh, ++sh_source) {
update_simplex_tree_after_node_insertion(sh);
if (has_children(sh_source)) {
Siblings * newsib = new Siblings(sib, sh_source->first);
if constexpr (!Options::stable_simplex_handles) {
newsib->members_.reserve(sh_source->second.children()->members().size());
}
for (auto & child : sh_source->second.children()->members())
newsib->members_.emplace_hint(newsib->members_.end(), child.first, Node(newsib, child.second.filtration()));
rec_copy(newsib, sh_source->second.children());
for (auto & child : sh_source->second.children()->members()){
if constexpr (store_key && Options::store_key) {
newsib->members_.emplace_hint(
newsib->members_.end(),
child.first,
Node(newsib, translate_filtration_value(child.second.filtration()), child.second.key()));
} else {
newsib->members_.emplace_hint(newsib->members_.end(),
child.first,
Node(newsib, translate_filtration_value(child.second.filtration())));
}
}
rec_copy<store_key>(newsib, sh_source->second.children(), translate_filtration_value);
sh->second.assign_children(newsib);
}
}
Expand Down Expand Up @@ -1432,9 +1500,9 @@ class Simplex_tree {
*
* Nodes with label @p u get affected only if a Node with label @p v is in their same
* siblings set.
* We then try to insert "ponctually" @p v all over the subtree rooted
* We then try to insert "punctually" @p v all over the subtree rooted
* at `Node(u)`. Each insertion of a Node with @p v label induces a local
* expansion at this Node (as explained above) and a sequence of "ponctual"
* expansion at this Node (as explained above) and a sequence of "punctual"
* insertion of `Node(v)` in the subtree rooted at sibling nodes of the new node,
* on its left.
*/
Expand Down Expand Up @@ -1479,7 +1547,7 @@ class Simplex_tree {

//for all siblings containing a Node labeled with u (including the root), run
//compute_punctual_expansion
//todo parallelise
//todo parallelize
List_max_vertex* nodes_with_label_u = nodes_by_label(u);//all Nodes with u label

GUDHI_CHECK(nodes_with_label_u != nullptr,
Expand Down Expand Up @@ -1798,7 +1866,7 @@ class Simplex_tree {
if (blocker_result) {
blocked_new_sib_vertex_list.push_back(new_sib_member->first);
// update data structures for all deleted simplices
// can be done in the loop as part of another datastructure
// can be done in the loop as part of another data structure
update_simplex_tree_before_node_removal(new_sib_member);
}
}
Expand Down Expand Up @@ -1972,7 +2040,7 @@ class Simplex_tree {
return true;
};

//TODO: `if constexpr` replacable by `std::erase_if` in C++20? Has a risk of additional runtime,
//TODO: `if constexpr` replaceable by `std::erase_if` in C++20? Has a risk of additional runtime,
//so to benchmark first.
if constexpr (Options::stable_simplex_handles) {
modified = false;
Expand Down Expand Up @@ -2312,7 +2380,7 @@ class Simplex_tree {
static Simplex_handle simplex_handle_from_node(Node& node) {
if constexpr (Options::stable_simplex_handles){
//Relies on the Dictionary type to be boost::container::map<Vertex_handle, Node>.
//If the type changes or boost fondamentally changes something on the structure of their map,
//If the type changes or boost fundamentally changes something on the structure of their map,
//a safer/more general but much slower version is:
// if (node.children()->parent() == label) { // verifies if node is a leaf
// return children->oncles()->find(label);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#define GUDHI_EMPTY_BASE_CLASS_OPTIMIZATION
#endif

#include <vector>
#include <boost/core/empty_value.hpp>

namespace Gudhi {
Expand All @@ -33,21 +32,25 @@ namespace Gudhi {
*
* It stores explicitly its own filtration value and its own Simplex_key.
*/
template<class SimplexTree>
struct GUDHI_EMPTY_BASE_CLASS_OPTIMIZATION Simplex_tree_node_explicit_storage : SimplexTree::Filtration_simplex_base,
SimplexTree::Key_simplex_base,
SimplexTree::Hooks_simplex_base,
boost::empty_value<typename SimplexTree::Simplex_data> {
template <class SimplexTree>
struct GUDHI_EMPTY_BASE_CLASS_OPTIMIZATION Simplex_tree_node_explicit_storage
: SimplexTree::Filtration_simplex_base,
SimplexTree::Key_simplex_base,
SimplexTree::Hooks_simplex_base,
boost::empty_value<typename SimplexTree::Simplex_data> {
typedef typename SimplexTree::Siblings Siblings;
typedef typename SimplexTree::Filtration_value Filtration_value;
typedef typename SimplexTree::Simplex_key Simplex_key;
typedef typename SimplexTree::Simplex_data Simplex_data;

Simplex_tree_node_explicit_storage(Siblings * sib = nullptr,
Filtration_value filtration = 0)
: children_(sib) {
this->assign_filtration(filtration);
}
Simplex_tree_node_explicit_storage(Siblings* sib = nullptr, Filtration_value filtration = 0)
: SimplexTree::Filtration_simplex_base(filtration), children_(sib)
{}

//will fail to compile when called with SimplexTree::Options::store_key is false.
Simplex_tree_node_explicit_storage(Siblings* sib, Filtration_value filtration, Simplex_key key)
: SimplexTree::Filtration_simplex_base(filtration), SimplexTree::Key_simplex_base(key), children_(sib)
{}

/*
* Assign children to the node
Expand Down
Loading
Loading