diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index a6fbe9cba1..45a955dd87 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -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 */ @@ -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: @@ -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; @@ -154,6 +157,7 @@ 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: @@ -161,7 +165,12 @@ class Simplex_tree { }; 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 Simplex_vertex_range; /** \brief Range over the cofaces of a simplex. */ typedef typename std::conditional>::type Cofaces_simplex_range; /** \private @@ -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 { @@ -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 + Simplex_tree(const Simplex_tree& 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 @@ -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( + &root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; }); + } + + // Copy from complex_source to "this" + template + void copy_from(const Simplex_tree& 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(&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 + 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(newsib, sh_source->second.children(), translate_filtration_value); sh->second.assign_children(newsib); } } @@ -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. */ @@ -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, @@ -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); } } @@ -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; @@ -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. - //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); diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h index 2d5ab0f6b1..738f13b817 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h @@ -18,7 +18,6 @@ #define GUDHI_EMPTY_BASE_CLASS_OPTIMIZATION #endif -#include #include namespace Gudhi { @@ -33,21 +32,25 @@ namespace Gudhi { * * It stores explicitly its own filtration value and its own Simplex_key. */ -template -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 { +template +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 { 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 diff --git a/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp index 46d3bbc14a..1d370adfef 100644 --- a/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp @@ -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 > list_of_tested_variants; + Simplex_tree > list_of_tested_variants; + +std::string print_filtration_value(double fil){ + return std::to_string(fil); +} + +std::string print_filtration_value(std::vector fil){ + std::string ss; + for (auto val : fil) ss += std::to_string(val) + " "; + ss.pop_back(); + return ss; +} template void print_simplex_filtration(Simplex_tree& st, const std::string& msg) { @@ -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; } @@ -179,6 +177,165 @@ 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 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 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 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 > + 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); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_custom_copy_constructor_key, typeST, list_of_custom_fil_variants) +{ + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST OF CUSTOM COPY CONSTRUCTOR WITH KEY VALUES" << std::endl; + + typeST st; + + 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}); + + /* 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] */ + + if constexpr (typeST::Options::store_key){ + for (auto f_simplex : st.complex_simplex_range()) { + std::int32_t key = 1; + for (auto filt : st.filtration(f_simplex)) { + key *= filt; + } + st.assign_key(f_simplex, key); + } + } + + auto trans = [](const typename typeST::Filtration_value& fil) + -> Simplex_tree::Filtration_value { + Simplex_tree::Filtration_value copy(std::begin(fil), + std::end(fil)); + std::sort(copy.begin(), copy.end()); + return copy; + }; + + Simplex_tree st_trans(st, trans); + for (auto f_simplex : st_trans.complex_simplex_range()) { + auto filtrations = st_trans.filtration(f_simplex); + BOOST_CHECK(std::is_sorted(std::begin(filtrations), std::end(filtrations))); + + if constexpr (typeST::Options::store_key){ + std::int32_t key = 1; + for (auto filt : st_trans.filtration(f_simplex)) { + key *= filt; + } + std::clog << "key = " << st_trans.key(f_simplex) << " vs " << key << std::endl; + + BOOST_CHECK(st_trans.key(f_simplex) == key); + } else { + std::clog << "No key stored initially: key = " << st_trans.key(f_simplex) << std::endl; + + BOOST_CHECK(st_trans.key(f_simplex) == -1); + } + } +} + template std::vector> get_star(Simplex_tree& st) { std::vector> output; diff --git a/src/Simplex_tree/test/simplex_tree_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_unit_test.cpp index 04b5f1671d..d0dc0140ec 100644 --- a/src/Simplex_tree/test/simplex_tree_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_unit_test.cpp @@ -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 > list_of_tested_variants; + Simplex_tree > list_of_tested_variants; template void test_empty_simplex_tree(typeST& tst) { @@ -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 sst1; + Simplex_tree sst1; sst1.insert_graph(g); BOOST_CHECK(sst1.num_simplices() == 6); @@ -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 sst2; + Simplex_tree sst2; sst2.insert_graph(g); BOOST_CHECK(sst2.num_simplices() == 6); @@ -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 > list_of_tested_variants_wo_fast_persistence; BOOST_AUTO_TEST_CASE_TEMPLATE(batch_vertices, typeST, list_of_tested_variants_wo_fast_persistence) {