diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 53225db16..62cd16041 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2671,13 +2671,40 @@ class Simplex_tree { * */ void deserialize(const char* buffer, const std::size_t buffer_size) { + deserialize(buffer, buffer_size, [](Filtration_value& filtration, const char* ptr) { + using namespace Gudhi::simplex_tree; + return deserialize_trivial(filtration, ptr); + }); + } + + /** @private @brief Deserialize the array of char (flatten version of the tree) to initialize a Simplex tree. + * It is the user's responsibility to provide an 'empty' Simplex_tree, there is no guarantee otherwise. + * + * @tparam F Method taking an @ref Filtration_value and a `const char*` as input and returning a + * `const char*`. + * @param[in] buffer A pointer on a buffer that contains a serialized Simplex_tree. + * @param[in] buffer_size The size of the buffer. + * @param[in] deserialize_filtration_value To provide if the type of @ref Filtration_value is not trivially + * convertible from the filtration value type of the serialized simplex tree. Takes the filtration value to fill and + * a pointer to the current position in the buffer as arguments and returns the new position of the pointer after + * reading the filtration value and transforming it into a element of the host's filtration value type. + * + * @exception std::invalid_argument In case the deserialization does not finish at the correct buffer_size. + * @exception std::logic_error In debug mode, if the Simplex_tree is not 'empty'. + * + * @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same + * SimplexTreeOptions (except for the @ref Filtration_value type) and on a computer with the same architecture. + * + */ + template + void deserialize(const char* buffer, const std::size_t buffer_size, F&& deserialize_filtration_value) { using namespace Gudhi::simplex_tree; GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty")); const char* ptr = buffer; // Needs to read size before recursivity to manage new siblings for children Vertex_handle members_size; ptr = deserialize_trivial(members_size, ptr); - ptr = rec_deserialize(&root_, members_size, ptr, 0); + ptr = rec_deserialize(&root_, members_size, ptr, 0, deserialize_filtration_value); if (static_cast(ptr - buffer) != buffer_size) { throw std::invalid_argument("Deserialization does not match end of buffer"); } @@ -2685,7 +2712,13 @@ class Simplex_tree { private: /** \brief Serialize each element of the sibling and recursively call serialization. */ - const char* rec_deserialize(Siblings *sib, Vertex_handle members_size, const char* ptr, int dim) { + template + const char* rec_deserialize(Siblings* sib, + Vertex_handle members_size, + const char* ptr, + int dim, + [[maybe_unused]] F&& deserialize_filtration_value) + { using namespace Gudhi::simplex_tree; // In case buffer is just a 0 char if (members_size > 0) { @@ -2694,8 +2727,8 @@ class Simplex_tree { Filtration_value filtration(0); for (Vertex_handle idx = 0; idx < members_size; idx++) { ptr = deserialize_trivial(vertex, ptr); - if (Options::store_filtration) { - ptr = deserialize_trivial(filtration, ptr); + if constexpr (Options::store_filtration) { + ptr = deserialize_filtration_value(filtration, ptr); } // Default is no children // If store_filtration is false, `filtration` is ignored. @@ -2708,7 +2741,7 @@ class Simplex_tree { if (child_size > 0) { Siblings* child = new Siblings(sib, sh->first); sh->second.assign_children(child); - ptr = rec_deserialize(child, child_size, ptr, dim + 1); + ptr = rec_deserialize(child, child_size, ptr, dim + 1, deserialize_filtration_value); } } if (dim > dimension_) { diff --git a/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp index d64fb06bb..c2ea8e24e 100644 --- a/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp @@ -11,9 +11,7 @@ #include #include // for std::size_t and strncmp #include -#include // for std::distance #include -#include #include // for std::uint8_t #include // for std::setfill, setw #include // for std::hex, uppercase @@ -364,3 +362,84 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_serialization_and_cofaces, Stree, lis BOOST_CHECK(num_stars == 5); } + +struct Simplex_tree_vec_fil : Simplex_tree_options_default { + typedef Vector_filtration_value Filtration_value; +}; + +BOOST_AUTO_TEST_CASE(simplex_tree_custom_deserialization_vec_to_double) { + using source_st_type = Simplex_tree; + using target_st_type = Simplex_tree; + + source_st_type st; + target_st_type st_copy; + target_st_type 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); + + auto deserialize_filtration_value = [](typename target_st_type::Filtration_value& fil, + const char* start) -> const char* { + typename source_st_type::Filtration_value origin_fil; + const char* ptr = deserialize_trivial(origin_fil, start); //naive way to do it, but fine enough for a test + fil = origin_fil[0]; + return ptr; + }; + + const std::size_t stree_buffer_size = st.get_serialization_size(); + char* stree_buffer = new char[stree_buffer_size]; + st.serialize(stree_buffer, stree_buffer_size); + + st_copy.deserialize(stree_buffer, stree_buffer_size, deserialize_filtration_value); + delete[] stree_buffer; + + BOOST_CHECK(st_witness == st_copy); +} + +BOOST_AUTO_TEST_CASE(simplex_tree_custom_deserialization_double_to_vec) { + using source_st_type = Simplex_tree; + using target_st_type = Simplex_tree; + + source_st_type st; + target_st_type st_copy; + target_st_type st_witness; + + st_witness.insert_simplex_and_subfaces({2, 1, 0}, {3, 1}); + st_witness.insert_simplex_and_subfaces({0, 1, 6, 7}, {4, 1}); + st_witness.insert_simplex_and_subfaces({3, 0}, {2, 1}); + st_witness.insert_simplex_and_subfaces({3, 4, 5}, {3, 1}); + st_witness.insert_simplex_and_subfaces({8}, {1, 1}); + + st.insert_simplex_and_subfaces({2, 1, 0}, 3.0); + st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.0); + st.insert_simplex_and_subfaces({3, 0}, 2.0); + st.insert_simplex_and_subfaces({3, 4, 5}, 3.0); + st.insert_simplex_and_subfaces({8}, 1.0); + + auto deserialize_filtration_value = [](typename target_st_type::Filtration_value& fil, + const char* start) -> const char* { + typename source_st_type::Filtration_value origin_fil; + const char* ptr = deserialize_trivial(origin_fil, start); + fil.resize(2, 1); + fil[0] = origin_fil; + return ptr; + }; + + const std::size_t stree_buffer_size = st.get_serialization_size(); + char* stree_buffer = new char[stree_buffer_size]; + st.serialize(stree_buffer, stree_buffer_size); + + st_copy.deserialize(stree_buffer, stree_buffer_size, deserialize_filtration_value); + delete[] stree_buffer; + + BOOST_CHECK(st_witness == st_copy); +}