Skip to content

Commit

Permalink
add new copy constructor to simplex tree
Browse files Browse the repository at this point in the history
  • Loading branch information
hschreiber committed Aug 13, 2024
1 parent f23c154 commit 2956d4c
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 56 deletions.
86 changes: 70 additions & 16 deletions src/Simplex_tree/include/gudhi/Simplex_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,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 +345,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 +402,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 +494,58 @@ 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
for (auto& p : root_source.members()){
auto it = root_.members().try_emplace(root_.members().end(), p.first);
it->second.assign_children(&root_);
it->second.assign_filtration(translate_filtration_value(p.second.filtration()));
if constexpr (Options::store_key && OtherSimplexTreeOptions::store_key) it->second.assign_key(p.second.key());
}

rec_copy<OtherSimplexTreeOptions::store_key>(&root_, &root_source, translate_filtration_value);
}

/** \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)
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 +1486,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 +1533,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 +1852,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 +2026,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 +2366,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,20 +32,23 @@ 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)
Filtration_value filtration = 0,
[[maybe_unused]] Simplex_key key = -1)
: children_(sib) {
this->assign_filtration(filtration);
if constexpr (SimplexTree::Options::store_key) this->assign_key(key);
}

/*
Expand Down
119 changes: 104 additions & 15 deletions src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,20 @@

using namespace Gudhi;

struct Simplex_tree_options_stable_simplex_handles {
typedef linear_indexing_tag Indexing_tag;
typedef int Vertex_handle;
typedef double 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 = true;
static const bool stable_simplex_handles = true;
};

typedef boost::mpl::list<Simplex_tree<>,
Simplex_tree<Simplex_tree_options_fast_persistence>,
Simplex_tree<Simplex_tree_options_full_featured>,
Simplex_tree<Simplex_tree_options_stable_simplex_handles> > list_of_tested_variants;
Simplex_tree<Simplex_tree_options_full_featured> > list_of_tested_variants;

std::string print_filtration_value(double fil){
return std::to_string(fil);
}

std::string print_filtration_value(std::vector<int> fil){
std::string ss;
for (auto val : fil) ss += std::to_string(val) + " ";
ss.pop_back();
return ss;
}

template<typename Simplex_tree>
void print_simplex_filtration(Simplex_tree& st, const std::string& msg) {
Expand All @@ -55,7 +53,7 @@ void print_simplex_filtration(Simplex_tree& st, const std::string& msg) {
std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n";
for (auto f_simplex : st.filtration_simplex_range()) {
std::clog << " "
<< "[" << st.filtration(f_simplex) << "] ";
<< "[" << print_filtration_value(st.filtration(f_simplex)) << "] ";
for (auto vertex : st.simplex_vertex_range(f_simplex)) std::clog << "(" << vertex << ")";
std::clog << std::endl;
}
Expand Down Expand Up @@ -179,6 +177,97 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_copy_constructor, Simplex_tree, list_of_te

}

struct Simplex_tree_options_custom_fil_values_default {
typedef linear_indexing_tag Indexing_tag;
typedef std::int16_t Vertex_handle;
typedef std::vector<int> Filtration_value;
typedef std::int32_t Simplex_key;
static const bool store_key = false;
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;
};

struct Simplex_tree_options_custom_fil_values_fast_persistence {
typedef linear_indexing_tag Indexing_tag;
typedef std::int16_t Vertex_handle;
typedef std::vector<int> Filtration_value;
typedef std::int32_t Simplex_key;
static const bool store_key = true;
static const bool store_filtration = true;
static const bool contiguous_vertices = true;
static const bool link_nodes_by_label = false;
static const bool stable_simplex_handles = false;
};

struct Simplex_tree_options_custom_fil_values_full_featured {
typedef linear_indexing_tag Indexing_tag;
typedef std::int16_t Vertex_handle;
typedef std::vector<int> Filtration_value;
typedef std::int32_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 = true;
};

typedef boost::mpl::list<Simplex_tree<Simplex_tree_options_custom_fil_values_default>,
Simplex_tree<Simplex_tree_options_custom_fil_values_fast_persistence>,
Simplex_tree<Simplex_tree_options_custom_fil_values_full_featured> >
list_of_custom_fil_variants;

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_custom_copy_constructor, typeST, list_of_custom_fil_variants) {
typeST st;
Simplex_tree<> st_witness;

st.insert_simplex_and_subfaces({2, 1, 0}, {3, 1});
st.insert_simplex_and_subfaces({0, 1, 6, 7}, {4, 1});
st.insert_simplex_and_subfaces({3, 0}, {2, 1});
st.insert_simplex_and_subfaces({3, 4, 5}, {3, 1});
st.insert_simplex_and_subfaces({8}, {1, 1});

st_witness.insert_simplex_and_subfaces({2, 1, 0}, 3.0);
st_witness.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.0);
st_witness.insert_simplex_and_subfaces({3, 0}, 2.0);
st_witness.insert_simplex_and_subfaces({3, 4, 5}, 3.0);
st_witness.insert_simplex_and_subfaces({8}, 1.0);
/* Inserted simplex: */
/* 1 6 */
/* o---o */
/* /X\7/ */
/* o---o---o---o o */
/* 2 0 3\X/4 8 */
/* o */
/* 5 */
/* */
/* In other words: */
/* A facet [2,1,0] */
/* An edge [0,3] */
/* A facet [3,4,5] */
/* A cell [0,1,6,7] */
/* A vertex [8] */

print_simplex_filtration(st, "Simplex_tree is initialized");

std::clog << "********************************************************************" << std::endl;
std::clog << "TEST OF CUSTOM COPY CONSTRUCTOR" << std::endl;

auto trans = [](const typename typeST::Filtration_value& fil) -> Simplex_tree<>::Filtration_value {
return fil[0];
};

Simplex_tree<> st1(st, trans);
Simplex_tree<> st2(st, trans);
print_simplex_filtration(st1, "First custom copy constructor from the Simplex_tree");
print_simplex_filtration(st2, "Second custom copy constructor from the Simplex_tree");
// Cross check
BOOST_CHECK(st1 == st2);
BOOST_CHECK(st_witness == st2);
BOOST_CHECK(st1 == st_witness);
}

template<typename Simplex_tree>
std::vector<std::vector<typename Simplex_tree::Vertex_handle>> get_star(Simplex_tree& st) {
std::vector<std::vector<typename Simplex_tree::Vertex_handle>> output;
Expand Down
22 changes: 4 additions & 18 deletions src/Simplex_tree/test/simplex_tree_unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,9 @@

using namespace Gudhi;

struct Simplex_tree_options_stable_simplex_handles {
typedef linear_indexing_tag Indexing_tag;
typedef int Vertex_handle;
typedef double 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 = true;
static const bool stable_simplex_handles = true;
};

typedef boost::mpl::list<Simplex_tree<>,
Simplex_tree<Simplex_tree_options_fast_persistence>,
Simplex_tree<Simplex_tree_options_full_featured>,
Simplex_tree<Simplex_tree_options_stable_simplex_handles> > list_of_tested_variants;
Simplex_tree<Simplex_tree_options_full_featured> > list_of_tested_variants;

template<class typeST>
void test_empty_simplex_tree(typeST& tst) {
Expand Down Expand Up @@ -933,7 +920,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_insert_graph, Graph, list_of_graph_va
st1.insert_graph(g);
BOOST_CHECK(st1.num_simplices() == 6);

Simplex_tree<Simplex_tree_options_stable_simplex_handles> sst1;
Simplex_tree<Simplex_tree_options_full_featured> sst1;
sst1.insert_graph(g);
BOOST_CHECK(sst1.num_simplices() == 6);

Expand All @@ -948,7 +935,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_insert_graph, Graph, list_of_graph_va
st2.insert_graph(g);
BOOST_CHECK(st2.num_simplices() == 6);

Simplex_tree<Simplex_tree_options_stable_simplex_handles> sst2;
Simplex_tree<Simplex_tree_options_full_featured> sst2;
sst2.insert_graph(g);
BOOST_CHECK(sst2.num_simplices() == 6);

Expand Down Expand Up @@ -1157,8 +1144,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_boundaries_and_opposite_vertex_iterat
}

typedef boost::mpl::list<Simplex_tree<>,
Simplex_tree<Simplex_tree_options_full_featured>,
Simplex_tree<Simplex_tree_options_stable_simplex_handles> >
Simplex_tree<Simplex_tree_options_full_featured> >
list_of_tested_variants_wo_fast_persistence;

BOOST_AUTO_TEST_CASE_TEMPLATE(batch_vertices, typeST, list_of_tested_variants_wo_fast_persistence) {
Expand Down

0 comments on commit 2956d4c

Please sign in to comment.