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 ctors with data #1151

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
129 changes: 80 additions & 49 deletions src/Simplex_tree/include/gudhi/Simplex_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* - 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.
* - 2024/08 Marc Glisse: Allow storing custom data in simplices.
* - YYYY/MM Author: Description of the modification
*/

Expand Down Expand Up @@ -61,7 +62,7 @@

namespace Gudhi {

/** \addtogroup simplex_tree
/** \addtogroup simplex_tree
* @{
*/

Expand Down Expand Up @@ -280,12 +281,12 @@ class Simplex_tree {
typedef Simplex_tree_complex_simplex_iterator<Simplex_tree> Complex_simplex_iterator;
/** \brief Range over the simplices of the simplicial complex. */
typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
/** \brief Iterator over the simplices of the skeleton of the simplicial complex, for a given
/** \brief Iterator over the simplices of the skeleton of the simplicial complex, for a given
* dimension.
*
* 'value_type' is Simplex_handle. */
typedef Simplex_tree_skeleton_simplex_iterator<Simplex_tree> Skeleton_simplex_iterator;
/** \brief Range over the simplices of the skeleton of the simplicial complex, for a given
/** \brief Range over the simplices of the skeleton of the simplicial complex, for a given
* dimension. */
typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
/** \brief Range over the simplices of the simplicial complex, ordered by the filtration. */
Expand All @@ -299,7 +300,7 @@ class Simplex_tree {
/** \name Range and iterator methods
* @{ */

/** \brief Returns a range over the vertices of the simplicial complex.
/** \brief Returns a range over the vertices of the simplicial complex.
* The order is increasing according to < on Vertex_handles.*/
Complex_vertex_range complex_vertex_range() {
return Complex_vertex_range(
Expand Down Expand Up @@ -418,7 +419,7 @@ class Simplex_tree {
* 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.
Expand All @@ -434,15 +435,18 @@ class Simplex_tree {
copy_from(complex_source, translate_filtration_value);
}

/** \brief User-defined copy constructor reproduces the whole tree structure. */
/** \brief User-defined copy constructor reproduces the whole tree structure including extra data (@ref Simplex_data)
* stored in the simplices.
*/
Simplex_tree(const Simplex_tree& complex_source) {
#ifdef DEBUG_TRACES
std::clog << "Simplex_tree copy constructor" << std::endl;
#endif // DEBUG_TRACES
copy_from(complex_source);
}

/** \brief User-defined move constructor relocates the whole tree structure.
/** \brief User-defined move constructor relocates the whole tree structure including extra data (@ref Simplex_data)
* stored in the simplices.
* \exception std::invalid_argument In debug mode, if the complex_source is invalid.
*/
Simplex_tree(Simplex_tree && complex_source) {
Expand All @@ -461,7 +465,9 @@ class Simplex_tree {
root_members_recursive_deletion();
}

/** \brief User-defined copy assignment reproduces the whole tree structure. */
/** \brief User-defined copy assignment reproduces the whole tree structure including extra data (@ref Simplex_data)
* stored in the simplices.
*/
Simplex_tree& operator= (const Simplex_tree& complex_source) {
#ifdef DEBUG_TRACES
std::clog << "Simplex_tree copy assignment" << std::endl;
Expand All @@ -476,7 +482,8 @@ class Simplex_tree {
return *this;
}

/** \brief User-defined move assignment relocates the whole tree structure.
/** \brief User-defined move assignment relocates the whole tree structure including extra data (@ref Simplex_data)
* stored in the simplices.
* \exception std::invalid_argument In debug mode, if the complex_source is invalid.
*/
Simplex_tree& operator=(Simplex_tree&& complex_source) {
Expand Down Expand Up @@ -509,7 +516,21 @@ class Simplex_tree {
for (auto& map_el : root_.members()) {
map_el.second.assign_children(&root_);
}
rec_copy<Options::store_key>(
// Specific for optionnal data
if constexpr (!std::is_same_v<Simplex_data, No_simplex_data>) {
auto dst_iter = root_.members().begin();
auto src_iter = root_source.members().begin();

while(dst_iter != root_.members().end() || src_iter != root_source.members().end()) {
dst_iter->second.data() = src_iter->second.data();
dst_iter++;
src_iter++;
}
// Check in debug mode members data were copied
assert(dst_iter == root_.members().end());
assert(src_iter == root_source.members().end());
}
rec_copy<Options::store_key, true>(
&root_, &root_source, [](const Filtration_value& fil) -> const Filtration_value& { return fil; });
}

Expand All @@ -533,11 +554,11 @@ class Simplex_tree {
}
}

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

/** \brief depth first search, inserts simplices when reaching a leaf. */
template<bool store_key, typename OtherSiblings, typename F>
template<bool store_key, bool copy_simplex_data, 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) {
Expand All @@ -548,18 +569,23 @@ class Simplex_tree {
newsib->members_.reserve(sh_source->second.children()->members().size());
}
for (auto & child : sh_source->second.children()->members()){
Dictionary_it new_it{};
if constexpr (store_key && Options::store_key) {
newsib->members_.emplace_hint(
new_it = 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(),
new_it = newsib->members_.emplace_hint(newsib->members_.end(),
child.first,
Node(newsib, translate_filtration_value(child.second.filtration())));
}
// Specific for optionnal data
if constexpr (copy_simplex_data && !std::is_same_v<Simplex_data, No_simplex_data>) {
new_it->second.data() = child.second.data();
}
}
rec_copy<store_key>(newsib, sh_source->second.children(), translate_filtration_value);
rec_copy<store_key, copy_simplex_data>(newsib, sh_source->second.children(), translate_filtration_value);
sh->second.assign_children(newsib);
}
}
Expand Down Expand Up @@ -612,7 +638,9 @@ class Simplex_tree {
public:
template<typename> friend class Simplex_tree;

/** \brief Checks if two simplex trees are equal. */
/** \brief Checks if two simplex trees are equal. Any extra data (@ref Simplex_data) stored in the simplices are
* ignored in the comparison.
*/
template<class OtherSimplexTreeOptions>
bool operator==(Simplex_tree<OtherSimplexTreeOptions>& st2) {
if ((null_vertex_ != st2.null_vertex_) ||
Expand Down Expand Up @@ -758,7 +786,7 @@ class Simplex_tree {

/**
* @brief Returns the dimension of the given sibling simplices.
*
*
* @param curr_sib Pointer to the sibling container.
* @return Height of the siblings in the tree (root counts as zero to make the height correspond to the dimension).
*/
Expand Down Expand Up @@ -918,7 +946,7 @@ class Simplex_tree {
* we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
* output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
* null_simplex.
*
*
*/
template <class RandomVertexHandleRange = std::initializer_list<Vertex_handle>>
std::pair<Simplex_handle, bool> insert_simplex_raw(const RandomVertexHandleRange& simplex,
Expand Down Expand Up @@ -1229,7 +1257,7 @@ class Simplex_tree {
* \param simplex represent the simplex of which we search the star
* \return Vector of Simplex_tree::Simplex_handle (empty vector if no star found) when
* SimplexTreeOptions::link_nodes_by_label is false.
*
*
* Simplex_tree::Simplex_handle range for an optimized search for the star of a simplex when
* SimplexTreeOptions::link_nodes_by_label is true.
*/
Expand All @@ -1243,7 +1271,7 @@ class Simplex_tree {
* cofaces (equivalent of star function)
* \return Vector of Simplex_tree::Simplex_handle (empty vector if no cofaces found) when
* SimplexTreeOptions::link_nodes_by_label is false.
*
*
* Simplex_tree::Simplex_handle range for an optimized search for the coface of a simplex when
* SimplexTreeOptions::link_nodes_by_label is true.
*/
Expand Down Expand Up @@ -1452,15 +1480,15 @@ class Simplex_tree {
* @brief Adds a new vertex or a new edge in a flag complex, as well as all
* simplices of its star, defined to maintain the property
* of the complex to be a flag complex, truncated at dimension dim_max.
* To insert a new edge, the two given vertex handles have to correspond
* To insert a new edge, the two given vertex handles have to correspond
* to the two end points of the edge. To insert a new vertex, the handles
* have to be twice the same and correspond to the number you want assigned
* to it. I.e., to insert vertex \f$i\f$, give \f$u = v = i\f$.
* The method assumes that the given edge was not already contained in
* the simplex tree, so the behaviour is undefined if called on an existing
* edge. Also, the vertices of an edge have to be inserted before the edge.
*
* @param[in] u,v Vertex_handle representing the new edge
* @param[in] u,v Vertex_handle representing the new edge
* (@p v != @p u) or the new vertex (@p v == @p u).
* @param[in] fil Filtration value of the edge.
* @param[in] dim_max Maximal dimension of the expansion.
Expand All @@ -1478,8 +1506,8 @@ class Simplex_tree {
* filtration values, the method `make_filtration_non_decreasing()` has to be
* called at the end of the insertions to restore the intended filtration.
* Note that even then, an edge has to be inserted after its vertices.
* @warning The method assumes that the given edge or vertex was not already
* contained in the simplex tree, so the behaviour is undefined if called on
* @warning The method assumes that the given edge or vertex was not already
* contained in the simplex tree, so the behaviour is undefined if called on
* an existing simplex.
*/
void insert_edge_as_flag( Vertex_handle u
Expand Down Expand Up @@ -2084,7 +2112,7 @@ class Simplex_tree {
bool prune_above_dimension(int dimension) {
if (dimension >= dimension_)
return false;

bool modified = false;
if (dimension < 0) {
if (num_vertices() > 0) {
Expand Down Expand Up @@ -2185,17 +2213,17 @@ class Simplex_tree {
}
}

/** \brief Retrieve the original filtration value for a given simplex in the Simplex_tree. Since the
/** \brief Retrieve the original filtration value for a given simplex in the Simplex_tree. Since the
* computation of extended persistence requires modifying the filtration values, this function can be used
* to recover the original values. Moreover, computing extended persistence requires adding new simplices
* in the Simplex_tree. Hence, this function also outputs the type of each simplex. It can be either UP (which means
* that the simplex was present originally, and is thus part of the ascending extended filtration), DOWN (which means
* that the simplex is the cone of an original simplex, and is thus part of the descending extended filtration) or
* EXTRA (which means the simplex is the cone point). See the definition of Extended_simplex_type. Note that if the simplex type is DOWN, the original filtration value
* is set to be the original filtration value of the corresponding (not coned) original simplex.
* is set to be the original filtration value of the corresponding (not coned) original simplex.
* \pre This function should be called only if `extend_filtration()` has been called first!
* \post The output filtration value is supposed to be the same, but might be a little different, than the
* original filtration value, due to the internal transformation (scaling to [-2,-1]) that is
* original filtration value, due to the internal transformation (scaling to [-2,-1]) that is
* performed on the original filtration values during the computation of extended persistence.
* @param[in] f Filtration value of the simplex in the extended (i.e., modified) filtration.
* @param[in] efd Structure containing the minimum and maximum values of the original filtration. This the output of `extend_filtration()`.
Expand All @@ -2215,12 +2243,12 @@ class Simplex_tree {
return p;
};

/** \brief Extend filtration for computing extended persistence.
* This function only uses the filtration values at the 0-dimensional simplices,
* and computes the extended persistence diagram induced by the lower-star filtration
* computed with these values.
* \post Note that after calling this function, the filtration
* values are actually modified. The function `decode_extended_filtration()`
/** \brief Extend filtration for computing extended persistence.
* This function only uses the filtration values at the 0-dimensional simplices,
* and computes the extended persistence diagram induced by the lower-star filtration
* computed with these values.
* \post Note that after calling this function, the filtration
* values are actually modified. The function `decode_extended_filtration()`
* retrieves the original values and outputs the extended simplex type.
* @exception std::invalid_argument In debug mode if the Simplex tree contains a vertex with the largest
* Vertex_handle, as this method requires to create an extra vertex internally.
Expand All @@ -2241,7 +2269,7 @@ class Simplex_tree {
maxval = std::max(maxval, f);
maxvert = std::max(sh->first, maxvert);
}

GUDHI_CHECK(maxvert < std::numeric_limits<Vertex_handle>::max(), std::invalid_argument("Simplex_tree contains a vertex with the largest Vertex_handle"));
maxvert++;

Expand Down Expand Up @@ -2280,7 +2308,7 @@ class Simplex_tree {
// Automatically assign good values for simplices
this->make_filtration_non_decreasing();

// Return the filtration data
// Return the filtration data
return Extended_filtration_data(minval, maxval);
}

Expand Down Expand Up @@ -2398,14 +2426,14 @@ class Simplex_tree {

//decltype(testPtr) = boost::intrusive::compact_rbtree_node<void*>*
decltype(testPtr) sh_ptr = decltype(testPtr)((const char*)(&node) - shift); //shifts from node to pointer
//decltype(testIIt) =
//decltype(testIIt) =
//boost::intrusive::tree_iterator<
// boost::intrusive::bhtraits<
// boost::container::base_node<
// std::pair<const int, Simplex_tree_node_explicit_storage<Simplex_tree>>,
// boost::container::dtl::intrusive_tree_hook<void*, boost::container::red_black_tree, true>, true>,
// boost::intrusive::rbtree_node_traits<void*, true>,
// boost::intrusive::normal_link,
// boost::intrusive::rbtree_node_traits<void*, true>,
// boost::intrusive::normal_link,
// boost::intrusive::dft_tag,
// 3>,
// false>
Expand Down Expand Up @@ -2482,9 +2510,9 @@ class Simplex_tree {

public:
/** @private @brief Returns the serialization required buffer size.
*
*
* @return The exact serialization required size in number of bytes.
*
*
* @warning It is meant to return the same size with the same SimplexTreeOptions and on a computer with the same
* architecture.
*/
Expand All @@ -2497,16 +2525,18 @@ class Simplex_tree {
#endif // DEBUG_TRACES
return buffer_byte_size;
}

/** @private @brief Serialize the Simplex tree - Flatten it in a user given array of char
*
*
* @param[in] buffer An array of char allocated with enough space (cf. Gudhi::simplex_tree::get_serialization_size)
* @param[in] buffer_size The buffer size.
*
*
* @exception std::invalid_argument If serialization does not match exactly the buffer_size value.
*
*
* @warning Serialize/Deserialize is not portable. It is meant to be read in a Simplex_tree with the same
* SimplexTreeOptions and on a computer with the same architecture.
*
* Serialize/Deserialize ignores any extra data (@ref Simplex_data) stored in the simplices for now.
*/
/* Let's take the following simplicial complex as example: */
/* (vertices are represented as letters to ease the understanding) */
Expand Down Expand Up @@ -2565,16 +2595,17 @@ class Simplex_tree {
public:
/** @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.
*
*
* @param[in] buffer A pointer on a buffer that contains a serialized Simplex_tree.
* @param[in] buffer_size The size of the buffer.
*
*
* @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 and on a computer with the same architecture.
*
*
* Serialize/Deserialize ignores any extra data (@ref Simplex_data) stored in the simplices for now.
*/
void deserialize(const char* buffer, const std::size_t buffer_size) {
GUDHI_CHECK(num_vertices() == 0, std::logic_error("Simplex_tree::deserialize - Simplex_tree must be empty"));
Expand Down
3 changes: 3 additions & 0 deletions src/Simplex_tree/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ gudhi_add_boost_test(Simplex_tree_edge_expansion_unit_test)

add_executable_with_targets(Simplex_tree_extended_filtration_test_unit simplex_tree_extended_filtration_unit_test.cpp TBB::tbb)
gudhi_add_boost_test(Simplex_tree_extended_filtration_test_unit)

add_executable_with_targets(Simplex_tree_data_test_unit simplex_tree_data_unit_test.cpp TBB::tbb)
gudhi_add_boost_test(Simplex_tree_data_test_unit)
Loading
Loading