From 6d5ad8b1fd79cca16807e269bff9556972be8c93 Mon Sep 17 00:00:00 2001 From: David Loiseaux Date: Thu, 5 Oct 2023 14:13:54 +0200 Subject: [PATCH] Optional num_parameters, vincent's tests of simplextree multi --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 13 +- .../Finitely_critical_filtrations.h | 76 +++- .../test/simplex_tree_multi_unit_test.cpp | 372 +++++++++++++++--- 3 files changed, 383 insertions(+), 78 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 894148e4b7..ddf9b0a7a1 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -400,7 +400,9 @@ class Simplex_tree { : null_vertex_(-1), root_(nullptr, null_vertex_), filtration_vect_(), - dimension_(-1) { } + dimension_(-1) { + if constexpr (Options::is_multi_parameter) number_of_parameters_ = 2; + } /** \brief User-defined copy constructor reproduces the whole tree structure. */ Simplex_tree(const Simplex_tree& complex_source) { @@ -408,12 +410,13 @@ class Simplex_tree { std::clog << "Simplex_tree copy constructor" << std::endl; #endif // DEBUG_TRACES copy_from(complex_source); + if constexpr (Options::is_multi_parameter) number_of_parameters_ = complex_source.number_of_parameters_; } /** \brief User-defined move constructor relocates the whole tree structure. * \exception std::invalid_argument In debug mode, if the complex_source is invalid. */ - Simplex_tree(Simplex_tree && complex_source) { + Simplex_tree(Simplex_tree && complex_source) : number_of_parameters_(std::move(complex_source.number_of_parameters_)) { #ifdef DEBUG_TRACES std::clog << "Simplex_tree move constructor" << std::endl; #endif // DEBUG_TRACES @@ -1627,7 +1630,7 @@ class Simplex_tree { Filtration_value max_filt_border_value; if constexpr (SimplexTreeOptions::is_multi_parameter) { // in that case, we assume that Filtration_value has a `push_to` member to handle this. - max_filt_border_value = Filtration_value(this->number_of_parameters_); + max_filt_border_value = Filtration_value(*number_of_parameters_); // is multiparam for (auto& face_sh : boundary) { max_filt_border_value.push_to( filtration(face_sh)); // pushes the value of max_filt_border_value to reach simplex' filtration @@ -2286,7 +2289,7 @@ class Simplex_tree { * */ int get_number_of_parameters() const { if constexpr (SimplexTreeOptions::is_multi_parameter) - return number_of_parameters_; + return *number_of_parameters_; else return 1; } @@ -2296,7 +2299,7 @@ class Simplex_tree { : std::numeric_limits::max(); /**< Default infinite value. */ private: - int number_of_parameters_; /**< Number of parameters of the multi-filtrations when SimplexTreeOptions::is_multi_parameter.-*/ + std::optional number_of_parameters_; /**< Number of parameters of the multi-filtrations when SimplexTreeOptions::is_multi_parameter.-*/ }; // Print a Simplex_tree in os. diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h b/src/Simplex_tree/include/gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h index 9343c9a723..56fe520fa3 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/multi_filtrations/Finitely_critical_filtrations.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace Gudhi::multiparameter::multi_filtrations { @@ -53,7 +54,20 @@ class Finitely_critical_multi_filtration : public std::vector { operator std::vector&() const { return *this; } std::vector get_vector() const { return static_cast>(*this); } - friend bool operator<(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + inline bool is_inf() const { + if (this->size() != 1) return false; + return *(this->begin()) == std::numeric_limits::infinity(); + } + inline bool is_minus_inf() const { + if (this->size() != 1) return false; + return *(this->begin()) == - std::numeric_limits::infinity(); + } + inline bool is_nan() const { + if (this->size() != 1) return false; + return std::isnan(*(this->begin())); + } + inline friend bool operator<(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + if (a.is_inf() || b.is_inf() || a.is_nan() || b.is_nan()) return false; bool isSame = true; int n = std::min(a.size(), b.size()); for (int i = 0; i < n; ++i) { @@ -63,7 +77,9 @@ class Finitely_critical_multi_filtration : public std::vector { if (isSame) return false; return true; } - friend bool operator<=(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + inline friend bool operator<=(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + if (a.is_nan() || b.is_nan()) return false; + if (b.is_inf()) return a.is_inf(); int n = std::min(a.size(), b.size()); for (int i = 0; i < n; ++i) { if (a[i] > b[i]) return false; @@ -72,32 +88,32 @@ class Finitely_critical_multi_filtration : public std::vector { } // GREATER THAN OPERATORS - friend bool operator>(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + inline friend bool operator>(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { return b < a; } - friend bool operator>=(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { + inline friend bool operator>=(const Finitely_critical_multi_filtration& a, const Finitely_critical_multi_filtration& b) { return b <= a; } - Finitely_critical_multi_filtration& operator=(const Finitely_critical_multi_filtration& a) { + inline Finitely_critical_multi_filtration& operator=(const Finitely_critical_multi_filtration& a) { std::vector::operator=(a); return *this; } std::vector& _convert_back() { return *this; } - friend Finitely_critical_multi_filtration& operator-=(Finitely_critical_multi_filtration& result, + inline friend Finitely_critical_multi_filtration& operator-=(Finitely_critical_multi_filtration& result, const Finitely_critical_multi_filtration& to_substract) { std::transform(result.begin(), result.end(), to_substract.begin(), result.begin(), std::minus()); return result; } - friend Finitely_critical_multi_filtration& operator+=(Finitely_critical_multi_filtration& result, + inline friend Finitely_critical_multi_filtration& operator+=(Finitely_critical_multi_filtration& result, const Finitely_critical_multi_filtration& to_add) { std::transform(result.begin(), result.end(), to_add.begin(), result.begin(), std::plus()); return result; } - friend Finitely_critical_multi_filtration& operator-=(Finitely_critical_multi_filtration& result, + inline friend Finitely_critical_multi_filtration& operator-=(Finitely_critical_multi_filtration& result, const T& to_substract) { // std::transform(result.begin(), result.end(), to_substract.begin(),result.begin(), std::minus()); for (auto& truc : result) { @@ -105,7 +121,7 @@ class Finitely_critical_multi_filtration : public std::vector { } return result; } - friend Finitely_critical_multi_filtration& operator+=(Finitely_critical_multi_filtration& result, + inline friend Finitely_critical_multi_filtration& operator+=(Finitely_critical_multi_filtration& result, const T& to_add) { for (auto& truc : result) { truc += to_add; @@ -114,7 +130,7 @@ class Finitely_critical_multi_filtration : public std::vector { } // template - friend bool operator==(Finitely_critical_multi_filtration& self, + inline friend bool operator==(Finitely_critical_multi_filtration& self, const Finitely_critical_multi_filtration& to_compare) { if (self.size() != to_compare.size()) return false; auto it = to_compare.begin(); @@ -124,28 +140,37 @@ class Finitely_critical_multi_filtration : public std::vector { return true; } - static std::vector> to_python(const std::vector>& to_convert) { + inline static std::vector> to_python(const std::vector>& to_convert) { return std::vector>(to_convert.begin(), to_convert.end()); } - static std::vector> from_python(const std::vector>& to_convert) { + inline static std::vector> from_python(const std::vector>& to_convert) { return std::vector>(to_convert.begin(), to_convert.end()); } - void push_to(const Finitely_critical_multi_filtration& x) { + inline void push_to(const Finitely_critical_multi_filtration& x) { + if (this->is_inf() || this->is_nan() || x.is_nan() || x.is_minus_inf()) + return; + if (x.is_inf() || this->is_minus_inf()) { + *this = x; + return; + } if (this->size() != x.size()) { - std::cerr << "Does only work with 1-critical filtrations ! Sizes " << this->size() << " and " << x.size() - << "are different !" << std::endl; + std::cerr << "Does only work with 1-critical filtrations ! Sizes " + << this->size() << " and " << x.size() + << "are different !" << std::endl; + std::cerr << "This : " << *this << std::endl; + std::cerr << "arg : " << x << std::endl; throw std::logic_error("Bad sizes"); } for (unsigned int i = 0; i < x.size(); i++) this->at(i) = this->at(i) > x[i] ? this->at(i) : x[i]; } // Warning, this function assumes that the comparisons checks have already been made ! - void insert_new(Finitely_critical_multi_filtration to_concatenate) { + inline void insert_new(Finitely_critical_multi_filtration to_concatenate) { this->insert(this->end(), std::move_iterator(to_concatenate.begin()), std::move_iterator(to_concatenate.end())); } // scalar product of a filtration value with x. - T linear_projection(const std::vector& x) { + inline T linear_projection(const std::vector& x) { T projection = 0; unsigned int size = std::min(x.size(), this->size()); for (auto i = 0u; i < size; i++) projection += x[i] * this->at(i); @@ -153,7 +178,19 @@ class Finitely_critical_multi_filtration : public std::vector { } // easy debug - friend std::ostream& operator<<(std::ostream& stream, const Finitely_critical_multi_filtration& truc) { + inline friend std::ostream& operator<<(std::ostream& stream, const Finitely_critical_multi_filtration& truc) { + if (truc.is_inf()) { + stream << "[inf, ..., inf]"; + return stream; + } + if (truc.is_minus_inf()) { + stream << "[-inf, ..., -inf]"; + return stream; + } + if (truc.is_nan()) { + stream << "[NaN]"; + return stream; + } if (truc.empty()) { stream << "[]"; return stream; @@ -192,4 +229,7 @@ static Gudhi::multiparameter::multi_filtrations::Finitely_critical_multi_filtrat } + + + #endif // FINITELY_CRITICAL_FILTRATIONS_H_ diff --git a/src/Simplex_tree/test/simplex_tree_multi_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_multi_unit_test.cpp index b62b8059ef..9d003ab729 100644 --- a/src/Simplex_tree/test/simplex_tree_multi_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_multi_unit_test.cpp @@ -1,6 +1,6 @@ /* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): David Loiseaux + * Author(s): David Loiseaux, Vincent Rouvreau * * Copyright (C) 2023 Inria * @@ -21,7 +21,7 @@ #include // for std::size_t #define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE "simplex_tree" +#define BOOST_TEST_MODULE "simplex_tree_multi" #include #include @@ -33,10 +33,14 @@ using namespace Gudhi; using namespace Gudhi::multiparameter; using Gudhi::multiparameter::multi_filtrations::Finitely_critical_multi_filtration; -using vec=Finitely_critical_multi_filtration; +using namespace Gudhi; +using namespace Gudhi::multiparameter; +using Multi_Filtration_values = Finitely_critical_multi_filtration; -typedef boost::mpl::list> list_of_tested_variants; using typeST_STD = Simplex_tree; +using Stree_multi = Simplex_tree; + +typedef boost::mpl::list list_of_tested_variants; template void test_empty_simplex_tree(typeST& tst) { @@ -583,10 +587,10 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_reset_filtration, typeST, list_of_tes std::clog << "TEST RESET FILTRATION" << std::endl; typeST st; - st.insert_simplex_and_subfaces({2, 1, 0}, vec({2.,1.})); - st.insert_simplex_and_subfaces({3, 0}, vec({1.,2.})); - st.insert_simplex_and_subfaces({3, 4, 5}, vec({3.,4.})); - st.insert_simplex_and_subfaces({0, 1, 6, 7}, vec({4.,3.})); + st.insert_simplex_and_subfaces({2, 1, 0}, Multi_Filtration_values({2.,1.})); + st.insert_simplex_and_subfaces({3, 0}, Multi_Filtration_values({1.,2.})); + st.insert_simplex_and_subfaces({3, 4, 5}, Multi_Filtration_values({3.,4.})); + st.insert_simplex_and_subfaces({0, 1, 6, 7}, Multi_Filtration_values({4.,3.})); std::cout <<"TRUC "<< st.filtration(st.find({2,1,0})) << std::endl; /* Inserted simplex: */ /* 1 6 */ @@ -605,7 +609,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_reset_filtration, typeST, list_of_tes std::clog << ") - filtration = " << st.filtration(f_simplex); std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl; // Guaranteed by construction - BOOST_CHECK(st.filtration(f_simplex) >= vec({1.,1.})); + BOOST_CHECK(st.filtration(f_simplex) >= Multi_Filtration_values({1.,1.})); } // dimension until 5 even if simplex tree is of dimension 3 to test the limits @@ -620,7 +624,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_reset_filtration, typeST, list_of_tes std::clog << ") - filtration = " << st.filtration(f_simplex); std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl; if (st.dimension(f_simplex) < dimension) - BOOST_CHECK(st.filtration(f_simplex) >= vec({1.,1})); + BOOST_CHECK(st.filtration(f_simplex) >= Multi_Filtration_values({1.,1})); else BOOST_CHECK(st.filtration(f_simplex) == st.inf_); } @@ -632,7 +636,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_clear, typeST, list_of_tested_variant std::clog << "********************************************************************" << std::endl; std::clog << "TEST SIMPLEX TREE CLEAR" << std::endl; typeST st; - st.insert_simplex_and_subfaces({0, 1}, vec({1.5})); + st.insert_simplex_and_subfaces({0, 1}, Multi_Filtration_values({1.5})); st.initialize_filtration(); st.clear(); BOOST_CHECK(st.num_vertices() == 0); @@ -642,54 +646,13 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_clear, typeST, list_of_tested_variant BOOST_CHECK(boost::size(st.filtration_simplex_range()) == 0); typeST st_empty; BOOST_CHECK(st == st_empty); - st.insert_simplex_and_subfaces({0}, vec({2.5})); + st.insert_simplex_and_subfaces({0}, Multi_Filtration_values({2.5})); BOOST_CHECK(boost::size(st.cofaces_simplex_range(st.find({0}), 1)) == 0); } -BOOST_AUTO_TEST_CASE_TEMPLATE(for_each_simplex_skip_iteration, typeST, list_of_tested_variants) { - std::clog << "********************************************************************" << std::endl; - std::clog << "TEST FOR_EACH ITERATION SKIP MECHANISM" << std::endl; - typeST st; - - st.insert_simplex_and_subfaces({2, 1, 0}, 3.); - st.insert_simplex_and_subfaces({3, 0}, 2.); - st.insert_simplex_and_subfaces({3, 4, 5}, 3.); - st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.); - - /* Inserted simplex: */ - /* 1 6 */ - /* o---o */ - /* /X\7/ */ - /* o---o---o---o */ - /* 2 0 3\X/4 */ - /* o */ - /* 5 */ - - std::vector num_simplices_by_dim_until_two(2); - auto lambda_nb_simp_by_dim = [&num_simplices_by_dim_until_two](typename typeST::Simplex_handle, int dim) - { - BOOST_CHECK (dim < 2); - ++num_simplices_by_dim_until_two[dim]; - return dim >= 1; // The iteration will skip the children in this case - }; - st.for_each_simplex(lambda_nb_simp_by_dim); - for (auto num_simplices : num_simplices_by_dim_until_two) - std::cout << num_simplices << ", "; - std::cout << std::endl; - - auto num_simplices_by_dim = st.num_simplices_by_dimension(); - for (auto num_simplices : num_simplices_by_dim) - std::cout << num_simplices << ", "; - std::cout << std::endl; - - BOOST_CHECK(num_simplices_by_dim_until_two[0] == num_simplices_by_dim[0]); - BOOST_CHECK(num_simplices_by_dim_until_two[1] == num_simplices_by_dim[1]); -} - - BOOST_AUTO_TEST_CASE_TEMPLATE(multify_simplex_tree, typeST, list_of_tested_variants) { std::clog << "********************************************************************" << std::endl; - std::clog << "TEST MULTIFY" << std::endl; + std::clog << "TEST MULTIFY FLATTEN LINEAR PROJECTION" << std::endl; typeST_STD st; typeST st_multi; @@ -701,7 +664,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(multify_simplex_tree, typeST, list_of_tested_varia BOOST_CHECK(st_multi.num_simplices() == st.num_simplices()); // simplicial complexes should be the same for (auto sh : st_multi.complex_simplex_range()){ const auto& filtration = st_multi.filtration(sh); - BOOST_CHECK(filtration == vec({1,2,3})); // Checks the filtration values + BOOST_CHECK(filtration == Multi_Filtration_values({1,2,3})); // Checks the filtration values } std::clog << "********************************************************************" << std::endl; std::clog << "TEST FLATTEN" << std::endl; @@ -724,3 +687,302 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(multify_simplex_tree, typeST, list_of_tested_varia } + + +BOOST_AUTO_TEST_CASE(simplex_tree_multi_assign_filtration) { + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST MULTI FILTRATION INSERT SIMPLEX AND SUBFACES" << std::endl; + Stree_multi st; + + typename Stree_multi::Simplex_handle sh; + bool success = false; + const Multi_Filtration_values multi_filt_1 = {1., 2.}; + std::tie(sh, success) = st.insert_simplex_and_subfaces({0, 1}, multi_filt_1); + BOOST_CHECK(success); + BOOST_CHECK(sh != st.null_simplex()); + // Only [0,1], [0] and [1] are already inserted + const Multi_Filtration_values multi_filt_2 = {3., 2., 1.}; + std::tie(sh, success) = st.insert_simplex_and_subfaces({2, 1, 0}, multi_filt_2); + BOOST_CHECK(success); + BOOST_CHECK(sh != st.null_simplex()); + // Already inserted + std::tie(sh, success) = st.insert_simplex_and_subfaces({0, 2}, {4.}); + BOOST_CHECK(!success); + BOOST_CHECK(sh != st.null_simplex()); + + // Check filtration values of an already inserted simplex + sh = st.find({1, 2}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_2); + // And assign a new value + Multi_Filtration_values const multi_filt_3 = {5.}; + st.assign_filtration(sh, multi_filt_3); + + for (auto f_simplex : st.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : st.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + std::clog << ") - filtration = " << st.filtration(f_simplex); + std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl; + } + + // Check all filtration values + sh = st.find({2, 1, 0}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_2); + sh = st.find({1, 0}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_1); + sh = st.find({2, 0}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_2); + sh = st.find({0}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_1); + sh = st.find({2, 1}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_3); + sh = st.find({1}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_1); + sh = st.find({2}); + BOOST_CHECK(sh != st.null_simplex()); + BOOST_CHECK(st.filtration(sh) == multi_filt_2); + +} + +BOOST_AUTO_TEST_CASE(simplex_tree_multi_reset_filtration) { + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST RESET MULTI FILTRATION" << std::endl; + Stree_multi st; + + st.insert_simplex_and_subfaces({2, 1, 0}, {3., 2.}); + st.insert_simplex_and_subfaces({3, 0}, {2., 3.}); + st.insert_simplex_and_subfaces({3, 4, 5}, {3., 2.}); + st.insert_simplex_and_subfaces({0, 1, 6, 7}, {4.}); + + /* Inserted simplex: */ + /* 1 6 */ + /* o---o */ + /* /X\7/ */ + /* o---o---o---o */ + /* 2 0 3\X/4 */ + /* o */ + /* 5 */ + + for (auto f_simplex : st.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : st.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + std::clog << ") - filtration = " << st.filtration(f_simplex); + std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl; + // Guaranteed by construction + BOOST_CHECK(st.filtration(f_simplex) >= 2.); + } + + Multi_Filtration_values new_filt = {0., 1., 2.}; + // dimension until 5 even if simplex tree is of dimension 3 to test the limits + for(int dimension = 5; dimension >= 0; dimension --) { + std::clog << "### reset_filtration - dimension = " << dimension << "\n"; + st.reset_filtration(new_filt, dimension); + for (auto f_simplex : st.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : st.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + std::clog << ") - filtration = " << st.filtration(f_simplex); + std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl; + if (st.dimension(f_simplex) >= dimension) + BOOST_CHECK(st.filtration(f_simplex) == new_filt); + } + } +} + +BOOST_AUTO_TEST_CASE(simplex_tree_multi_filtration_multify) { + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST MULTI FILTRATION MULTIFY" << std::endl; + + Simplex_tree<> st; + st.insert_simplex_and_subfaces({2, 1, 0}, 3.); + st.insert_simplex_and_subfaces({3, 0}, 2.); + st.insert_simplex_and_subfaces({3, 4, 5}, 3.); + st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.); + + /* Inserted simplex: */ + /* 1 6 */ + /* o---o */ + /* /X\7/ */ + /* o---o---o---o */ + /* 2 0 3\X/4 */ + /* o */ + /* 5 */ + + Stree_multi st_multi; + Multi_Filtration_values default_multi (3, std::numeric_limits::quiet_NaN()); + Gudhi::multiparameter::multify(st, st_multi, 3, default_multi); + + for (auto f_simplex : st_multi.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : st_multi.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + auto multi_filtration = st_multi.filtration(f_simplex); + std::clog << ") - filtration = " << multi_filtration << std::endl; + BOOST_CHECK(!std::isnan(multi_filtration[0])); + BOOST_CHECK(std::isnan(multi_filtration[1])); + BOOST_CHECK(std::isnan(multi_filtration[2])); + } + + { + Simplex_tree<> copy; + Gudhi::multiparameter::flatten(copy, st_multi, 0); + BOOST_CHECK(st == copy); + } + + { + Simplex_tree<> copy; + Gudhi::multiparameter::flatten(copy, st_multi, 1); + for (auto f_simplex : copy.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : copy.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + std::clog << ") - filtration = " << copy.filtration(f_simplex) << std::endl; + BOOST_CHECK(std::isnan(copy.filtration(f_simplex))); + } + } + +} + +BOOST_AUTO_TEST_CASE(simplex_tree_multi_filtration_numeric_limits) { + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST MULTI FILTRATION NUMERIC LIMITS" << std::endl; + + // NaN + auto nan_multi = std::numeric_limits::quiet_NaN(); + BOOST_CHECK(nan_multi.size() == 1); + BOOST_CHECK(std::isnan(nan_multi[0])); + std::clog << nan_multi << std::endl; + + // Inf + auto inf_multi = std::numeric_limits::infinity(); + BOOST_CHECK(inf_multi.size() == 1); + BOOST_CHECK(std::isinf(inf_multi[0])); + std::clog << inf_multi << std::endl; +} + +BOOST_AUTO_TEST_CASE(make_filtration_non_decreasing_on_multi) { + std::clog << "********************************************************************" << std::endl; + std::clog << "TEST MULTI FILTRATION NON DECREASING" << std::endl; + Stree_multi st; + + st.insert_simplex_and_subfaces({2, 1, 0}, {3., 4.}); + st.insert_simplex_and_subfaces({3, 0}, {2., 3.}); + st.insert_simplex_and_subfaces({3, 4, 5}, {3., 4.}); + + /* Inserted simplex: */ + /* 1 */ + /* o */ + /* /X\ */ + /* o---o---o---o */ + /* 2 0 3\X/4 */ + /* o */ + /* 5 */ + + + for (auto f_simplex : st.complex_simplex_range()) { + std::clog << "vertex = ("; + for (auto vertex : st.simplex_vertex_range(f_simplex)) { + std::clog << vertex << ","; + } + std::clog << ") - filtration = " << st.filtration(f_simplex) << std::endl; + } + auto filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({2., 3.})); + + st.set_number_of_parameters(2); + st.make_filtration_non_decreasing(); + + filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({3., 4.})); + + st.assign_filtration(st.find({3, 0}), Multi_Filtration_values({2.9, 4.})); + filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({2.9, 4.})); + + st.make_filtration_non_decreasing(); + + filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({3., 4.})); + + st.assign_filtration(st.find({3, 0}), Multi_Filtration_values({5., 3.99})); + filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({5., 3.99})); + + st.make_filtration_non_decreasing(); + + filt = st.filtration(st.find({3, 0})); + std::clog << "filtration([3,0]) = " << filt << std::endl; + BOOST_CHECK(filt == Multi_Filtration_values({3., 4.})); +} + +BOOST_AUTO_TEST_CASE(make_filtration_non_decreasing_on_multi_nan_values) { + Stree_multi st; + + BOOST_CHECK(std::numeric_limits::quiet_NaN().is_nan()); + BOOST_CHECK(std::numeric_limits::infinity().is_inf()); + + st.insert_simplex_and_subfaces({2, 1, 0}, {1.,2.,3.}); + st.insert_simplex_and_subfaces({3, 0}, {1.,2.,3.}); + st.insert_simplex_and_subfaces({3, 4, 5}, {1.,2.,3.}); + + st.assign_filtration(st.find({0}), std::numeric_limits::quiet_NaN()); + st.assign_filtration(st.find({3}), std::numeric_limits::infinity()); + + /* Inserted simplex: */ + /* 1 */ + /* o */ + /* /X\ */ + /* o---o---o---o */ + /* 2 0 3\X/4 */ + /* o */ + /* 5 */ + + // Default number of parameter is 2 + BOOST_CHECK(st.get_number_of_parameters() == 2); + + st.set_number_of_parameters(3); + BOOST_CHECK(st.get_number_of_parameters() == 3); + + std::clog << "SPECIFIC CASE:" << std::endl; + std::clog << "Insertion with NaN values does not ensure the filtration values are non decreasing" << std::endl; + st.make_filtration_non_decreasing(); + + std::clog << "Check that NaN filtrations are ignored, and inf filtrations are propagated." << std::endl; + for (auto f_simplex : st.complex_simplex_range()) { + auto filt = st.filtration(f_simplex); + bool contains3 = false; + bool contains0 = false; + int dim = -1; + std::clog << "Simplex "; + for (auto vertex : st.simplex_vertex_range(f_simplex)){ + std::clog << vertex << " "; + if (vertex == 3) contains3 = true; + else if (vertex == 0) contains0 = true; + dim++; + } + bool is_zero = dim == 0 && contains0; + std::clog << "Filtration: " << filt << std::endl; + if (is_zero) BOOST_CHECK(filt.is_nan()); + else if (contains3) BOOST_CHECK(filt.is_inf()); + else BOOST_CHECK(filt == Multi_Filtration_values({1.,2.,3.})); + } +} +