From cea821f9ca34c270a5ccc047342c2c21ae79a6c0 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Fri, 28 Aug 2020 17:42:12 +0200 Subject: A prototype to fix #364 --- src/python/gudhi/simplex_tree.pxd | 3 ++ src/python/gudhi/simplex_tree.pyx | 21 ++++++++++++++ src/python/include/Simplex_tree_interface.h | 12 ++++++++ src/python/test/test_simplex_tree.py | 44 +++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 75e94e0b..44533d7f 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -66,6 +66,9 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": vector[Simplex_tree_simplex_handle].const_iterator get_filtration_iterator_end() nogil Simplex_tree_skeleton_iterator get_skeleton_iterator_begin(int dimension) nogil Simplex_tree_skeleton_iterator get_skeleton_iterator_end(int dimension) nogil + # + ctypedef bool (*blocker_func)(vector[int], void *user_data) + void expansion_with_blockers_callback(int dimension, blocker_func user_func, void *user_data) cdef extern from "Persistent_cohomology_interface.h" namespace "Gudhi": cdef cppclass Simplex_tree_persistence_interface "Gudhi::Persistent_cohomology_interface>": diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index dfb1d985..e297cfbd 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -17,6 +17,9 @@ __author__ = "Vincent Rouvreau" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" +cdef bool callback(vector[int] simplex, void *blocker_func): + return (blocker_func)(simplex) + # SimplexTree python interface cdef class SimplexTree: """The simplex tree is an efficient and flexible data structure for @@ -409,6 +412,24 @@ cdef class SimplexTree: persistence_result = self.pcohptr.get_persistence() return self.get_ptr().compute_extended_persistence_subdiagrams(persistence_result, min_persistence) + def expansion_with_blocker(self, max_dim, blocker_func): + """Expands the Simplex_tree containing only its one skeleton + until dimension max_dim. + + The expanded simplicial complex until dimension :math:`d` + attached to a graph :math:`G` is the maximal simplicial complex of + dimension at most :math:`d` admitting the graph :math:`G` as + :math:`1`-skeleton. + The filtration value assigned to a simplex is the maximal filtration + value of one of its edges. + + The Simplex_tree must contain no simplex of dimension bigger than + 1 when calling the method. + + :param max_dim: The maximal dimension. + :type max_dim: int + """ + self.get_ptr().expansion_with_blockers_callback(max_dim, callback, blocker_func) def persistence(self, homology_coeff_field=11, min_persistence=0, persistence_dim_max = False): """This function computes and returns the persistence of the simplicial complex. diff --git a/src/python/include/Simplex_tree_interface.h b/src/python/include/Simplex_tree_interface.h index e288a8cf..9f92b349 100644 --- a/src/python/include/Simplex_tree_interface.h +++ b/src/python/include/Simplex_tree_interface.h @@ -39,6 +39,7 @@ class Simplex_tree_interface : public Simplex_tree { using Skeleton_simplex_iterator = typename Base::Skeleton_simplex_iterator; using Complex_simplex_iterator = typename Base::Complex_simplex_iterator; using Extended_filtration_data = typename Base::Extended_filtration_data; + typedef bool (*blocker_func)(Simplex simplex, void *user_data); public: @@ -188,6 +189,17 @@ class Simplex_tree_interface : public Simplex_tree { return collapsed_stree_ptr; } + void expansion_with_blockers_callback(int dimension, blocker_func user_func, void *user_data) { + Base::expansion_with_blockers(dimension, [&](Simplex_handle sh){ + Simplex simplex; + for (auto vertex : Base::simplex_vertex_range(sh)) { + simplex.insert(simplex.begin(), vertex); + } + return user_func(simplex, user_data); + }); + Base::clear_filtration(); + } + // Iterator over the simplex tree Complex_simplex_iterator get_simplices_iterator_begin() { // this specific case works because the range is just a pair of iterators - won't work if range was a vector diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 83be0602..7aad8259 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -358,3 +358,47 @@ def test_collapse_edges(): assert st.find([1, 3]) == False for simplex in st.get_skeleton(0): assert simplex[1] == 1. + +def blocker(simplex): + try: + # Block all simplices that countains vertex 6 + simplex.index(6) + print(simplex, ' is blocked') + return True + except ValueError: + print(simplex, ' is accepted') + return False + +def test_expansion_with_blocker(): + st=SimplexTree() + st.insert([0,1],0) + st.insert([0,2],1) + st.insert([0,3],2) + st.insert([1,2],3) + st.insert([1,3],4) + st.insert([2,3],5) + st.insert([2,4],6) + st.insert([3,6],7) + st.insert([4,5],8) + st.insert([4,6],9) + st.insert([5,6],10) + st.insert([6],10) + + st.expansion_with_blocker(2, blocker) + assert st.num_simplices() == 22 + assert st.dimension() == 2 + assert st.find([4,5,6]) == False + assert st.filtration([0,1,2]) == 3. + assert st.filtration([0,1,3]) == 4. + assert st.filtration([0,2,3]) == 5. + assert st.filtration([1,2,3]) == 5. + + st.expansion_with_blocker(3, blocker) + assert st.num_simplices() == 23 + assert st.dimension() == 3 + assert st.find([4,5,6]) == False + assert st.filtration([0,1,2]) == 3. + assert st.filtration([0,1,3]) == 4. + assert st.filtration([0,2,3]) == 5. + assert st.filtration([1,2,3]) == 5. + assert st.filtration([0,1,2,3]) == 5. -- cgit v1.2.3 From c2eb0484191f89fcbe40bc4ab04943eb808f12a9 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 1 Sep 2020 14:51:25 +0200 Subject: expansion with blocker and how to modify filtration value --- src/python/gudhi/simplex_tree.pxd | 2 +- src/python/gudhi/simplex_tree.pyx | 23 +++++++++--------- src/python/test/test_simplex_tree.py | 46 +++++++++++++++++++++--------------- 3 files changed, 40 insertions(+), 31 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 44533d7f..80c6ffca 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -66,7 +66,7 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": vector[Simplex_tree_simplex_handle].const_iterator get_filtration_iterator_end() nogil Simplex_tree_skeleton_iterator get_skeleton_iterator_begin(int dimension) nogil Simplex_tree_skeleton_iterator get_skeleton_iterator_end(int dimension) nogil - # + # Expansion with blockers ctypedef bool (*blocker_func)(vector[int], void *user_data) void expansion_with_blockers_callback(int dimension, blocker_func user_func, void *user_data) diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index e297cfbd..debe92c0 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -413,21 +413,22 @@ cdef class SimplexTree: return self.get_ptr().compute_extended_persistence_subdiagrams(persistence_result, min_persistence) def expansion_with_blocker(self, max_dim, blocker_func): - """Expands the Simplex_tree containing only its one skeleton - until dimension max_dim. + """Expands the Simplex_tree containing only a graph. Simplices corresponding to cliques in the graph are added + incrementally, faces before cofaces, unless the simplex has dimension larger than `max_dim` or `blocker_func` + returns `True` for this simplex. - The expanded simplicial complex until dimension :math:`d` - attached to a graph :math:`G` is the maximal simplicial complex of - dimension at most :math:`d` admitting the graph :math:`G` as - :math:`1`-skeleton. - The filtration value assigned to a simplex is the maximal filtration - value of one of its edges. + The function identifies a candidate simplex whose faces are all already in the complex, inserts it with a + filtration value corresponding to the maximum of the filtration values of the faces, then calls `blocker_func` + with this new simplex (represented as a list of int). If `blocker_func` returns `True`, the simplex is removed, + otherwise it is kept. The algorithm then proceeds with the next candidate. - The Simplex_tree must contain no simplex of dimension bigger than - 1 when calling the method. + Note that you cannot update the filtration value of the simplex during the evaluation of `blocker_func`, as it + would segfault. - :param max_dim: The maximal dimension. + :param max_dim: Expansion maximal dimension value. :type max_dim: int + :param blocker_func: Blocker oracle. + :type blocker_func: Its concept is `Boolean blocker_func(list of int)` """ self.get_ptr().expansion_with_blockers_callback(max_dim, callback, blocker_func) diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 7aad8259..33b0ac99 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -359,16 +359,6 @@ def test_collapse_edges(): for simplex in st.get_skeleton(0): assert simplex[1] == 1. -def blocker(simplex): - try: - # Block all simplices that countains vertex 6 - simplex.index(6) - print(simplex, ' is blocked') - return True - except ValueError: - print(simplex, ' is accepted') - return False - def test_expansion_with_blocker(): st=SimplexTree() st.insert([0,1],0) @@ -384,21 +374,39 @@ def test_expansion_with_blocker(): st.insert([5,6],10) st.insert([6],10) + # One cannot modify filtration inside blocker - segfault - Let's record accepted simplices + accepted_simp = [] + def blocker(simplex): + try: + # Block all simplices that countains vertex 6 + simplex.index(6) + print(simplex, ' is blocked') + return True + except ValueError: + print(simplex, ' is accepted') + accepted_simp.append(simplex) + return False + st.expansion_with_blocker(2, blocker) + for simplex in accepted_simp: + st.assign_filtration(simplex, st.filtration(simplex) + 1.) assert st.num_simplices() == 22 assert st.dimension() == 2 assert st.find([4,5,6]) == False - assert st.filtration([0,1,2]) == 3. - assert st.filtration([0,1,3]) == 4. - assert st.filtration([0,2,3]) == 5. - assert st.filtration([1,2,3]) == 5. + assert st.filtration([0,1,2]) == 4. + assert st.filtration([0,1,3]) == 5. + assert st.filtration([0,2,3]) == 6. + assert st.filtration([1,2,3]) == 6. + accepted_simp = [] st.expansion_with_blocker(3, blocker) + for simplex in accepted_simp: + st.assign_filtration(simplex, st.filtration(simplex) + 1.) assert st.num_simplices() == 23 assert st.dimension() == 3 assert st.find([4,5,6]) == False - assert st.filtration([0,1,2]) == 3. - assert st.filtration([0,1,3]) == 4. - assert st.filtration([0,2,3]) == 5. - assert st.filtration([1,2,3]) == 5. - assert st.filtration([0,1,2,3]) == 5. + assert st.filtration([0,1,2]) == 4. + assert st.filtration([0,1,3]) == 5. + assert st.filtration([0,2,3]) == 6. + assert st.filtration([1,2,3]) == 6. + assert st.filtration([0,1,2,3]) == 6. -- cgit v1.2.3 From 953a7da68657b9ab493cfb35b41fc46e26a73876 Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 25 Mar 2021 08:52:50 +0100 Subject: Add equality operator for python Simplex tree --- src/python/gudhi/simplex_tree.pxd | 1 + src/python/gudhi/simplex_tree.pyx | 7 +++++++ src/python/test/test_simplex_tree.py | 12 ++++++++++++ 3 files changed, 20 insertions(+) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 000323af..3df614dd 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -65,6 +65,7 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": vector[vector[pair[int, pair[double, double]]]] compute_extended_persistence_subdiagrams(vector[pair[int, pair[double, double]]] dgm, double min_persistence) nogil Simplex_tree_interface_full_featured* collapse_edges(int nb_collapse_iteration) nogil except + void reset_filtration(double filtration, int dimension) nogil + bint operator==(Simplex_tree_interface_full_featured) nogil # Iterators over Simplex tree pair[vector[int], double] get_simplex_and_filtration(Simplex_tree_simplex_handle f_simplex) nogil Simplex_tree_simplices_iterator get_simplices_iterator_begin() nogil diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index d7991417..0bfaa19f 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -639,3 +639,10 @@ cdef class SimplexTree: self.thisptr = (ptr.collapse_edges(nb_iter)) # Delete old pointer del ptr + + def __eq__(self, other:SimplexTree): + """Simplex tree equality operator using C++ depth first search operator== + :returns: True if the 2 simplex trees are equal, False otherwise. + :rtype: bool + """ + return dereference(self.get_ptr()) == dereference(other.get_ptr()) \ No newline at end of file diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index a3eacaa9..92b909ca 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -404,3 +404,15 @@ def test_boundaries_iterator(): with pytest.raises(RuntimeError): list(st.get_boundaries([6])) # (6) does not exist + +def test_equality_operator(): + st1 = SimplexTree() + st2 = SimplexTree() + + assert st1 == st2 + + st1.insert([1,2,3], 4.) + assert st1 != st2 + + st2.insert([1,2,3], 4.) + assert st1 == st2 \ No newline at end of file -- cgit v1.2.3 From a6e7f96f7d2c391e4548309174cc05f5ae05d871 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 15 Apr 2021 22:48:10 +0200 Subject: Update src/python/gudhi/simplex_tree.pyx Co-authored-by: Marc Glisse --- src/python/gudhi/simplex_tree.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 0bfaa19f..67428401 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -641,8 +641,8 @@ cdef class SimplexTree: del ptr def __eq__(self, other:SimplexTree): - """Simplex tree equality operator using C++ depth first search operator== + """Test for structural equality :returns: True if the 2 simplex trees are equal, False otherwise. :rtype: bool """ - return dereference(self.get_ptr()) == dereference(other.get_ptr()) \ No newline at end of file + return dereference(self.get_ptr()) == dereference(other.get_ptr()) -- cgit v1.2.3 From f4f5992e9025d055e35c86589923f1b0299f552e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Thu, 22 Apr 2021 08:39:03 +0200 Subject: code review: rename block_func when it is a type. Type is a 'callable' --- src/python/gudhi/simplex_tree.pxd | 4 ++-- src/python/gudhi/simplex_tree.pyx | 2 +- src/python/include/Simplex_tree_interface.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 80c6ffca..ff750af9 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -67,8 +67,8 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": Simplex_tree_skeleton_iterator get_skeleton_iterator_begin(int dimension) nogil Simplex_tree_skeleton_iterator get_skeleton_iterator_end(int dimension) nogil # Expansion with blockers - ctypedef bool (*blocker_func)(vector[int], void *user_data) - void expansion_with_blockers_callback(int dimension, blocker_func user_func, void *user_data) + ctypedef bool (*blocker_func_t)(vector[int], void *user_data) + void expansion_with_blockers_callback(int dimension, blocker_func_t user_func, void *user_data) cdef extern from "Persistent_cohomology_interface.h" namespace "Gudhi": cdef cppclass Simplex_tree_persistence_interface "Gudhi::Persistent_cohomology_interface>": diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index debe92c0..383d1949 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -428,7 +428,7 @@ cdef class SimplexTree: :param max_dim: Expansion maximal dimension value. :type max_dim: int :param blocker_func: Blocker oracle. - :type blocker_func: Its concept is `Boolean blocker_func(list of int)` + :type blocker_func: Callable[[List[int]], bool] """ self.get_ptr().expansion_with_blockers_callback(max_dim, callback, blocker_func) diff --git a/src/python/include/Simplex_tree_interface.h b/src/python/include/Simplex_tree_interface.h index 9f92b349..a859bec0 100644 --- a/src/python/include/Simplex_tree_interface.h +++ b/src/python/include/Simplex_tree_interface.h @@ -39,7 +39,7 @@ class Simplex_tree_interface : public Simplex_tree { using Skeleton_simplex_iterator = typename Base::Skeleton_simplex_iterator; using Complex_simplex_iterator = typename Base::Complex_simplex_iterator; using Extended_filtration_data = typename Base::Extended_filtration_data; - typedef bool (*blocker_func)(Simplex simplex, void *user_data); + typedef bool (*blocker_func_t)(Simplex simplex, void *user_data); public: @@ -189,7 +189,7 @@ class Simplex_tree_interface : public Simplex_tree { return collapsed_stree_ptr; } - void expansion_with_blockers_callback(int dimension, blocker_func user_func, void *user_data) { + void expansion_with_blockers_callback(int dimension, blocker_func_t user_func, void *user_data) { Base::expansion_with_blockers(dimension, [&](Simplex_handle sh){ Simplex simplex; for (auto vertex : Base::simplex_vertex_range(sh)) { -- cgit v1.2.3 From 69168e8ed24165ab89ea1c57bc21dd994c93dd8e Mon Sep 17 00:00:00 2001 From: ROUVREAU Vincent Date: Tue, 27 Apr 2021 09:03:47 +0200 Subject: vector initialization improvement --- src/python/include/Simplex_tree_interface.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/python') diff --git a/src/python/include/Simplex_tree_interface.h b/src/python/include/Simplex_tree_interface.h index a859bec0..dd8d715c 100644 --- a/src/python/include/Simplex_tree_interface.h +++ b/src/python/include/Simplex_tree_interface.h @@ -191,13 +191,9 @@ class Simplex_tree_interface : public Simplex_tree { void expansion_with_blockers_callback(int dimension, blocker_func_t user_func, void *user_data) { Base::expansion_with_blockers(dimension, [&](Simplex_handle sh){ - Simplex simplex; - for (auto vertex : Base::simplex_vertex_range(sh)) { - simplex.insert(simplex.begin(), vertex); - } + Simplex simplex(Base::simplex_vertex_range(sh).begin(), Base::simplex_vertex_range(sh).end()); return user_func(simplex, user_data); }); - Base::clear_filtration(); } // Iterator over the simplex tree -- cgit v1.2.3 From f339fb0ef1dbe74cdf4912dc256ec3648308ec2b Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 6 Dec 2021 15:29:37 +0100 Subject: Modify minimal requirements for python typing. Fix no cgal compilation --- .github/next_release.md | 3 + .../modules/GUDHI_third_party_libraries.cmake | 7 +- src/python/CMakeLists.txt | 1 + src/python/doc/installation.rst | 93 +++++++++++----------- src/python/pyproject.toml | 2 +- src/python/setup.py.in | 4 +- 6 files changed, 60 insertions(+), 50 deletions(-) (limited to 'src/python') diff --git a/.github/next_release.md b/.github/next_release.md index 170c7494..0d4673bd 100644 --- a/.github/next_release.md +++ b/.github/next_release.md @@ -9,6 +9,9 @@ Below is a list of changes made since GUDHI 3.4.0: - [Coxeter triangulation](https://gudhi.inria.fr/doc/latest/group__coxeter__triangulation.html) - constructs a piecewise-linear approximation of an m-dimensional smooth manifold embedded in R^d using an ambient triangulation. +- [Python installation](link) + - Python >= 3.5 and cython >= 0.27 are now required for Python typing purpose. + - [Module](link) - ... diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 023061f1..deaea588 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -6,6 +6,10 @@ find_package(Boost 1.56.0 QUIET OPTIONAL_COMPONENTS filesystem unit_test_framewo if(NOT Boost_VERSION) message(FATAL_ERROR "NOTICE: This program requires Boost and will not be compiled.") endif(NOT Boost_VERSION) +INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) +message(STATUS "boost include dirs:" ${Boost_INCLUDE_DIRS}) +message(STATUS "boost library dirs:" ${Boost_LIBRARY_DIRS}) + find_package(GMP) if(GMP_FOUND) @@ -89,9 +93,6 @@ add_definitions( -DBOOST_ALL_DYN_LINK ) # problem on Mac with boost_system and boost_thread add_definitions( -DBOOST_SYSTEM_NO_DEPRECATED ) -message(STATUS "boost include dirs:" ${Boost_INCLUDE_DIRS}) -message(STATUS "boost library dirs:" ${Boost_LIBRARY_DIRS}) - # Find the correct Python interpreter. # Can be set with -DPYTHON_EXECUTABLE=/usr/bin/python3 or -DPython_ADDITIONAL_VERSIONS=3 for instance. find_package( PythonInterp ) diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 4a017251..12534b86 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -175,6 +175,7 @@ if(PYTHONINTERP_FOUND) set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}'euclidean_strong_witness_complex', ") endif () + add_gudhi_debug_info("Boost version ${Boost_VERSION}") if(CGAL_FOUND) # Add CGAL compilation args if(CGAL_HEADER_ONLY) diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 35c344e3..6ed53960 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -33,25 +33,19 @@ Compiling These instructions are for people who want to compile gudhi from source, they are unnecessary if you installed a binary package of Gudhi as above. They assume that you have downloaded a `release `_, -with a name like `gudhi.3.2.0.tar.gz`, then run `tar xf gudhi.3.2.0.tar.gz`, which -created a directory `gudhi.3.2.0`, hereinafter referred to as `/path-to-gudhi/`. +with a name like `gudhi.3.X.X.tar.gz`, then run `tar xf gudhi.3.X.X.tar.gz`, which +created a directory `gudhi.3.X.X`, hereinafter referred to as `/path-to-gudhi/`. If you are instead using a git checkout, beware that the paths are a bit different, and in particular the `python/` subdirectory is actually `src/python/` there. The library uses c++14 and requires `Boost `_ :math:`\geq` 1.56.0, `CMake `_ :math:`\geq` 3.5 to generate makefiles, -`NumPy `_ :math:`\geq` 1.15.0, `Cython `_ and -`pybind11 `_ to compile -the GUDHI Python module. -It is a multi-platform library and compiles on Linux, Mac OSX and Visual -Studio 2017 or later. +Python :math:`\geq` 3.5, `NumPy `_ :math:`\geq` 1.15.0, `Cython `_ +:math:`\geq` 0.27 and `pybind11 `_ to compile the GUDHI Python module. +It is a multi-platform library and compiles on Linux, Mac OSX and Visual Studio 2017 or later. -On `Windows `_ , only Python -:math:`\geq` 3.5 are available because of the required Visual Studio version. - -On other systems, if you have several Python/python installed, the version 2.X -will be used by default, but you can force it by adding +If you have several Python/python installed, the version 2.X may be used by default, but you can force it by adding :code:`-DPython_ADDITIONAL_VERSIONS=3` to the cmake command. GUDHI Python module compilation @@ -143,53 +137,62 @@ If :code:`import gudhi` succeeds, please have a look to debug information: .. code-block:: python import gudhi - print(gudhi.__debug_info__) +print(gudhi.__debug_info__) +print("+ Installed modules are: " + gudhi.__available_modules) +print("+ Missing modules are: " + gudhi.__missing_modules) You shall have something like: .. code-block:: none - Python version 2.7.15 - Cython version 0.26.1 - Numpy version 1.14.1 - Eigen3 version 3.1.1 - Installed modules are: off_reader;simplex_tree;rips_complex; - cubical_complex;periodic_cubical_complex;reader_utils;witness_complex; - strong_witness_complex;alpha_complex; - Missing modules are: bottleneck_distance;nerve_gic;subsampling; - tangential_complex;persistence_graphical_tools; - euclidean_witness_complex;euclidean_strong_witness_complex; - CGAL version 4.7.1000 - GMP_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmp.so - GMPXX_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmpxx.so - TBB version 9107 found and used + Pybind11 version 2.8.1 + Python version 3.7.12 + Cython version 0.29.25 + Numpy version 1.21.4 + Boost version 1.77.0 + print("+ Installed modules are: " + gudhi.__available_modules) + + Installed modules are: off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; + persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex; + + Missing modules are: bottleneck;nerve_gic;subsampling;tangential_complex;alpha_complex;euclidean_witness_complex; + euclidean_strong_witness_complex; -Here, you can see that bottleneck_distance, nerve_gic, subsampling and -tangential_complex are missing because of the CGAL version. -persistence_graphical_tools is not available as matplotlib is not -available. +Here, you can see that are requiring CGAL are missing because CGAL is not installed. +:code:`persistence_graphical_tools` is installed, but +`its functions `_ will produce an error as +matplotlib is not available. Unitary tests cannot be run as pytest is missing. A complete configuration would be : .. code-block:: none - Python version 3.6.5 - Cython version 0.28.2 - Pytest version 3.3.2 - Matplotlib version 2.2.2 - Numpy version 1.14.5 - Eigen3 version 3.3.4 - Installed modules are: off_reader;simplex_tree;rips_complex; - cubical_complex;periodic_cubical_complex;persistence_graphical_tools; - reader_utils;witness_complex;strong_witness_complex; - persistence_graphical_tools;bottleneck_distance;nerve_gic;subsampling; - tangential_complex;alpha_complex;euclidean_witness_complex; - euclidean_strong_witness_complex; - CGAL header only version 4.11.0 + Pybind11 version 2.8.1 + Python version 3.9.7 + Cython version 0.29.24 + Pytest version 6.2.5 + Matplotlib version 3.5.0 + Numpy version 1.21.4 + Scipy version 1.7.3 + Scikit-learn version 1.0.1 + POT version 0.8.0 + HNSWlib found + PyKeOps version [pyKeOps]: 1.5 + EagerPy version 0.30.0 + TensorFlow version 2.7.0 + Sphinx version 4.3.0 + Sphinx-paramlinks version 0.5.2 + python_docs_theme found + Eigen3 version 3.4.0 + Boost version 1.74.0 + CGAL version 5.3 GMP_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmp.so GMPXX_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmpxx.so - TBB version 9107 found and used + MPFR_LIBRARIES = /usr/lib/x86_64-linux-gnu/libmpfr.so + + Installed modules are: bottleneck;off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; + persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex;nerve_gic;subsampling; + tangential_complex;alpha_complex;euclidean_witness_complex;euclidean_strong_witness_complex; + + Missing modules are: + Documentation ============= diff --git a/src/python/pyproject.toml b/src/python/pyproject.toml index a9fb4985..55b64466 100644 --- a/src/python/pyproject.toml +++ b/src/python/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "wheel", "numpy>=1.15.0", "cython", "pybind11"] +requires = ["setuptools>=24.2.0", "wheel", "numpy>=1.15.0", "cython>=0.27", "pybind11"] build-backend = "setuptools.build_meta" diff --git a/src/python/setup.py.in b/src/python/setup.py.in index 23746998..2c67c2c5 100644 --- a/src/python/setup.py.in +++ b/src/python/setup.py.in @@ -5,6 +5,7 @@ Copyright (C) 2019 Inria Modification(s): + - 2021/12 Vincent Rouvreau: Python 3.5 as minimal version - YYYY/MM Author: Description of the modification """ @@ -43,7 +44,7 @@ for module in cython_modules: include_dirs=include_dirs, runtime_library_dirs=runtime_library_dirs,)) -ext_modules = cythonize(ext_modules, compiler_directives={'language_level': str(sys.version_info[0])}) +ext_modules = cythonize(ext_modules, compiler_directives={'language_level': '3'}) for module in pybind11_modules: my_include_dirs = include_dirs + [pybind11.get_include(False), pybind11.get_include(True)] @@ -86,6 +87,7 @@ setup( long_description_content_type='text/x-rst', long_description=long_description, ext_modules = ext_modules, + python_requires='>=3.5.0', install_requires = ['numpy >= 1.15.0',], package_data={"": ["*.dll"], }, ) -- cgit v1.2.3 From 54f6f178d30f59a35758421e285cfac946927064 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 6 Dec 2021 17:36:03 +0100 Subject: Doc review: rephrase --- .github/next_release.md | 2 +- src/python/doc/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/python') diff --git a/.github/next_release.md b/.github/next_release.md index 0d4673bd..5a1215df 100644 --- a/.github/next_release.md +++ b/.github/next_release.md @@ -10,7 +10,7 @@ Below is a list of changes made since GUDHI 3.4.0: - constructs a piecewise-linear approximation of an m-dimensional smooth manifold embedded in R^d using an ambient triangulation. - [Python installation](link) - - Python >= 3.5 and cython >= 0.27 are now required for Python typing purpose. + - Python >= 3.5 and cython >= 0.27 are now required. - [Module](link) - ... diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 6ed53960..e8321fed 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -156,7 +156,7 @@ You shall have something like: + Missing modules are: bottleneck;nerve_gic;subsampling;tangential_complex;alpha_complex;euclidean_witness_complex; euclidean_strong_witness_complex; -Here, you can see that are requiring CGAL are missing because CGAL is not installed. +Here, you can see that the modules that need CGAL are missing, because CGAL is not installed. :code:`persistence_graphical_tools` is installed, but `its functions `_ will produce an error as matplotlib is not available. -- cgit v1.2.3 From f221db3627c3f71a76f57c3f7b5308b00f1d4248 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Fri, 21 Jan 2022 11:26:46 +0100 Subject: [skip ci] Code review: restore TBB version for the complete configuration example --- src/python/doc/installation.rst | 1 + 1 file changed, 1 insertion(+) (limited to 'src/python') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index e8321fed..8fca94f5 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -188,6 +188,7 @@ A complete configuration would be : GMP_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmp.so GMPXX_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmpxx.so MPFR_LIBRARIES = /usr/lib/x86_64-linux-gnu/libmpfr.so + TBB version 9107 found and used + Installed modules are: bottleneck;off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex;nerve_gic;subsampling; tangential_complex;alpha_complex;euclidean_witness_complex;euclidean_strong_witness_complex; -- cgit v1.2.3 From 6b6a2ca097e38ca8d1601e764c8f3a6eecfd19e7 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 24 Jan 2022 10:46:27 +0100 Subject: doc review: remove unwanted line. Use 'import gudhi as gd' --- src/python/doc/installation.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/python') diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 8fca94f5..873f7870 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -33,8 +33,8 @@ Compiling These instructions are for people who want to compile gudhi from source, they are unnecessary if you installed a binary package of Gudhi as above. They assume that you have downloaded a `release `_, -with a name like `gudhi.3.X.X.tar.gz`, then run `tar xf gudhi.3.X.X.tar.gz`, which -created a directory `gudhi.3.X.X`, hereinafter referred to as `/path-to-gudhi/`. +with a name like `gudhi.3.X.Y.tar.gz`, then run `tar xf gudhi.3.X.Y.tar.gz`, which +created a directory `gudhi.3.X.Y`, hereinafter referred to as `/path-to-gudhi/`. If you are instead using a git checkout, beware that the paths are a bit different, and in particular the `python/` subdirectory is actually `src/python/` there. @@ -136,10 +136,10 @@ If :code:`import gudhi` succeeds, please have a look to debug information: .. code-block:: python - import gudhi -print(gudhi.__debug_info__) -print("+ Installed modules are: " + gudhi.__available_modules) -print("+ Missing modules are: " + gudhi.__missing_modules) + import gudhi as gd + print(gd.__debug_info__) + print("+ Installed modules are: " + gd.__available_modules) + print("+ Missing modules are: " + gd.__missing_modules) You shall have something like: @@ -150,7 +150,6 @@ You shall have something like: Cython version 0.29.25 Numpy version 1.21.4 Boost version 1.77.0 - print("+ Installed modules are: " + gudhi.__available_modules) + Installed modules are: off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex; + Missing modules are: bottleneck;nerve_gic;subsampling;tangential_complex;alpha_complex;euclidean_witness_complex; @@ -349,8 +348,8 @@ You can still deactivate LaTeX rendering by saying: .. code-block:: python - import gudhi - gudhi.persistence_graphical_tools._gudhi_matplotlib_use_tex=False + import gudhi as gd + gd.persistence_graphical_tools._gudhi_matplotlib_use_tex=False PyKeOps ------- -- cgit v1.2.3 From 4fc331267735a3368160ea7cd001b7fa45d720f7 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Thu, 10 Feb 2022 10:32:18 +0100 Subject: Boost 1.66.0 is now the minimal version as required from cgal 5.1 - Also remove useless include boost/version.h --- .github/next_release.md | 3 +++ src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 1 - src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h | 1 - src/Subsampling/include/gudhi/sparsify_point_set.h | 5 ----- src/cmake/modules/GUDHI_third_party_libraries.cmake | 2 +- src/common/doc/installation.h | 2 +- src/common/include/gudhi/reader_utils.h | 4 ---- src/python/doc/installation.rst | 2 +- 8 files changed, 6 insertions(+), 14 deletions(-) (limited to 'src/python') diff --git a/.github/next_release.md b/.github/next_release.md index 65a92816..e21b25c7 100644 --- a/.github/next_release.md +++ b/.github/next_release.md @@ -13,6 +13,9 @@ Below is a list of changes made since GUDHI 3.5.0: - [Representations](https://gudhi.inria.fr/python/latest/representations.html#gudhi.representations.vector_methods.BettiCurve) - A more flexible Betti curve class capable of computing exact curves +- Installation + - Boost ≥ 1.66.0 is now required (was ≥ 1.56.0). + - [Module](link) - ... diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index ccc3d852..df5c630e 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -12,7 +12,6 @@ #ifndef ALPHA_COMPLEX_3D_H_ #define ALPHA_COMPLEX_3D_H_ -#include #include #include #include diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h index ee64a277..e5522cc7 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/Simplex_tree_iterators.h @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/src/Subsampling/include/gudhi/sparsify_point_set.h b/src/Subsampling/include/gudhi/sparsify_point_set.h index 4571b8f3..b325fe3c 100644 --- a/src/Subsampling/include/gudhi/sparsify_point_set.h +++ b/src/Subsampling/include/gudhi/sparsify_point_set.h @@ -11,12 +11,7 @@ #ifndef SPARSIFY_POINT_SET_H_ #define SPARSIFY_POINT_SET_H_ -#include -#if BOOST_VERSION < 106600 -# include -#else # include -#endif #include #ifdef GUDHI_SUBSAMPLING_PROFILING diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index b316740d..68ffaee1 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -1,6 +1,6 @@ # This files manage third party libraries required by GUDHI -find_package(Boost 1.56.0 QUIET OPTIONAL_COMPONENTS filesystem unit_test_framework program_options) +find_package(Boost 1.66.0 QUIET OPTIONAL_COMPONENTS filesystem unit_test_framework program_options) # Boost_FOUND is not reliable if(NOT Boost_VERSION) diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h index ef668dfb..67d026bd 100644 --- a/src/common/doc/installation.h +++ b/src/common/doc/installation.h @@ -5,7 +5,7 @@ * Examples of GUDHI headers inclusion can be found in \ref utilities. * * \section compiling Compiling - * The library uses c++14 and requires Boost ≥ 1.56.0 + * The library uses c++14 and requires Boost ≥ 1.66.0 * and CMake ≥ 3.5. * It is a multi-platform library and compiles on Linux, Mac OSX and Visual Studio 2015. * diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h index a1b104e2..29d5423d 100644 --- a/src/common/include/gudhi/reader_utils.h +++ b/src/common/include/gudhi/reader_utils.h @@ -14,11 +14,7 @@ #include #include -#if BOOST_VERSION < 106600 -# include -#else # include -#endif #include #include diff --git a/src/python/doc/installation.rst b/src/python/doc/installation.rst index 35c344e3..c2c3db34 100644 --- a/src/python/doc/installation.rst +++ b/src/python/doc/installation.rst @@ -39,7 +39,7 @@ If you are instead using a git checkout, beware that the paths are a bit different, and in particular the `python/` subdirectory is actually `src/python/` there. -The library uses c++14 and requires `Boost `_ :math:`\geq` 1.56.0, +The library uses c++14 and requires `Boost `_ :math:`\geq` 1.66.0, `CMake `_ :math:`\geq` 3.5 to generate makefiles, `NumPy `_ :math:`\geq` 1.15.0, `Cython `_ and `pybind11 `_ to compile -- cgit v1.2.3 From 37a141533397568e7070c734e21ef9c4dc85d132 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Thu, 10 Feb 2022 16:16:14 +0100 Subject: Add SimplexTree copy method and its test --- src/python/gudhi/simplex_tree.pxd | 1 + src/python/gudhi/simplex_tree.pyx | 14 ++++++++++++++ src/python/test/test_simplex_tree.py | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 006a24ed..92139db4 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -45,6 +45,7 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": cdef cppclass Simplex_tree_interface_full_featured "Gudhi::Simplex_tree_interface": Simplex_tree_interface_full_featured() nogil + Simplex_tree_interface_full_featured(Simplex_tree_interface_full_featured&) nogil double simplex_filtration(vector[int] simplex) nogil void assign_simplex_filtration(vector[int] simplex, double filtration) nogil void initialize_filtration() nogil diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index c3720936..6b3116a4 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -63,6 +63,20 @@ cdef class SimplexTree: """ return self.pcohptr != NULL + def copy(self): + """ + :returns: A simplex tree that is a deep copy itself. + :rtype: SimplexTree + """ + stree = SimplexTree() + cdef Simplex_tree_interface_full_featured* stree_ptr + cdef Simplex_tree_interface_full_featured* self_ptr=self.get_ptr() + with nogil: + stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) + + stree.thisptr = (stree_ptr) + return stree + def filtration(self, simplex): """This function returns the filtration value for a given N-simplex in this simplicial complex, or +infinity if it is not in the complex. diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 31c46213..dac45288 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -447,4 +447,21 @@ def test_persistence_intervals_in_dimension(): assert np.array_equal(H2, np.array([[ 0., float("inf")]])) # Test empty case assert st.persistence_intervals_in_dimension(3).shape == (0, 2) - \ No newline at end of file + +def test_simplex_tree_copy(): + st = SimplexTree() + st .insert([1,2,3], 0.) + a = st.copy() + # TODO(VR): when #463 is merged, replace with + # assert a == st + assert a.num_vertices() == st.num_vertices() + assert a.num_simplices() == st.num_simplices() + st_filt_list = list(st.get_filtration()) + assert list(a.get_filtration()) == st_filt_list + + a.remove_maximal_simplex([1, 2, 3]) + a_filt_list = list(a.get_filtration()) + assert len(a_filt_list) < len(st_filt_list) + + for a_splx in a_filt_list: + assert a_splx in st_filt_list -- cgit v1.2.3 From 1eae93c4f6c5fa401cf03dce0bcbc70e72d4f46f Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Fri, 11 Feb 2022 14:22:55 +0100 Subject: Debug traces for python module --- src/python/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/python') diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 8eb7478e..777fae72 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -264,6 +264,10 @@ if(PYTHONINTERP_FOUND) set(GUDHI_PYTHON_INCLUDE_DIRS "${GUDHI_PYTHON_INCLUDE_DIRS}'${TBB_INCLUDE_DIRS}', ") endif() + if(DEBUG_TRACES) + set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DDEBUG_TRACES', ") + endif(DEBUG_TRACES) + if(UNIX AND WITH_GUDHI_PYTHON_RUNTIME_LIBRARY_DIRS) set( GUDHI_PYTHON_RUNTIME_LIBRARY_DIRS "${GUDHI_PYTHON_LIBRARY_DIRS}") endif(UNIX AND WITH_GUDHI_PYTHON_RUNTIME_LIBRARY_DIRS) -- cgit v1.2.3 From fb8ce008feadcaf6a936740a3ed54d50970c731c Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Fri, 11 Feb 2022 23:11:26 +0100 Subject: __copy__, __deepcopy__, copy, and copy ctors. Still pb with the doc --- .github/next_release.md | 3 ++ src/python/gudhi/simplex_tree.pyx | 54 +++++++++++++++---- src/python/test/test_simplex_tree.py | 102 +++++++++++++++++++++++++++++++---- 3 files changed, 140 insertions(+), 19 deletions(-) (limited to 'src/python') diff --git a/.github/next_release.md b/.github/next_release.md index e21b25c7..3946404b 100644 --- a/.github/next_release.md +++ b/.github/next_release.md @@ -13,6 +13,9 @@ Below is a list of changes made since GUDHI 3.5.0: - [Representations](https://gudhi.inria.fr/python/latest/representations.html#gudhi.representations.vector_methods.BettiCurve) - A more flexible Betti curve class capable of computing exact curves +- [Simplex tree](https://gudhi.inria.fr/python/latest/simplex_tree_ref.html) + - `__copy__`, `__deepcopy__`, `copy` and copy constructors + - Installation - Boost ≥ 1.66.0 is now required (was ≥ 1.56.0). diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 6b3116a4..ed7c3b92 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -30,6 +30,7 @@ cdef class SimplexTree: # unfortunately 'cdef public Simplex_tree_interface_full_featured* thisptr' is not possible # Use intptr_t instead to cast the pointer cdef public intptr_t thisptr + cdef bool __thisptr_to_be_deleted # Get the pointer casted as it should be cdef Simplex_tree_interface_full_featured* get_ptr(self) nogil: @@ -38,17 +39,36 @@ cdef class SimplexTree: cdef Simplex_tree_persistence_interface * pcohptr # Fake constructor that does nothing but documenting the constructor - def __init__(self): + def __init__(self, other = None, copy = True): """SimplexTree constructor. + :param other: If `other` is a SimplexTree (default = None), the SimplexTree is constructed from a deep/shallow copy of `other`. + :type other: SimplexTree + :param copy: If `True`, the copy will be deep and if `False, the copy will be shallow. Default is `True`. + :type copy: bool + :returns: A simplex tree that is a (deep or shallow) copy of itself. + :rtype: SimplexTree + :note: copy constructor requires :func:`compute_persistence` to be launched again as the result is not copied. """ # The real cython constructor - def __cinit__(self): - self.thisptr = (new Simplex_tree_interface_full_featured()) + def __cinit__(self, other = None, copy = True): + cdef SimplexTree ostr + if other and type(other) is SimplexTree: + ostr = other + if copy: + self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) + else: + self.thisptr = ostr.thisptr + # Avoid double free - The original is in charge of deletion + self.__thisptr_to_be_deleted = False + else: + self.__thisptr_to_be_deleted = True + self.thisptr = (new Simplex_tree_interface_full_featured()) def __dealloc__(self): cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() - if ptr != NULL: + # Avoid double free - The original is in charge of deletion + if ptr != NULL and self.__thisptr_to_be_deleted: del ptr if self.pcohptr != NULL: del self.pcohptr @@ -63,20 +83,34 @@ cdef class SimplexTree: """ return self.pcohptr != NULL - def copy(self): + def copy(self, deep=True): """ - :returns: A simplex tree that is a deep copy itself. + :param deep: If `True`, the copy will be deep and if `False`, the copy will be shallow. Default is `True`. + :type deep: bool + :returns: A simplex tree that is a (deep or shallow) copy of itself. :rtype: SimplexTree + :note: copy requires :func:`compute_persistence` to be launched again as the result is not copied. """ stree = SimplexTree() cdef Simplex_tree_interface_full_featured* stree_ptr cdef Simplex_tree_interface_full_featured* self_ptr=self.get_ptr() - with nogil: - stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) - - stree.thisptr = (stree_ptr) + if deep: + with nogil: + stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) + + stree.thisptr = (stree_ptr) + else: + stree.thisptr = self.thisptr + # Avoid double free - The original is in charge of deletion + stree.__thisptr_to_be_deleted = False return stree + def __copy__(self): + return self.copy(deep=False) + + def __deepcopy__(self): + return self.copy(deep=True) + def filtration(self, simplex): """This function returns the filtration value for a given N-simplex in this simplicial complex, or +infinity if it is not in the complex. diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index dac45288..6db6d8fb 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -448,20 +448,104 @@ def test_persistence_intervals_in_dimension(): # Test empty case assert st.persistence_intervals_in_dimension(3).shape == (0, 2) -def test_simplex_tree_copy(): +def test_simplex_tree_deep_copy(): st = SimplexTree() - st .insert([1,2,3], 0.) - a = st.copy() + st.insert([1, 2, 3], 0.) + # persistence is not copied + st.compute_persistence() + + st_copy = st.copy(deep=True) # TODO(VR): when #463 is merged, replace with - # assert a == st - assert a.num_vertices() == st.num_vertices() - assert a.num_simplices() == st.num_simplices() + # assert st_copy == st + assert st_copy.num_vertices() == st.num_vertices() + assert st_copy.num_simplices() == st.num_simplices() st_filt_list = list(st.get_filtration()) - assert list(a.get_filtration()) == st_filt_list + assert list(st_copy.get_filtration()) == st_filt_list + + assert st.__is_persistence_defined() == True + assert st_copy.__is_persistence_defined() == False - a.remove_maximal_simplex([1, 2, 3]) - a_filt_list = list(a.get_filtration()) + st_copy.remove_maximal_simplex([1, 2, 3]) + a_filt_list = list(st_copy.get_filtration()) assert len(a_filt_list) < len(st_filt_list) for a_splx in a_filt_list: assert a_splx in st_filt_list + + # test double free + del st + del st_copy + +def test_simplex_tree_shallow_copy(): + st = SimplexTree() + st.insert([1, 2, 3], 0.) + # persistence is not copied + st.compute_persistence() + + st_copy = st.copy(deep=False) + # TODO(VR): when #463 is merged, replace with + # assert st_copy == st + assert st_copy.num_vertices() == st.num_vertices() + assert st_copy.num_simplices() == st.num_simplices() + assert list(st_copy.get_filtration()) == list(st.get_filtration()) + + assert st.__is_persistence_defined() == True + assert st_copy.__is_persistence_defined() == False + + st_copy.assign_filtration([1, 2, 3], 2.) + assert list(st_copy.get_filtration()) == list(st.get_filtration()) + + # test double free + del st + del st_copy + +def test_simplex_tree_deep_copy_constructor(): + st = SimplexTree() + st.insert([1, 2, 3], 0.) + # persistence is not copied + st.compute_persistence() + + st_copy = SimplexTree(st, copy = True) + # TODO(VR): when #463 is merged, replace with + # assert st_copy == st + assert st_copy.num_vertices() == st.num_vertices() + assert st_copy.num_simplices() == st.num_simplices() + st_filt_list = list(st.get_filtration()) + assert list(st_copy.get_filtration()) == st_filt_list + + assert st.__is_persistence_defined() == True + assert st_copy.__is_persistence_defined() == False + + st_copy.remove_maximal_simplex([1, 2, 3]) + a_filt_list = list(st_copy.get_filtration()) + assert len(a_filt_list) < len(st_filt_list) + + for a_splx in a_filt_list: + assert a_splx in st_filt_list + + # test double free + del st + del st_copy + +def test_simplex_tree_shallow_copy(): + st = SimplexTree() + st.insert([1, 2, 3], 0.) + # persistence is not copied + st.compute_persistence() + + st_copy = SimplexTree(st, copy = False) + # TODO(VR): when #463 is merged, replace with + # assert st_copy == st + assert st_copy.num_vertices() == st.num_vertices() + assert st_copy.num_simplices() == st.num_simplices() + assert list(st_copy.get_filtration()) == list(st.get_filtration()) + + assert st.__is_persistence_defined() == True + assert st_copy.__is_persistence_defined() == False + + st_copy.assign_filtration([1, 2, 3], 2.) + assert list(st_copy.get_filtration()) == list(st.get_filtration()) + + # test double free + del st + del st_copy -- cgit v1.2.3 From 43981a4d487669fe2002337ab62b72dd9e83a64a Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 14 Feb 2022 11:08:00 +0100 Subject: Remove shallow copy --- .github/next_release.md | 2 +- src/python/gudhi/simplex_tree.pyx | 54 +++++++++++++----------------------- src/python/test/test_simplex_tree.py | 50 ++------------------------------- 3 files changed, 23 insertions(+), 83 deletions(-) (limited to 'src/python') diff --git a/.github/next_release.md b/.github/next_release.md index 3946404b..3d4761eb 100644 --- a/.github/next_release.md +++ b/.github/next_release.md @@ -14,7 +14,7 @@ Below is a list of changes made since GUDHI 3.5.0: - A more flexible Betti curve class capable of computing exact curves - [Simplex tree](https://gudhi.inria.fr/python/latest/simplex_tree_ref.html) - - `__copy__`, `__deepcopy__`, `copy` and copy constructors + - `__deepcopy__`, `copy` and copy constructors - Installation - Boost ≥ 1.66.0 is now required (was ≥ 1.56.0). diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index ed7c3b92..0213e363 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -30,7 +30,6 @@ cdef class SimplexTree: # unfortunately 'cdef public Simplex_tree_interface_full_featured* thisptr' is not possible # Use intptr_t instead to cast the pointer cdef public intptr_t thisptr - cdef bool __thisptr_to_be_deleted # Get the pointer casted as it should be cdef Simplex_tree_interface_full_featured* get_ptr(self) nogil: @@ -39,36 +38,32 @@ cdef class SimplexTree: cdef Simplex_tree_persistence_interface * pcohptr # Fake constructor that does nothing but documenting the constructor - def __init__(self, other = None, copy = True): + def __init__(self, other = None): """SimplexTree constructor. - :param other: If `other` is a SimplexTree (default = None), the SimplexTree is constructed from a deep/shallow copy of `other`. + + :param other: If `other` is a `None` (default value), an empty `SimplexTree` is created. + If `other` is a `SimplexTree`, the `SimplexTree` is constructed from a deep copy of `other`. :type other: SimplexTree - :param copy: If `True`, the copy will be deep and if `False, the copy will be shallow. Default is `True`. - :type copy: bool - :returns: A simplex tree that is a (deep or shallow) copy of itself. + :returns: An empty or a copy simplex tree. :rtype: SimplexTree - :note: copy constructor requires :func:`compute_persistence` to be launched again as the result is not copied. + + :note: If the `SimplexTree` is a copy, it requires :func:`compute_persistence` to be launched again as the + persistence result is not copied. """ # The real cython constructor - def __cinit__(self, other = None, copy = True): + def __cinit__(self, other = None): cdef SimplexTree ostr if other and type(other) is SimplexTree: ostr = other - if copy: - self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) - else: - self.thisptr = ostr.thisptr - # Avoid double free - The original is in charge of deletion - self.__thisptr_to_be_deleted = False + self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) else: - self.__thisptr_to_be_deleted = True self.thisptr = (new Simplex_tree_interface_full_featured()) def __dealloc__(self): cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() # Avoid double free - The original is in charge of deletion - if ptr != NULL and self.__thisptr_to_be_deleted: + if ptr != NULL: del ptr if self.pcohptr != NULL: del self.pcohptr @@ -83,33 +78,24 @@ cdef class SimplexTree: """ return self.pcohptr != NULL - def copy(self, deep=True): + def copy(self): """ - :param deep: If `True`, the copy will be deep and if `False`, the copy will be shallow. Default is `True`. - :type deep: bool - :returns: A simplex tree that is a (deep or shallow) copy of itself. + :returns: A simplex tree that is a deep copy of itself. :rtype: SimplexTree - :note: copy requires :func:`compute_persistence` to be launched again as the result is not copied. + + :note: copy requires :func:`compute_persistence` to be launched again as the persistence result is not copied. """ stree = SimplexTree() cdef Simplex_tree_interface_full_featured* stree_ptr cdef Simplex_tree_interface_full_featured* self_ptr=self.get_ptr() - if deep: - with nogil: - stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) - - stree.thisptr = (stree_ptr) - else: - stree.thisptr = self.thisptr - # Avoid double free - The original is in charge of deletion - stree.__thisptr_to_be_deleted = False - return stree + with nogil: + stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) - def __copy__(self): - return self.copy(deep=False) + stree.thisptr = (stree_ptr) + return stree def __deepcopy__(self): - return self.copy(deep=True) + return self.copy() def filtration(self, simplex): """This function returns the filtration value for a given N-simplex in diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 6db6d8fb..62dcc865 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -454,7 +454,7 @@ def test_simplex_tree_deep_copy(): # persistence is not copied st.compute_persistence() - st_copy = st.copy(deep=True) + st_copy = st.copy() # TODO(VR): when #463 is merged, replace with # assert st_copy == st assert st_copy.num_vertices() == st.num_vertices() @@ -476,36 +476,13 @@ def test_simplex_tree_deep_copy(): del st del st_copy -def test_simplex_tree_shallow_copy(): - st = SimplexTree() - st.insert([1, 2, 3], 0.) - # persistence is not copied - st.compute_persistence() - - st_copy = st.copy(deep=False) - # TODO(VR): when #463 is merged, replace with - # assert st_copy == st - assert st_copy.num_vertices() == st.num_vertices() - assert st_copy.num_simplices() == st.num_simplices() - assert list(st_copy.get_filtration()) == list(st.get_filtration()) - - assert st.__is_persistence_defined() == True - assert st_copy.__is_persistence_defined() == False - - st_copy.assign_filtration([1, 2, 3], 2.) - assert list(st_copy.get_filtration()) == list(st.get_filtration()) - - # test double free - del st - del st_copy - def test_simplex_tree_deep_copy_constructor(): st = SimplexTree() st.insert([1, 2, 3], 0.) # persistence is not copied st.compute_persistence() - st_copy = SimplexTree(st, copy = True) + st_copy = SimplexTree(st) # TODO(VR): when #463 is merged, replace with # assert st_copy == st assert st_copy.num_vertices() == st.num_vertices() @@ -526,26 +503,3 @@ def test_simplex_tree_deep_copy_constructor(): # test double free del st del st_copy - -def test_simplex_tree_shallow_copy(): - st = SimplexTree() - st.insert([1, 2, 3], 0.) - # persistence is not copied - st.compute_persistence() - - st_copy = SimplexTree(st, copy = False) - # TODO(VR): when #463 is merged, replace with - # assert st_copy == st - assert st_copy.num_vertices() == st.num_vertices() - assert st_copy.num_simplices() == st.num_simplices() - assert list(st_copy.get_filtration()) == list(st.get_filtration()) - - assert st.__is_persistence_defined() == True - assert st_copy.__is_persistence_defined() == False - - st_copy.assign_filtration([1, 2, 3], 2.) - assert list(st_copy.get_filtration()) == list(st.get_filtration()) - - # test double free - del st - del st_copy -- cgit v1.2.3 From c7303733b28d3bc429d4bdfd030409e07430599d Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 14 Feb 2022 11:29:49 +0100 Subject: Rewrite test as #463 is merged. Rephrase constructor documentation --- src/python/gudhi/simplex_tree.pyx | 4 ++-- src/python/test/test_simplex_tree.py | 20 ++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 6701d98d..a2b8719a 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -41,9 +41,9 @@ cdef class SimplexTree: def __init__(self, other = None): """SimplexTree constructor. - :param other: If `other` is a `None` (default value), an empty `SimplexTree` is created. + :param other: If `other` is `None` (default value), an empty `SimplexTree` is created. If `other` is a `SimplexTree`, the `SimplexTree` is constructed from a deep copy of `other`. - :type other: SimplexTree + :type other: SimplexTree (Optional) :returns: An empty or a copy simplex tree. :rtype: SimplexTree diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 32fc63ec..2c2e09a2 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -463,20 +463,18 @@ def test_equality_operator(): def test_simplex_tree_deep_copy(): st = SimplexTree() st.insert([1, 2, 3], 0.) - # persistence is not copied + # compute persistence only on the original st.compute_persistence() st_copy = st.copy() - # TODO(VR): when #463 is merged, replace with - # assert st_copy == st - assert st_copy.num_vertices() == st.num_vertices() - assert st_copy.num_simplices() == st.num_simplices() + assert st_copy == st st_filt_list = list(st.get_filtration()) - assert list(st_copy.get_filtration()) == st_filt_list + # check persistence is not copied assert st.__is_persistence_defined() == True assert st_copy.__is_persistence_defined() == False + # remove something in the copy and check the copy is included in the original st_copy.remove_maximal_simplex([1, 2, 3]) a_filt_list = list(st_copy.get_filtration()) assert len(a_filt_list) < len(st_filt_list) @@ -491,20 +489,18 @@ def test_simplex_tree_deep_copy(): def test_simplex_tree_deep_copy_constructor(): st = SimplexTree() st.insert([1, 2, 3], 0.) - # persistence is not copied + # compute persistence only on the original st.compute_persistence() st_copy = SimplexTree(st) - # TODO(VR): when #463 is merged, replace with - # assert st_copy == st - assert st_copy.num_vertices() == st.num_vertices() - assert st_copy.num_simplices() == st.num_simplices() + assert st_copy == st st_filt_list = list(st.get_filtration()) - assert list(st_copy.get_filtration()) == st_filt_list + # check persistence is not copied assert st.__is_persistence_defined() == True assert st_copy.__is_persistence_defined() == False + # remove something in the copy and check the copy is included in the original st_copy.remove_maximal_simplex([1, 2, 3]) a_filt_list = list(st_copy.get_filtration()) assert len(a_filt_list) < len(st_filt_list) -- cgit v1.2.3 From 1a0551c033e55024cd0f00302cd9df1f356bab44 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 14 Feb 2022 11:31:53 +0100 Subject: some left over --- src/python/gudhi/simplex_tree.pyx | 1 - 1 file changed, 1 deletion(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index a2b8719a..8f760422 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -62,7 +62,6 @@ cdef class SimplexTree: def __dealloc__(self): cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() - # Avoid double free - The original is in charge of deletion if ptr != NULL: del ptr if self.pcohptr != NULL: -- cgit v1.2.3 From d5ac245a6dc4ab2d6e30689fc5d95503c40b6187 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 14 Feb 2022 12:31:30 +0100 Subject: code review: ctor shall raise a TypeError when constructed from something else than a SimplexTree --- src/python/gudhi/simplex_tree.pyx | 10 +++++++--- src/python/test/test_simplex_tree.py | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 8f760422..e1685ded 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -47,6 +47,7 @@ cdef class SimplexTree: :returns: An empty or a copy simplex tree. :rtype: SimplexTree + :raises TypeError: In case `other` is neither `None`, nor a `SimplexTree`. :note: If the `SimplexTree` is a copy, it requires :func:`compute_persistence` to be launched again as the persistence result is not copied. """ @@ -54,9 +55,12 @@ cdef class SimplexTree: # The real cython constructor def __cinit__(self, other = None): cdef SimplexTree ostr - if other and type(other) is SimplexTree: - ostr = other - self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) + if other: + if type(other) is SimplexTree: + ostr = other + self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) + else: + raise TypeError("`other` argument requires to be of type `SimplexTree`, or `None`.") else: self.thisptr = (new Simplex_tree_interface_full_featured()) diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index 2c2e09a2..a8180ce8 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -511,3 +511,7 @@ def test_simplex_tree_deep_copy_constructor(): # test double free del st del st_copy + +def test_simplex_tree_constructor_exception(): + with pytest.raises(TypeError): + st = SimplexTree(other = "Construction from a string shall raise an exception") -- cgit v1.2.3 From 362ac0955656d7c131e042901d6606fdeaab7fc9 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Mon, 14 Feb 2022 23:33:38 +0100 Subject: More details in the doc of weighted rips --- biblio/bibliography.bib | 25 ++++++++++++++++++++++++- src/python/gudhi/weighted_rips_complex.py | 6 ++++-- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src/python') diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index b5afff52..e75e8db2 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -1280,7 +1280,7 @@ year = "2011" publisher={Springer} } -@inproceedings{dtmfiltrations, +@inproceedings{dtmfiltrationsconf, author = {Hirokazu Anai and Fr{\'{e}}d{\'{e}}ric Chazal and Marc Glisse and @@ -1305,6 +1305,29 @@ year = "2011" bibsource = {dblp computer science bibliography, https://dblp.org} } +@InProceedings{dtmfiltrations, +author="Anai, Hirokazu +and Chazal, Fr{\'e}d{\'e}ric +and Glisse, Marc +and Ike, Yuichi +and Inakoshi, Hiroya +and Tinarrage, Rapha{\"e}l +and Umeda, Yuhei", +editor="Baas, Nils A. +and Carlsson, Gunnar E. +and Quick, Gereon +and Szymik, Markus +and Thaule, Marius", +title="DTM-Based Filtrations", +booktitle="Topological Data Analysis", +year="2020", +publisher="Springer International Publishing", +address="Cham", +pages="33--66", +isbn="978-3-030-43408-3", +doi="10.1007/978-3-030-43408-3_2", +} + @InProceedings{edgecollapsesocg2020, author = {Jean-Daniel Boissonnat and Siddharth Pritam}, title = {{Edge Collapse and Persistence of Flag Complexes}}, diff --git a/src/python/gudhi/weighted_rips_complex.py b/src/python/gudhi/weighted_rips_complex.py index 0541572b..16f63c3d 100644 --- a/src/python/gudhi/weighted_rips_complex.py +++ b/src/python/gudhi/weighted_rips_complex.py @@ -12,9 +12,11 @@ from gudhi import SimplexTree class WeightedRipsComplex: """ Class to generate a weighted Rips complex from a distance matrix and weights on vertices, - in the way described in :cite:`dtmfiltrations`. + in the way described in :cite:`dtmfiltrations` with `p=1`. The filtration value of vertex `i` is `2*weights[i]`, + and the filtration value of edge `ij` is `distance_matrix[i][j]+weights[i]+weights[j]`, + or the maximum of the filtrations of its extremities, whichever is largest. Remark that all the filtration values are doubled compared to the definition in the paper - for the consistency with RipsComplex. + for consistency with RipsComplex. """ def __init__(self, distance_matrix, -- cgit v1.2.3 From 4e63e52758daa7ef27782b4f129b7e55fa73de43 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 28 Feb 2022 09:50:08 +0100 Subject: code review: use 'isinstance' instead of 'type' --- src/python/gudhi/simplex_tree.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index e1685ded..711796d4 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -56,7 +56,7 @@ cdef class SimplexTree: def __cinit__(self, other = None): cdef SimplexTree ostr if other: - if type(other) is SimplexTree: + if isinstance(other, SimplexTree): ostr = other self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) else: -- cgit v1.2.3 From e7ba8967e9b5c3c7260522003d8f87e643b7912e Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 28 Feb 2022 10:56:47 +0100 Subject: doc review: reformulate the copy notes --- src/python/gudhi/simplex_tree.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 711796d4..91e079aa 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -48,8 +48,8 @@ cdef class SimplexTree: :rtype: SimplexTree :raises TypeError: In case `other` is neither `None`, nor a `SimplexTree`. - :note: If the `SimplexTree` is a copy, it requires :func:`compute_persistence` to be launched again as the - persistence result is not copied. + :note: If the `SimplexTree` is a copy, the persistence information is not copied. If you need it in the clone, + you have to call :func:`compute_persistence` on it even if you had already computed it in the original. """ # The real cython constructor @@ -86,7 +86,8 @@ cdef class SimplexTree: :returns: A simplex tree that is a deep copy of itself. :rtype: SimplexTree - :note: copy requires :func:`compute_persistence` to be launched again as the persistence result is not copied. + :note: The persistence information is not copied. If you need it in the clone, you have to call + :func:`compute_persistence` on it even if you had already computed it in the original. """ stree = SimplexTree() cdef Simplex_tree_interface_full_featured* stree_ptr -- cgit v1.2.3 From 41a976cc85ab36dc2df748b9d6900d77e76b2fa7 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 28 Feb 2022 14:58:24 +0100 Subject: code review: copy simplex tree c++ pointer in a factorized nogil function --- src/python/gudhi/simplex_tree.pyx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 91e079aa..b8fabf78 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -54,11 +54,9 @@ cdef class SimplexTree: # The real cython constructor def __cinit__(self, other = None): - cdef SimplexTree ostr if other: if isinstance(other, SimplexTree): - ostr = other - self.thisptr = (new Simplex_tree_interface_full_featured(dereference(ostr.get_ptr()))) + self.thisptr = _get_copy_intptr(other) else: raise TypeError("`other` argument requires to be of type `SimplexTree`, or `None`.") else: @@ -90,12 +88,7 @@ cdef class SimplexTree: :func:`compute_persistence` on it even if you had already computed it in the original. """ stree = SimplexTree() - cdef Simplex_tree_interface_full_featured* stree_ptr - cdef Simplex_tree_interface_full_featured* self_ptr=self.get_ptr() - with nogil: - stree_ptr = new Simplex_tree_interface_full_featured(dereference(self_ptr)) - - stree.thisptr = (stree_ptr) + stree.thisptr = _get_copy_intptr(self) return stree def __deepcopy__(self): @@ -687,3 +680,6 @@ cdef class SimplexTree: :rtype: bool """ return dereference(self.get_ptr()) == dereference(other.get_ptr()) + +cdef intptr_t _get_copy_intptr(SimplexTree stree) nogil: + return (new Simplex_tree_interface_full_featured(dereference(stree.get_ptr()))) -- cgit v1.2.3 From 8667b75601cc0420f3b196d5b44ec93fd6058057 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 21 Mar 2022 17:14:30 +0100 Subject: Add some c++ tests and use unitary_tests_utils. Fix python test --- .../simplex_tree_graph_expansion_unit_test.cpp | 192 ++++++++++++++++----- src/python/test/test_simplex_tree.py | 11 +- 2 files changed, 151 insertions(+), 52 deletions(-) (limited to 'src/python') diff --git a/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp index 881a06ae..6d63d8ae 100644 --- a/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp @@ -9,33 +9,62 @@ */ #include -#include -#include -#include -#include // std::pair, std::make_pair -#include // float comparison -#include -#include // greater +#include #define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE "simplex_tree" +#define BOOST_TEST_MODULE "simplex_tree_graph_expansion" #include #include -// ^ -// /!\ Nothing else from Simplex_tree shall be included to test includes are well defined. #include "gudhi/Simplex_tree.h" +#include using namespace Gudhi; typedef boost::mpl::list, Simplex_tree> list_of_tested_variants; +BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_all_is_blocked, typeST, list_of_tested_variants) { + std::clog << "********************************************************************\n"; + std::clog << "simplex_tree_expansion_all_is_blocked\n"; + std::clog << "********************************************************************\n"; + using Simplex_handle = typename typeST::Simplex_handle; + // Construct the Simplex Tree with a 1-skeleton graph example + typeST simplex_tree; + + simplex_tree.insert_simplex({0, 1}, 0.); + simplex_tree.insert_simplex({0, 2}, 1.); + simplex_tree.insert_simplex({0, 3}, 2.); + simplex_tree.insert_simplex({1, 2}, 3.); + simplex_tree.insert_simplex({1, 3}, 4.); + simplex_tree.insert_simplex({2, 3}, 5.); + simplex_tree.insert_simplex({2, 4}, 6.); + simplex_tree.insert_simplex({3, 6}, 7.); + simplex_tree.insert_simplex({4, 5}, 8.); + simplex_tree.insert_simplex({4, 6}, 9.); + simplex_tree.insert_simplex({5, 6}, 10.); + simplex_tree.insert_simplex({6}, 10.); -bool AreAlmostTheSame(float a, float b) { - return std::fabs(a - b) < std::numeric_limits::epsilon(); + typeST stree_copy = simplex_tree; + + simplex_tree.expansion_with_blockers(3, [&](Simplex_handle sh){ return true; }); + + std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; + std::clog << " - dimension " << simplex_tree.dimension() << "\n"; + std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; + for (auto f_simplex : simplex_tree.filtration_simplex_range()) { + std::clog << " " << "[" << simplex_tree.filtration(f_simplex) << "] "; + for (auto vertex : simplex_tree.simplex_vertex_range(f_simplex)) + std::clog << "(" << vertex << ")"; + std::clog << std::endl; + } + + BOOST_CHECK(stree_copy == simplex_tree); } BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_3, typeST, list_of_tested_variants) { + std::clog << "********************************************************************\n"; + std::clog << "simplex_tree_expansion_with_blockers_3\n"; + std::clog << "********************************************************************\n"; using Simplex_handle = typename typeST::Simplex_handle; // Construct the Simplex Tree with a 1-skeleton graph example typeST simplex_tree; @@ -72,9 +101,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_3, typeST, li return result; }); - std::clog << "********************************************************************\n"; - std::clog << "simplex_tree_expansion_with_blockers_3\n"; - std::clog << "********************************************************************\n"; std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; std::clog << " - dimension " << simplex_tree.dimension() << "\n"; std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; @@ -89,15 +115,23 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_3, typeST, li BOOST_CHECK(simplex_tree.dimension() == 3); // {4, 5, 6} shall be blocked BOOST_CHECK(simplex_tree.find({4, 5, 6}) == simplex_tree.null_simplex()); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2})), 4.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,3})), 5.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,2,3})), 6.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({1,2,3})), 6.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2,3})), 7.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2})), + static_cast(4.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,3})), + static_cast(5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,2,3})), + static_cast(6.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({1,2,3})), + static_cast(6.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2,3})), + static_cast(7.)); } BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_2, typeST, list_of_tested_variants) { + std::clog << "********************************************************************\n"; + std::clog << "simplex_tree_expansion_with_blockers_2\n"; + std::clog << "********************************************************************\n"; using Simplex_handle = typename typeST::Simplex_handle; // Construct the Simplex Tree with a 1-skeleton graph example typeST simplex_tree; @@ -134,9 +168,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_2, typeST, li return result; }); - std::clog << "********************************************************************\n"; - std::clog << "simplex_tree_expansion_with_blockers_2\n"; - std::clog << "********************************************************************\n"; std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; std::clog << " - dimension " << simplex_tree.dimension() << "\n"; std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; @@ -151,14 +182,22 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_blockers_2, typeST, li BOOST_CHECK(simplex_tree.dimension() == 2); // {4, 5, 6} shall be blocked BOOST_CHECK(simplex_tree.find({4, 5, 6}) == simplex_tree.null_simplex()); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2})), 4.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,3})), 5.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,2,3})), 6.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({1,2,3})), 6.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2})), + static_cast(4.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,3})), + static_cast(5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,2,3})), + static_cast(6.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({1,2,3})), + static_cast(6.)); BOOST_CHECK(simplex_tree.find({0,1,2,3}) == simplex_tree.null_simplex()); } -BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion, typeST, list_of_tested_variants) { +BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_with_find_simplex_blockers, typeST, list_of_tested_variants) { + std::clog << "********************************************************************\n"; + std::clog << "simplex_tree_expansion_with_find_simplex_blockers\n"; + std::clog << "********************************************************************\n"; + using Simplex_handle = typename typeST::Simplex_handle; // Construct the Simplex Tree with a 1-skeleton graph example typeST simplex_tree; @@ -175,10 +214,66 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion, typeST, list_of_tested_var simplex_tree.insert_simplex({5, 6}, 10.); simplex_tree.insert_simplex({6}, 10.); - simplex_tree.expansion(3); + simplex_tree.expansion_with_blockers(3, [&](Simplex_handle sh){ + bool result = false; + std::clog << "Blocker on ["; + std::vector simplex; + // User can loop on the vertices from the given simplex_handle i.e. + for (auto vertex : simplex_tree.simplex_vertex_range(sh)) { + // We block the expansion, if the vertex '1' is in the given list of vertices + if (vertex == 1) + result = true; + std::clog << vertex << ", "; + simplex.push_back(vertex); + } + std::clog << "] => " << result << std::endl; + // Not efficient but test it works - required by the python interface + BOOST_CHECK(simplex_tree.find(simplex) == sh); + return result; + }); + + std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; + std::clog << " - dimension " << simplex_tree.dimension() << "\n"; + std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; + for (auto f_simplex : simplex_tree.filtration_simplex_range()) { + std::clog << " " << "[" << simplex_tree.filtration(f_simplex) << "] "; + for (auto vertex : simplex_tree.simplex_vertex_range(f_simplex)) + std::clog << "(" << vertex << ")"; + std::clog << std::endl; + } + + BOOST_CHECK(simplex_tree.num_simplices() == 20); + BOOST_CHECK(simplex_tree.dimension() == 2); + + // {1, 2, 3}, {0, 1, 2} and {0, 1, 3} shall be blocked as it contains vertex 1 + BOOST_CHECK(simplex_tree.find({4, 5, 6}) != simplex_tree.null_simplex()); + BOOST_CHECK(simplex_tree.find({1, 2, 3}) == simplex_tree.null_simplex()); + BOOST_CHECK(simplex_tree.find({0, 2, 3}) != simplex_tree.null_simplex()); + BOOST_CHECK(simplex_tree.find({0, 1, 2}) == simplex_tree.null_simplex()); + BOOST_CHECK(simplex_tree.find({0, 1, 3}) == simplex_tree.null_simplex()); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_3, typeST, list_of_tested_variants) { std::clog << "********************************************************************\n"; std::clog << "simplex_tree_expansion_3\n"; std::clog << "********************************************************************\n"; + // Construct the Simplex Tree with a 1-skeleton graph example + typeST simplex_tree; + + simplex_tree.insert_simplex({0, 1}, 0.); + simplex_tree.insert_simplex({0, 2}, 1.); + simplex_tree.insert_simplex({0, 3}, 2.); + simplex_tree.insert_simplex({1, 2}, 3.); + simplex_tree.insert_simplex({1, 3}, 4.); + simplex_tree.insert_simplex({2, 3}, 5.); + simplex_tree.insert_simplex({2, 4}, 6.); + simplex_tree.insert_simplex({3, 6}, 7.); + simplex_tree.insert_simplex({4, 5}, 8.); + simplex_tree.insert_simplex({4, 6}, 9.); + simplex_tree.insert_simplex({5, 6}, 10.); + simplex_tree.insert_simplex({6}, 10.); + + simplex_tree.expansion(3); std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; std::clog << " - dimension " << simplex_tree.dimension() << "\n"; std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; @@ -192,16 +287,25 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion, typeST, list_of_tested_var BOOST_CHECK(simplex_tree.num_simplices() == 24); BOOST_CHECK(simplex_tree.dimension() == 3); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({4,5,6})), 10.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2})), 3.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,3})), 4.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,2,3})), 5.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({1,2,3})), 5.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2,3})), 5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({4,5,6})), + static_cast(10.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2})), + static_cast(3.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,3})), + static_cast(4.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,2,3})), + static_cast(5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({1,2,3})), + static_cast(5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2,3})), + static_cast(5.)); } BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_2, typeST, list_of_tested_variants) { + std::clog << "********************************************************************\n"; + std::clog << "simplex_tree_expansion_2\n"; + std::clog << "********************************************************************\n"; // Construct the Simplex Tree with a 1-skeleton graph example typeST simplex_tree; @@ -220,9 +324,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_2, typeST, list_of_tested_v simplex_tree.expansion(2); - std::clog << "********************************************************************\n"; - std::clog << "simplex_tree_expansion_2\n"; - std::clog << "********************************************************************\n"; std::clog << "* The complex contains " << simplex_tree.num_simplices() << " simplices"; std::clog << " - dimension " << simplex_tree.dimension() << "\n"; std::clog << "* Iterator on Simplices in the filtration, with [filtration value]:\n"; @@ -236,10 +337,15 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_2, typeST, list_of_tested_v BOOST_CHECK(simplex_tree.num_simplices() == 23); BOOST_CHECK(simplex_tree.dimension() == 2); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({4,5,6})), 10.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,2})), 3.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,1,3})), 4.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({0,2,3})), 5.)); - BOOST_CHECK(AreAlmostTheSame(simplex_tree.filtration(simplex_tree.find({1,2,3})), 5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({4,5,6})), + static_cast(10.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,2})), + static_cast(3.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,1,3})), + static_cast(4.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({0,2,3})), + static_cast(5.)); + GUDHI_TEST_FLOAT_EQUALITY_CHECK(simplex_tree.filtration(simplex_tree.find({1,2,3})), + static_cast(5.)); BOOST_CHECK(simplex_tree.find({0,1,2,3}) == simplex_tree.null_simplex()); } diff --git a/src/python/test/test_simplex_tree.py b/src/python/test/test_simplex_tree.py index d8173b52..eb481a49 100755 --- a/src/python/test/test_simplex_tree.py +++ b/src/python/test/test_simplex_tree.py @@ -531,8 +531,6 @@ def test_expansion_with_blocker(): st.insert([5,6],10) st.insert([6],10) - # One cannot modify filtration inside blocker - segfault - Let's record accepted simplices - accepted_simp = [] def blocker(simplex): try: # Block all simplices that countains vertex 6 @@ -541,12 +539,10 @@ def test_expansion_with_blocker(): return True except ValueError: print(simplex, ' is accepted') - accepted_simp.append(simplex) + st.assign_filtration(simplex, st.filtration(simplex) + 1.) return False st.expansion_with_blocker(2, blocker) - for simplex in accepted_simp: - st.assign_filtration(simplex, st.filtration(simplex) + 1.) assert st.num_simplices() == 22 assert st.dimension() == 2 assert st.find([4,5,6]) == False @@ -555,10 +551,7 @@ def test_expansion_with_blocker(): assert st.filtration([0,2,3]) == 6. assert st.filtration([1,2,3]) == 6. - accepted_simp = [] st.expansion_with_blocker(3, blocker) - for simplex in accepted_simp: - st.assign_filtration(simplex, st.filtration(simplex) + 1.) assert st.num_simplices() == 23 assert st.dimension() == 3 assert st.find([4,5,6]) == False @@ -566,4 +559,4 @@ def test_expansion_with_blocker(): assert st.filtration([0,1,3]) == 5. assert st.filtration([0,2,3]) == 6. assert st.filtration([1,2,3]) == 6. - assert st.filtration([0,1,2,3]) == 6. + assert st.filtration([0,1,2,3]) == 7. -- cgit v1.2.3 From 924302f092d6d08c59500c5f6e75bcad9416581a Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 28 Mar 2022 08:39:21 +0200 Subject: doc review: remove segfault note as it is no more the case --- src/python/gudhi/simplex_tree.pyx | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index 644110c1..fc704358 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -487,9 +487,6 @@ cdef class SimplexTree: with this new simplex (represented as a list of int). If `blocker_func` returns `True`, the simplex is removed, otherwise it is kept. The algorithm then proceeds with the next candidate. - Note that you cannot update the filtration value of the simplex during the evaluation of `blocker_func`, as it - would segfault. - :param max_dim: Expansion maximal dimension value. :type max_dim: int :param blocker_func: Blocker oracle. -- cgit v1.2.3 From f6a45247e9a9f126c214d1b1003ae19fb2cc84a3 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Mon, 28 Mar 2022 09:00:23 +0200 Subject: doc review: add a warning about phantom simplices for blocker_func --- src/python/gudhi/simplex_tree.pyx | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/python') diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index fc704358..a4914184 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -487,6 +487,11 @@ cdef class SimplexTree: with this new simplex (represented as a list of int). If `blocker_func` returns `True`, the simplex is removed, otherwise it is kept. The algorithm then proceeds with the next candidate. + .. warning:: + Several candidates of the same dimension may be inserted simultaneously before calling `block_simplex`, so + if you examine the complex in `block_simplex`, you may hit a few simplices of the same dimension that have + not been vetted by `block_simplex` yet, or have already been rejected but not yet removed. + :param max_dim: Expansion maximal dimension value. :type max_dim: int :param blocker_func: Blocker oracle. -- cgit v1.2.3 From 495f3d9b51f5ed48151118fedcc61f5067f04d51 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Wed, 30 Mar 2022 12:22:56 +0200 Subject: get/set float_relative_precision and its documentation --- src/python/doc/alpha_complex_user.rst | 3 ++- src/python/gudhi/alpha_complex.pyx | 28 ++++++++++++++++++++++++++++ src/python/include/Alpha_complex_interface.h | 10 ++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/doc/alpha_complex_user.rst b/src/python/doc/alpha_complex_user.rst index cfd22742..b060c86e 100644 --- a/src/python/doc/alpha_complex_user.rst +++ b/src/python/doc/alpha_complex_user.rst @@ -27,7 +27,8 @@ Remarks If you pass :code:`precision = 'exact'` to :func:`~gudhi.AlphaComplex.__init__`, the filtration values are the exact ones converted to float. This can be very slow. If you pass :code:`precision = 'safe'` (the default), the filtration values are only - guaranteed to have a small multiplicative error compared to the exact value. + guaranteed to have a small multiplicative error compared to the exact value, see + :func:`~gudhi.AlphaComplex.set_float_relative_precision` to modify the precision. A drawback, when computing persistence, is that an empty exact interval [10^12,10^12] may become a non-empty approximate interval [10^12,10^12+10^6]. Using :code:`precision = 'fast'` makes the computations slightly faster, and the combinatorics are still exact, but diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index a4888914..9af62b43 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -31,6 +31,10 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": Alpha_complex_interface(vector[vector[double]] points, vector[double] weights, bool fast_version, bool exact_version) nogil except + vector[double] get_point(int vertex) nogil except + void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool default_filtration_value) nogil except + + @staticmethod + void set_float_relative_precision(double precision) nogil + @staticmethod + double get_float_relative_precision() nogil # AlphaComplex python interface cdef class AlphaComplex: @@ -133,3 +137,27 @@ cdef class AlphaComplex: self.this_ptr.create_simplex_tree(stree_int_ptr, mas, compute_filtration) return stree + + @staticmethod + def set_float_relative_precision(precision): + """ + :param precision: When the AlphaComplex is constructed with :code:`precision = 'safe'` (the default), + one can set the float relative precision of filtration values computed in + :func:`~gudhi.AlphaComplex.create_simplex_tree`. + Default is :code:`1e-5` (cf. :func:`~gudhi.AlphaComplex.get_float_relative_precision`). + For more details, please refer to + `CGAL::Lazy_exact_nt::set_relative_precision_of_to_double `_ + :type precision: float + """ + if precision <=0. or precision >= 1.: + raise ValueError("Relative precision value must be strictly greater than 0 and strictly lower than 1") + Alpha_complex_interface.set_float_relative_precision(precision) + + @staticmethod + def get_float_relative_precision(): + """ + :returns: The float relative precision of filtration values computation in + :func:`~gudhi.AlphaComplex.create_simplex_tree`. + :rtype: float + """ + return Alpha_complex_interface.get_float_relative_precision() diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index 671af4a4..469b91ce 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -57,6 +57,16 @@ class Alpha_complex_interface { alpha_ptr_->create_simplex_tree(simplex_tree, max_alpha_square, default_filtration_value); } + static void set_float_relative_precision(double precision) { + // cf. Exact_alpha_complex_dD kernel type in Alpha_complex_factory.h + CGAL::Epeck_d::FT::set_relative_precision_of_to_double(precision); + } + + static double get_float_relative_precision() { + // cf. Exact_alpha_complex_dD kernel type in Alpha_complex_factory.h + return CGAL::Epeck_d::FT::get_relative_precision_of_to_double(); + } + private: std::unique_ptr alpha_ptr_; }; -- cgit v1.2.3 From 37dcedb59cd2a6b70119688ea24c978749d63154 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Thu, 31 Mar 2022 16:37:20 +0200 Subject: Test the feature --- src/python/test/test_alpha_complex.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src/python') diff --git a/src/python/test/test_alpha_complex.py b/src/python/test/test_alpha_complex.py index f15284f3..f81e6137 100755 --- a/src/python/test/test_alpha_complex.py +++ b/src/python/test/test_alpha_complex.py @@ -286,3 +286,30 @@ def _weighted_doc_example(precision): def test_weighted_doc_example(): for precision in ['fast', 'safe', 'exact']: _weighted_doc_example(precision) + +def test_float_relative_precision(): + assert AlphaComplex.get_float_relative_precision() == 1e-5 + # Must be > 0. + with pytest.raises(ValueError): + AlphaComplex.set_float_relative_precision(0.) + # Must be < 1. + with pytest.raises(ValueError): + AlphaComplex.set_float_relative_precision(1.) + + points = [[1, 1], [7, 0], [4, 6], [9, 6], [0, 14], [2, 19], [9, 17]] + st = AlphaComplex(points=points).create_simplex_tree() + filtrations = list(st.get_filtration()) + + # Get a better precision + AlphaComplex.set_float_relative_precision(1e-15) + assert AlphaComplex.get_float_relative_precision() == 1e-15 + + st = AlphaComplex(points=points).create_simplex_tree() + filtrations_better_resolution = list(st.get_filtration()) + + assert len(filtrations) == len(filtrations_better_resolution) + for idx in range(len(filtrations)): + # check simplex is the same + assert filtrations[idx][0] == filtrations_better_resolution[idx][0] + # check filtration is about the same with a relative precision of the worst case + assert filtrations[idx][1] == pytest.approx(filtrations_better_resolution[idx][1], rel=1e-5) -- cgit v1.2.3 From f4b543bb4891b1c2aa104e45cc5a0d07b56e6977 Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Thu, 31 Mar 2022 21:15:18 +0200 Subject: code review: no need to nogil for cheap functions. Repeat get_float_relative_precision is only for safe mode --- src/python/gudhi/alpha_complex.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 9af62b43..e63796db 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -32,9 +32,9 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": vector[double] get_point(int vertex) nogil except + void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool default_filtration_value) nogil except + @staticmethod - void set_float_relative_precision(double precision) nogil + void set_float_relative_precision(double precision) @staticmethod - double get_float_relative_precision() nogil + double get_float_relative_precision() # AlphaComplex python interface cdef class AlphaComplex: @@ -157,7 +157,8 @@ cdef class AlphaComplex: def get_float_relative_precision(): """ :returns: The float relative precision of filtration values computation in - :func:`~gudhi.AlphaComplex.create_simplex_tree`. + :func:`~gudhi.AlphaComplex.create_simplex_tree` when the AlphaComplex is constructed with + :code:`precision = 'safe'` (the default). :rtype: float """ return Alpha_complex_interface.get_float_relative_precision() -- cgit v1.2.3 From 25172af7cbe5a3ea3017bed6fde53be471a7e4bf Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau Date: Tue, 5 Apr 2022 13:35:11 +0200 Subject: nogil for float_relative_precision methods --- src/python/gudhi/alpha_complex.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/python') diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index e63796db..375e1561 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -32,9 +32,9 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": vector[double] get_point(int vertex) nogil except + void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool default_filtration_value) nogil except + @staticmethod - void set_float_relative_precision(double precision) + void set_float_relative_precision(double precision) nogil @staticmethod - double get_float_relative_precision() + double get_float_relative_precision() nogil # AlphaComplex python interface cdef class AlphaComplex: -- cgit v1.2.3 From 5857c571388a4349934a266ca187b2c2ac10818c Mon Sep 17 00:00:00 2001 From: Vincent Rouvreau <10407034+VincentRouvreau@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:44:22 +0200 Subject: Make Windows compilation fails on error (#598) Use '-DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int' as a workaround --- .github/workflows/pip-build-windows.yml | 8 ++++---- .github/workflows/pip-packaging-windows.yml | 8 ++++---- azure-pipelines.yml | 17 ++++++++++++----- src/cmake/modules/GUDHI_third_party_libraries.cmake | 9 +++++++++ src/python/CMakeLists.txt | 12 +++++++++++- 5 files changed, 40 insertions(+), 14 deletions(-) (limited to 'src/python') diff --git a/.github/workflows/pip-build-windows.yml b/.github/workflows/pip-build-windows.yml index 954b59d5..30b0bd94 100644 --- a/.github/workflows/pip-build-windows.yml +++ b/.github/workflows/pip-build-windows.yml @@ -30,14 +30,14 @@ jobs: run: | mkdir build cd ".\build\" - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. + cmake -DCMAKE_BUILD_TYPE=Release -DFORCE_EIGEN_DEFAULT_DENSE_INDEX_TYPE_TO_INT=ON -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. Get-Location dir cd ".\src\python\" - cp "C:\vcpkg\installed\x64-windows\bin\mpfr-6.dll" ".\gudhi\" - cp "C:\vcpkg\installed\x64-windows\bin\gmp.dll" ".\gudhi\" + cp "C:\vcpkg\installed\x64-windows\bin\mpfr*.dll" ".\gudhi\" + cp "C:\vcpkg\installed\x64-windows\bin\gmp*.dll" ".\gudhi\" python setup.py bdist_wheel - ls dist + ls ".\dist\" cd ".\dist\" Get-ChildItem *.whl | ForEach-Object{python -m pip install --user $_.Name} - name: Test python wheel diff --git a/.github/workflows/pip-packaging-windows.yml b/.github/workflows/pip-packaging-windows.yml index 962ae68a..142a114c 100644 --- a/.github/workflows/pip-packaging-windows.yml +++ b/.github/workflows/pip-packaging-windows.yml @@ -33,14 +33,14 @@ jobs: run: | mkdir build cd ".\build\" - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. + cmake -DCMAKE_BUILD_TYPE=Release -DFORCE_EIGEN_DEFAULT_DENSE_INDEX_TYPE_TO_INT=ON -DCMAKE_TOOLCHAIN_FILE=c:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. Get-Location dir cd ".\src\python\" - cp "C:\vcpkg\installed\x64-windows\bin\mpfr-6.dll" ".\gudhi\" - cp "C:\vcpkg\installed\x64-windows\bin\gmp.dll" ".\gudhi\" + cp "C:\vcpkg\installed\x64-windows\bin\mpfr*.dll" ".\gudhi\" + cp "C:\vcpkg\installed\x64-windows\bin\gmp*.dll" ".\gudhi\" python setup.py bdist_wheel - ls dist + ls ".\dist\" cd ".\dist\" Get-ChildItem *.whl | ForEach-Object{python -m pip install --user $_.Name} - name: Test python wheel diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 21664244..a39f6d5c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,7 +30,7 @@ jobs: - bash: | mkdir build cd build - cmake -DCMAKE_BUILD_TYPE:STRING=$(cmakeBuildType) -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=ON .. + cmake -DCMAKE_BUILD_TYPE:STRING=$(cmakeBuildType) -DWITH_GUDHI_EXAMPLE=ON -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=ON .. make make doxygen ctest --output-on-failure @@ -64,20 +64,27 @@ jobs: vcpkg install boost-filesystem:x64-windows boost-test:x64-windows boost-program-options:x64-windows tbb:x64-windows eigen3:x64-windows cgal:x64-windows displayName: 'Install build dependencies' - script: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 + IF %errorlevel% NEQ 0 exit /b %errorlevel% mkdir build cd build - cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release $(cmakeVcpkgFlags) $(cmakeFlags) .. + cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release -DFORCE_EIGEN_DEFAULT_DENSE_INDEX_TYPE_TO_INT=ON $(cmakeVcpkgFlags) $(cmakeFlags) .. + IF %errorlevel% NEQ 0 exit /b %errorlevel% MSBuild GUDHIdev.sln /m /p:Configuration=Release /p:Platform=x64 + IF %errorlevel% NEQ 0 exit /b %errorlevel% ctest --output-on-failure -C Release -E diff_files + IF %errorlevel% NEQ 0 exit /b %errorlevel% cmake -DWITH_GUDHI_PYTHON=ON . + IF %errorlevel% NEQ 0 exit /b %errorlevel% cd src\python - copy "C:\vcpkg\installed\x64-windows\bin\mpfr-6.dll" ".\gudhi\" - copy "C:\vcpkg\installed\x64-windows\bin\gmp.dll" ".\gudhi\" + copy "C:\vcpkg\installed\x64-windows\bin\mpfr*.dll" ".\gudhi\" + copy "C:\vcpkg\installed\x64-windows\bin\gmp*.dll" ".\gudhi\" copy "C:\vcpkg\installed\x64-windows\bin\tbb.dll" ".\gudhi\" copy "C:\vcpkg\installed\x64-windows\bin\tbbmalloc.dll" ".\gudhi\" python setup.py build_ext --inplace + IF %errorlevel% NEQ 0 exit /b %errorlevel% SET PYTHONPATH=%CD%;%PYTHONPATH% echo %PYTHONPATH% ctest --output-on-failure -C Release + IF %errorlevel% NEQ 0 exit /b %errorlevel% displayName: 'Build and test' diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 7c982b3b..6a94d1f5 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -19,6 +19,15 @@ if(GMP_FOUND) endif() endif() +# from windows vcpkg eigen 3.4.0#2 : build fails with +# error C2440: '': cannot convert from 'Eigen::EigenBase::Index' to '__gmp_expr' +# cf. https://gitlab.com/libeigen/eigen/-/issues/2476 +# Workaround is to compile with '-DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int' +if (FORCE_EIGEN_DEFAULT_DENSE_INDEX_TYPE_TO_INT) + message("++ User explicit demand to force EIGEN_DEFAULT_DENSE_INDEX_TYPE to int") + add_definitions(-DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int) +endif() + # In CMakeLists.txt, when include(${CGAL_USE_FILE}), CMAKE_CXX_FLAGS are overwritten. # cf. http://doc.cgal.org/latest/Manual/installation.html#title40 # A workaround is to include(${CGAL_USE_FILE}) before adding "-std=c++11". diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index fbfb0d1f..e31af02e 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -180,6 +180,15 @@ if(PYTHONINTERP_FOUND) set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}'alpha_complex', ") endif () + # from windows vcpkg eigen 3.4.0#2 : build fails with + # error C2440: '': cannot convert from 'Eigen::EigenBase::Index' to '__gmp_expr' + # cf. https://gitlab.com/libeigen/eigen/-/issues/2476 + # Workaround is to compile with '-DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int' + if (FORCE_EIGEN_DEFAULT_DENSE_INDEX_TYPE_TO_INT) + set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DEIGEN_DEFAULT_DENSE_INDEX_TYPE=int', ") + endif() + + add_gudhi_debug_info("Boost version ${Boost_VERSION}") if(CGAL_FOUND) if(NOT CGAL_VERSION VERSION_LESS 5.3.0) @@ -215,13 +224,14 @@ if(PYTHONINTERP_FOUND) endif(NOT GMP_LIBRARIES_DIR) add_GUDHI_PYTHON_lib_dir(${GMP_LIBRARIES_DIR}) message("** Add gmp ${GMP_LIBRARIES_DIR}") + # When FORCE_CGAL_NOT_TO_BUILD_WITH_GMPXX is set, not defining CGAL_USE_GMPXX is sufficient enough if(GMPXX_FOUND) add_gudhi_debug_info("GMPXX_LIBRARIES = ${GMPXX_LIBRARIES}") set(GUDHI_PYTHON_EXTRA_COMPILE_ARGS "${GUDHI_PYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMPXX', ") add_GUDHI_PYTHON_lib("${GMPXX_LIBRARIES}") add_GUDHI_PYTHON_lib_dir(${GMPXX_LIBRARIES_DIR}) message("** Add gmpxx ${GMPXX_LIBRARIES_DIR}") - endif(GMPXX_FOUND) + endif() endif(GMP_FOUND) if(MPFR_FOUND) add_gudhi_debug_info("MPFR_LIBRARIES = ${MPFR_LIBRARIES}") -- cgit v1.2.3