Skip to content

Commit

Permalink
enable deserialization from a simplex tree with different filtration …
Browse files Browse the repository at this point in the history
…value type
  • Loading branch information
hschreiber committed Nov 7, 2024
1 parent 9c2047f commit e46325e
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 7 deletions.
43 changes: 38 additions & 5 deletions src/Simplex_tree/include/gudhi/Simplex_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2671,21 +2671,54 @@ 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 <class F>
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<std::size_t>(ptr - buffer) != buffer_size) {
throw std::invalid_argument("Deserialization does not match end of buffer");
}
}

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 <class F>
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) {
Expand All @@ -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.
Expand All @@ -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_) {
Expand Down
83 changes: 81 additions & 2 deletions src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
#include <iostream>
#include <cstring> // for std::size_t and strncmp
#include <random>
#include <iterator> // for std::distance
#include <type_traits>
#include <vector>
#include <cstdint> // for std::uint8_t
#include <iomanip> // for std::setfill, setw
#include <ios> // for std::hex, uppercase
Expand Down Expand Up @@ -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<Simplex_tree_vec_fil>;
using target_st_type = Simplex_tree<Simplex_tree_options_default>;

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<Simplex_tree_options_default>;
using target_st_type = Simplex_tree<Simplex_tree_vec_fil>;

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);
}

0 comments on commit e46325e

Please sign in to comment.