From bc2bc293069305562883f206cfedfc719bf5d2de Mon Sep 17 00:00:00 2001 From: REDS institute Date: Fri, 28 Aug 2020 15:25:28 +0200 Subject: [PATCH] Update gudhi bindings to latest changes on August 2020 (#468) * Pull latest changes on Gudhi-devel:giotto * Add Eigen as a submodule Eigen is now required for latest Gudhi version * Add Eigen for gudhi modules that require it * Update get_filtration and get_skeleton with the new Gudhi API * Update ccache in CI * Fix type issue for get_persistence in simplex_tree * Fix persistence in periodic cubical complex * Fix simplex tree * Fix cubical complex * Beautify setup.py * Move towards fixing #307 in azure_pipelines.yml (still not addressing docker_scripts.sh) Signed-off-by: julian Co-authored-by: Umberto Lupo --- .azure-ci/docker_scripts.sh | 5 +- .gitmodules | 3 + CMakeLists.txt | 11 ++ azure-pipelines.yml | 35 ++-- cmake/HelperBoost.cmake | 3 +- .../periodic_cubical_complex_bindings.cpp | 3 +- .../persistent_cohomology_bindings.cpp | 4 +- gtda/externals/bindings/ripser_bindings.cpp | 1 - .../bindings/simplex_tree_bindings.cpp | 26 ++- gtda/externals/eigen | 1 + gtda/externals/gudhi-devel | 2 +- .../python/cubical_complex_interface.py | 6 +- .../periodic_cubical_complex_interface.py | 4 +- .../python/simplex_tree_interface.py | 114 ++++++------- setup.py | 157 +++++++++--------- 15 files changed, 200 insertions(+), 175 deletions(-) create mode 160000 gtda/externals/eigen diff --git a/.azure-ci/docker_scripts.sh b/.azure-ci/docker_scripts.sh index a665f7d59..8807deeea 100755 --- a/.azure-ci/docker_scripts.sh +++ b/.azure-ci/docker_scripts.sh @@ -25,7 +25,7 @@ tar -zxvf /boost_1_69_0.tar.gz mkdir boost cd /boost_1_69_0 ./bootstrap.sh --prefix=/boost -./b2 install -j3 || echo "Parts of boost failed to build. Continuing.." +./b2 install -j3 || echo "Parts of boost failed to build. Continuing..." cd .. ccache -s @@ -36,6 +36,7 @@ export Boost_INCLUDE_DIR=/boost/include # Install dev environment cd /io +pip install wheel pip install -e ".[dev]" # Test dev install with pytest @@ -46,10 +47,10 @@ pip uninstall -y giotto-tda pip uninstall -y giotto-tda-nightly # Build wheels -pip install wheel==0.34.1 auditwheel==3.1.0 python setup.py bdist_wheel # Repair wheels with auditwheel +pip install auditwheel auditwheel repair dist/*whl -w dist/ # remove wheels that are not manylinux2010 rm -rf dist/*-linux*.whl diff --git a/.gitmodules b/.gitmodules index 7c18a5d56..443989d8f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ [submodule "gtda/externals/hera"] path = gtda/externals/hera url = https://github.com/grey-narn/hera +[submodule "gtda/externals/eigen"] + path = gtda/externals/eigen + url = https://gitlab.com/libeigen/eigen diff --git a/CMakeLists.txt b/CMakeLists.txt index eccc4b2b0..b6a514340 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ find_package(OpenMP) set(RIPSER_SRC_DIR "gtda/externals/ripser") set(GUDHI_SRC_DIR "gtda/externals/gudhi-devel/src") set(HERA_DIR "gtda/externals/hera") +set(EIGEN_DIR "gtda/externals/eigen") ####################################################################### # Ripser # @@ -171,6 +172,8 @@ target_include_directories(gtda_simplex_tree PRIVATE "${GUDHI_SRC_DIR}/Cech_comp target_include_directories(gtda_simplex_tree PRIVATE "${GUDHI_SRC_DIR}/Persistent_cohomology/include") target_include_directories(gtda_simplex_tree PRIVATE "${GUDHI_SRC_DIR}/Subsampling/include") target_include_directories(gtda_simplex_tree PRIVATE "${GUDHI_SRC_DIR}/python/include") +target_include_directories(gtda_simplex_tree PRIVATE "${GUDHI_SRC_DIR}/Collapse/include") +target_include_directories(gtda_simplex_tree PRIVATE "${EIGEN_DIR}") if(MSVC) target_compile_options(gtda_simplex_tree PUBLIC $<$: /O2 /Wall /fp:strict>) @@ -227,6 +230,8 @@ target_include_directories(gtda_witness_complex PRIVATE "${GUDHI_SRC_DIR}/Cech_c target_include_directories(gtda_witness_complex PRIVATE "${GUDHI_SRC_DIR}/Persistent_cohomology/include") target_include_directories(gtda_witness_complex PRIVATE "${GUDHI_SRC_DIR}/python/include") target_include_directories(gtda_witness_complex PRIVATE "${GUDHI_SRC_DIR}/common/include") +target_include_directories(gtda_witness_complex PRIVATE "${GUDHI_SRC_DIR}/Collapse/include") +target_include_directories(gtda_witness_complex PRIVATE "${EIGEN_DIR}") if(MSVC) target_compile_options(gtda_witness_complex PUBLIC $<$: /O2 /Wall /fp:strict>) @@ -256,6 +261,8 @@ target_include_directories(gtda_strong_witness_complex PRIVATE "${GUDHI_SRC_DIR} target_include_directories(gtda_strong_witness_complex PRIVATE "${GUDHI_SRC_DIR}/Persistent_cohomology/include") target_include_directories(gtda_strong_witness_complex PRIVATE "${GUDHI_SRC_DIR}/python/include") target_include_directories(gtda_strong_witness_complex PRIVATE "${GUDHI_SRC_DIR}/common/include") +target_include_directories(gtda_strong_witness_complex PRIVATE "${GUDHI_SRC_DIR}/Collapse/include") +target_include_directories(gtda_strong_witness_complex PRIVATE "${EIGEN_DIR}") if(MSVC) target_compile_options(gtda_strong_witness_complex PUBLIC $<$: /O2 /Wall /fp:strict>) @@ -286,6 +293,8 @@ target_include_directories(gtda_sparse_rips_complex PRIVATE "${GUDHI_SRC_DIR}/Pe target_include_directories(gtda_sparse_rips_complex PRIVATE "${GUDHI_SRC_DIR}/Rips_complex/include") target_include_directories(gtda_sparse_rips_complex PRIVATE "${GUDHI_SRC_DIR}/Subsampling/include") target_include_directories(gtda_sparse_rips_complex PRIVATE "${GUDHI_SRC_DIR}/python/include") +target_include_directories(gtda_sparse_rips_complex PRIVATE "${GUDHI_SRC_DIR}/Collapse/include") +target_include_directories(gtda_sparse_rips_complex PRIVATE "${EIGEN_DIR}") if(MSVC) target_compile_options(gtda_sparse_rips_complex PUBLIC $<$: /O2 /Wall /fp:strict>) @@ -314,6 +323,8 @@ target_include_directories(gtda_cech_complex PRIVATE "${GUDHI_SRC_DIR}/Cech_comp target_include_directories(gtda_cech_complex PRIVATE "${GUDHI_SRC_DIR}/Persistent_cohomology/include") target_include_directories(gtda_cech_complex PRIVATE "${GUDHI_SRC_DIR}/python/include") target_include_directories(gtda_cech_complex PRIVATE "${GUDHI_SRC_DIR}/common/include") +target_include_directories(gtda_cech_complex PRIVATE "${GUDHI_SRC_DIR}/Collapse/include") +target_include_directories(gtda_cech_complex PRIVATE "${EIGEN_DIR}") if(MSVC) target_compile_options(gtda_cech_complex PUBLIC $<$: /O2 /Wall /fp:strict>) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index be6ebfa22..8536b80de 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -41,7 +41,7 @@ jobs: - task: Cache@2 inputs: - key: '"ccache-wheels-v2020.08.08" | $(Agent.OS) | "$(python.version)"' + key: '"ccache-wheels-v2020.08.28" | $(Agent.OS) | "$(python.version)"' path: $(CCACHE_DIR) displayName: ccache @@ -63,12 +63,12 @@ jobs: - script: | set -e python -m pip install --upgrade pip - pip install dist/*manylinux2010*.whl + python -m pip install dist/*manylinux2010*.whl displayName: 'Install the wheels' - script: | set -e - pip install pytest pytest-cov pytest-azurepipelines pytest-benchmark hypothesis + python -m pip install pytest pytest-cov pytest-azurepipelines pytest-benchmark hypothesis mkdir tmp_test_cov cd tmp_test_cov pytest --pyargs gtda --ignore-glob='*externals*' --no-cov --no-coverage-upload @@ -76,8 +76,8 @@ jobs: - script: | set -e - pip install pandas openml matplotlib - pip install "papermill==1.2.1" + python -m pip install pandas openml matplotlib + python -m pip install "papermill==1.2.1" cd examples for n in *.ipynb do @@ -100,7 +100,7 @@ jobs: - bash: | set -e - pip install twine + python -m pip install twine twine upload -u giotto-learn -p $(pypi_psw) --skip-existing dist/*manylinux2010*.whl condition: eq(variables.nightlyRelease, true) displayName: 'Upload nightly wheels to PyPI' @@ -136,7 +136,7 @@ jobs: - task: Cache@2 inputs: - key: '"ccache-v2020.08.08" | $(Agent.OS) | "$(python.version)"' + key: '"ccache-v2020.08.28" | $(Agent.OS) | "$(python.version)"' path: $(CCACHE_DIR) displayName: ccache @@ -149,6 +149,7 @@ jobs: - script: | set -e python -m pip install --upgrade pip setuptools + python -m pip install wheel source .azure-ci/setup_ccache.sh python -m pip install -e ".[dev]" ccache -s @@ -172,11 +173,10 @@ jobs: - script: | set -e - pip install wheel python setup.py bdist_wheel displayName: 'Build the wheels' - - script: pip install dist/*.whl + - script: python -m pip install dist/*.whl displayName: 'Install the wheels' - script: | @@ -189,7 +189,7 @@ jobs: - script: | set -e python -m pip install -e ".[examples]" - pip install "papermill==1.2.1" + python -m pip install "papermill==1.2.1" cd examples for n in *.ipynb do @@ -205,7 +205,7 @@ jobs: - script: | set -e - pip install twine + python -m pip install twine twine check dist/* displayName: 'Check distribution with twine' @@ -257,6 +257,7 @@ jobs: - script: | python -m pip install --upgrade pip setuptools + python -m pip install wheel python -m pip install -e ".[dev]" displayName: 'Install dev environment' @@ -265,18 +266,17 @@ jobs: displayName: 'Test dev install with pytest' - script: | - pip uninstall -y giotto-tda - pip uninstall -y giotto-tda-nightly + python -m pip uninstall -y giotto-tda + python -m pip uninstall -y giotto-tda-nightly displayName: 'Uninstall giotto-tda/giotto-tda-nightly dev' - bash: | set -e sed -i $'s/\r$//' README.rst - pip install wheel python setup.py bdist_wheel displayName: 'Build the wheels' - - bash: pip install dist/*.whl + - bash: python -m pip install dist/*.whl displayName: 'Install the wheels' - script: | @@ -287,7 +287,7 @@ jobs: - script: | python -m pip install -e ".[examples]" - pip install "papermill==1.2.1" + python -m pip install "papermill==1.2.1" cd examples FOR %%n in (*.ipynb) DO (papermill --start_timeout 2000 %%n - || exit /b) condition: eq(variables['notebooks_check'], 'true') @@ -307,7 +307,8 @@ jobs: - bash: | set -e - pip install twine + python -m pip install twine twine upload -u giotto-learn -p $(pypi_psw) --skip-existing dist/* condition: eq(variables.nightlyRelease, true) displayName: 'Upload nightly wheels to PyPI' + diff --git a/cmake/HelperBoost.cmake b/cmake/HelperBoost.cmake index d3f19b12e..feaeff07f 100644 --- a/cmake/HelperBoost.cmake +++ b/cmake/HelperBoost.cmake @@ -1,6 +1,5 @@ - # Add to BOOST_ROOT variable a custom path to -# ease installation of giotto-tda on Windows platform +# ease installation of giotto-tda on Windows platforms # The custom path will be at `C:\\local\` if(WIN32) list(APPEND BOOST_ROOT "C:/local") diff --git a/gtda/externals/bindings/periodic_cubical_complex_bindings.cpp b/gtda/externals/bindings/periodic_cubical_complex_bindings.cpp index 92e62c862..a105e62d8 100644 --- a/gtda/externals/bindings/periodic_cubical_complex_bindings.cpp +++ b/gtda/externals/bindings/periodic_cubical_complex_bindings.cpp @@ -48,6 +48,8 @@ PYBIND11_MODULE(gtda_periodic_cubical_complex, m) { Bitmap_cubical_complex_periodic_boundary_conditions_base< double>>*, bool>()) + .def("compute_persistence", + &Persistent_cohomology_interface_inst::compute_persistence) .def("get_persistence", &Persistent_cohomology_interface_inst::get_persistence) .def("betti_numbers", @@ -58,4 +60,3 @@ PYBIND11_MODULE(gtda_periodic_cubical_complex, m) { &Persistent_cohomology_interface_inst::intervals_in_dimension); m.doc() = "GUDHI periocal cubical complex function interfacing"; } - diff --git a/gtda/externals/bindings/persistent_cohomology_bindings.cpp b/gtda/externals/bindings/persistent_cohomology_bindings.cpp index 1e3b804d5..a2db284bf 100644 --- a/gtda/externals/bindings/persistent_cohomology_bindings.cpp +++ b/gtda/externals/bindings/persistent_cohomology_bindings.cpp @@ -20,6 +20,8 @@ PYBIND11_MODULE(gtda_persistent_cohomology, m) { .def(py::init*>()) .def(py::init*, bool>()) + .def("compute_persistence", + &Persistent_cohomology_interface_inst::compute_persistence) .def("get_persistence", &Persistent_cohomology_interface_inst::get_persistence) .def("betti_numbers", @@ -28,5 +30,5 @@ PYBIND11_MODULE(gtda_persistent_cohomology, m) { &Persistent_cohomology_interface_inst::persistent_betti_numbers) .def("intervals_in_dimension", &Persistent_cohomology_interface_inst::intervals_in_dimension); - m.doc() = "GUDHI persistant homology interfacing"; + m.doc() = "GUDHI persistent homology interfacing"; } diff --git a/gtda/externals/bindings/ripser_bindings.cpp b/gtda/externals/bindings/ripser_bindings.cpp index 502bd1c5e..a6ae892ed 100644 --- a/gtda/externals/bindings/ripser_bindings.cpp +++ b/gtda/externals/bindings/ripser_bindings.cpp @@ -52,4 +52,3 @@ PYBIND11_MODULE(gtda_ripser, m) { "I"_a, "J"_a, "V"_a, "NEdges"_a, "N"_a, "modulus"_a, "dim_max"_a, "threshold"_a, "do_cocycles"_a, "ripser sparse distance matrix"); } - diff --git a/gtda/externals/bindings/simplex_tree_bindings.cpp b/gtda/externals/bindings/simplex_tree_bindings.cpp index 230c6ef3d..502a27a62 100644 --- a/gtda/externals/bindings/simplex_tree_bindings.cpp +++ b/gtda/externals/bindings/simplex_tree_bindings.cpp @@ -3,8 +3,8 @@ * License: Apache 2.0 *****************************************************************************/ - #include + #include #include @@ -41,8 +41,26 @@ PYBIND11_MODULE(gtda_simplex_tree, m) { const std::vector&, double>( &simplex_tree_interface_inst::insert_simplex_and_subfaces)) - .def("get_filtration", &simplex_tree_interface_inst::get_filtration) - .def("get_skeleton", &simplex_tree_interface_inst::get_skeleton) + .def("get_filtration", + [](simplex_tree_interface_inst& self) + -> std::vector { + std::vector tmp; + for (auto elem = self.get_filtration_iterator_begin(); + elem != self.get_filtration_iterator_end(); elem++) + tmp.push_back(self.get_simplex_and_filtration(*elem)); + return tmp; + }) + .def("get_skeleton", + [](simplex_tree_interface_inst& self, size_t dim) + -> std::vector< + simplex_tree_interface_inst::Simplex_and_filtration> { + std::vector + tmp; + for (auto elem = self.get_skeleton_iterator_begin(dim); + elem != self.get_skeleton_iterator_end(dim); elem++) + tmp.push_back(self.get_simplex_and_filtration(*elem)); + return tmp; + }) .def("get_star", &simplex_tree_interface_inst::get_star) .def("get_cofaces", &simplex_tree_interface_inst::get_cofaces) .def("expansion", &simplex_tree_interface_inst::expansion) @@ -59,6 +77,8 @@ PYBIND11_MODULE(gtda_simplex_tree, m) { py::class_( m, "Simplex_tree_persistence_interface") .def(py::init()) + .def("compute_persistence", + &Persistent_cohomology_interface_inst::compute_persistence) .def("get_persistence", &Persistent_cohomology_interface_inst::get_persistence) .def("betti_numbers", diff --git a/gtda/externals/eigen b/gtda/externals/eigen new file mode 160000 index 000000000..25424d91f --- /dev/null +++ b/gtda/externals/eigen @@ -0,0 +1 @@ +Subproject commit 25424d91f60a9f858e7dc1c7936021cc1dd72019 diff --git a/gtda/externals/gudhi-devel b/gtda/externals/gudhi-devel index a5476516e..a265b030e 160000 --- a/gtda/externals/gudhi-devel +++ b/gtda/externals/gudhi-devel @@ -1 +1 @@ -Subproject commit a5476516e0d1d56842a15c5a79af0df3c1e50c5b +Subproject commit a265b030effa9b34a99a09b0e1b5073e8bb50cb6 diff --git a/gtda/externals/python/cubical_complex_interface.py b/gtda/externals/python/cubical_complex_interface.py index c42d3e50b..3ceab973c 100644 --- a/gtda/externals/python/cubical_complex_interface.py +++ b/gtda/externals/python/cubical_complex_interface.py @@ -100,9 +100,9 @@ def persistence(self, homology_coeff_field=11, min_persistence=0): True) persistence_result = [] if self.pcohptr is not None: - persistence_result = self.pcohptr.get_persistence( - homology_coeff_field, min_persistence) - + self.pcohptr.compute_persistence(homology_coeff_field, + min_persistence) + persistence_result = self.pcohptr.get_persistence() return persistence_result def betti_numbers(self): diff --git a/gtda/externals/python/periodic_cubical_complex_interface.py b/gtda/externals/python/periodic_cubical_complex_interface.py index 01cddaaf5..6df2a4d95 100644 --- a/gtda/externals/python/periodic_cubical_complex_interface.py +++ b/gtda/externals/python/periodic_cubical_complex_interface.py @@ -100,9 +100,9 @@ def persistence(self, homology_coeff_field=11, min_persistence=0): True) persistence_result = [] if self.pcohptr is not None: - persistence_result = \ - self.pcohptr.get_persistence(homology_coeff_field, + self.pcohptr.compute_persistence(homology_coeff_field, min_persistence) + persistence_result = self.pcohptr.get_persistence() return persistence_result def betti_numbers(self): diff --git a/gtda/externals/python/simplex_tree_interface.py b/gtda/externals/python/simplex_tree_interface.py index 92bb83770..6edcb5700 100644 --- a/gtda/externals/python/simplex_tree_interface.py +++ b/gtda/externals/python/simplex_tree_interface.py @@ -16,8 +16,7 @@ class SimplexTree: # Fake constructor that does nothing but documenting the constructor def __init__(self): - """SimplexTree constructor. - """ + "SimplexTree constructor." self.thisptr = Simplex_tree_interface_full_featured() self.pcohptr = None @@ -28,22 +27,20 @@ def __del__(self): del self.pcohptr def __is_defined(self): - """Returns true if SimplexTree pointer is not NULL. - """ + "Return True if SimplexTree pointer is not NULL." if self.thisptr is not None: return True return False def __is_persistence_defined(self): - """Returns true if Persistence pointer is not NULL. - """ + """Return True if Persistence pointer is not NULL.""" if self.pcohptr is not None: return True return False 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. + """Return the filtration value for a given N-simplex in this simplicial + complex, or +infinity if it is not in the complex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. :returns: The simplicial complex filtration value. @@ -52,8 +49,8 @@ def filtration(self, simplex): return self.thisptr.simplex_filtration(simplex) def assign_filtration(self, simplex, filtration): - """This function assigns the simplicial complex filtration value for a - given N-simplex. + """Assign the simplicial complex filtration value for a given + N-simplex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. :param filtration: The simplicial complex filtration value. @@ -62,8 +59,7 @@ def assign_filtration(self, simplex, filtration): self.thisptr.assign_simplex_filtration(simplex, filtration) def initialize_filtration(self): - """This function initializes and sorts the simplicial complex - filtration vector. + """Initialize and sort the simplicial complex filtration vector. .. note:: This function must be launched before :func:`persistence()`, @@ -77,23 +73,21 @@ def initialize_filtration(self): self.thisptr.initialize_filtration() def num_vertices(self): - """This function returns the number of vertices of the simplicial - complex. + """Return the number of vertices of the simplicial complex. :returns: The simplicial complex number of vertices. :rtype: int """ return self.thisptr.num_vertices() def num_simplices(self): - """This function returns the number of simplices of the simplicial - complex. + """Return the number of simplices of the simplicial complex. :returns: the simplicial complex number of simplices. :rtype: int """ return self.thisptr.num_simplices() def dimension(self): - """This function returns the dimension of the simplicial complex. + """Return the dimension of the simplicial complex. :returns: the simplicial complex dimension. :rtype: int .. note:: @@ -107,15 +101,14 @@ def dimension(self): return self.thisptr.dimension() def upper_bound_dimension(self): - """This function returns a valid dimension upper bound of the - simplicial complex. + """Return a valid dimension upper bound of the simplicial complex. :returns: an upper bound on the dimension of the simplicial complex. :rtype: int """ return self.thisptr.upper_bound_dimension() def set_dimension(self, dimension): - """This function sets the dimension of the simplicial complex. + """Set the dimension of the simplicial complex. :param dimension: The new dimension value. :type dimension: int. .. note:: @@ -130,8 +123,7 @@ def set_dimension(self, dimension): self.thisptr.set_dimension(dimension) def find(self, simplex): - """This function returns if the N-simplex was found in the simplicial - complex or not. + """Return if the N-simplex was found in the simplicial complex or not. :param simplex: The N-simplex to find, represented by a list of vertex. :type simplex: list of int. :returns: true if the simplex was found, false otherwise. @@ -141,10 +133,10 @@ def find(self, simplex): return self.thisptr.find_simplex(csimplex) def insert(self, simplex, filtration=0.0): - """This function inserts the given N-simplex and its subfaces with the - given filtration value (default value is '0.0'). If some of those - simplices are already present with a higher filtration value, their - filtration value is lowered. + """Insert the given N-simplex and its subfaces with the given + filtration value (default value is '0.0'). If some of those simplices + are already present with a higher filtration value, their filtration + value is lowered. :param simplex: The N-simplex to insert, represented by a list of vertex. :type simplex: list of int. @@ -159,8 +151,7 @@ def insert(self, simplex, filtration=0.0): filtration) def get_filtration(self): - """This function returns a list of all simplices with their given - filtration values. + """Return a list of all simplices with their given filtration values. :returns: The simplices sorted by increasing filtration values. :rtype: list of tuples(simplex, filtration) """ @@ -172,8 +163,7 @@ def get_filtration(self): return ct def get_skeleton(self, dimension): - """This function returns the (simplices of the) skeleton of a maximum - given dimension. + """Return the (simplices of the) skeleton of a maximum given dimension. :param dimension: The skeleton dimension value. :type dimension: int. :returns: The (simplices of the) skeleton of a maximum dimension. @@ -187,7 +177,7 @@ def get_skeleton(self, dimension): return ct def get_star(self, simplex): - """This function returns the star of a given N-simplex. + """Return the star of a given N-simplex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. :returns: The (simplices of the) star of a simplex. @@ -202,8 +192,7 @@ def get_star(self, simplex): return ct def get_cofaces(self, simplex, codimension): - """This function returns the cofaces of a given N-simplex with a - given codimension. + """Return the cofaces of a given N-simplex with a given codimension. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. :param codimension: The codimension. If codimension = 0, all cofaces @@ -221,13 +210,13 @@ def get_cofaces(self, simplex, codimension): return ct def remove_maximal_simplex(self, simplex): - """This function removes a given maximal N-simplex from the simplicial - complex. + """Remove a given maximal N-simplex from the simplicial complex. :param simplex: The N-simplex, represented by a list of vertex. :type simplex: list of int. .. note:: Be aware that removing is shifting data in a flat_map - (:func:`initialize_filtration()` to be done). + (:func:`initialize_filtration()` + to be done). .. note:: The dimension of the simplicial complex may be lower after calling remove_maximal_simplex than it was before. However, @@ -267,8 +256,8 @@ def prune_above_filtration(self, filtration): return self.thisptr.prune_above_filtration(filtration) def expansion(self, max_dim): - """Expands the Simplex_tree containing only its one skeleton - until dimension max_dim. + """Expand 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 @@ -283,8 +272,8 @@ def expansion(self, max_dim): self.thisptr.expansion(max_dim) def make_filtration_non_decreasing(self): - """This function ensures that each simplex has a higher filtration - value than its faces by increasing the filtration values. + """Ensure that each simplex has a higher filtration value than its + faces by increasing the filtration values. :returns: True if any filtration value was modified, False if the filtration was already non-decreasing. :rtype: bool @@ -300,8 +289,8 @@ def make_filtration_non_decreasing(self): return self.thisptr.make_filtration_non_decreasing() def persistence(self, homology_coeff_field=11, min_persistence=0, - persistence_dim_max = False): - """This function returns the persistence of the simplicial complex. + persistence_dim_max=False): + """Return the persistence of the simplicial complex. :param homology_coeff_field: The homology coefficient field. Must be a prime number. Default value is 11. :type homology_coeff_field: int. @@ -323,13 +312,13 @@ def persistence(self, homology_coeff_field=11, min_persistence=0, persistence_dim_max) persistence_result = [] if self.pcohptr is not None: - persistence_result = \ - self.pcohptr.get_persistence(homology_coeff_field, + self.pcohptr.compute_persistence(homology_coeff_field, min_persistence) + persistence_result = self.pcohptr.get_persistence() return persistence_result def betti_numbers(self): - """This function returns the Betti numbers of the simplicial complex. + """Return the Betti numbers of the simplicial complex. :returns: The Betti numbers ([B0, B1, ..., Bn]). :rtype: list of int :note: betti_numbers function requires @@ -340,13 +329,12 @@ def betti_numbers(self): if self.pcohptr is not None: bn_result = self.pcohptr.betti_numbers() else: - print("betti_numbers function requires persistence function" - " to be launched first.") + print("`betti_numbers` requires persistence function to be " + "launched first.") return bn_result def persistent_betti_numbers(self, from_value, to_value): - """This function returns the persistent Betti numbers of the - simplicial complex. + """Return the persistent Betti numbers of the simplicial complex. :param from_value: The persistence birth limit to be added in the numbers (persistent birth <= from_value). :type from_value: float. @@ -364,13 +352,13 @@ def persistent_betti_numbers(self, from_value, to_value): pbn_result = self.pcohptr.persistent_betti_numbers(from_value, to_value) else: - print("persistent_betti_numbers function requires persistence function" - " to be launched first.") + print("`persistent_betti_numbers` requires persistence function " + "to be launched first.") return pbn_result def persistence_intervals_in_dimension(self, dimension): - """This function returns the persistence intervals of the simplicial - complex in a specific dimension. + """Return the persistence intervals of the simplicial complex in a + specific dimension. :param dimension: The specific dimension. :type dimension: int. :returns: The persistence intervals. @@ -383,12 +371,12 @@ def persistence_intervals_in_dimension(self, dimension): if self.pcohptr is not None: intervals_result = self.pcohptr.intervals_in_dimension(dimension) else: - print("intervals_in_dim function requires persistence function" - " to be launched first.") + print("`intervals_in_dim` requires persistence function to be " + "launched first.") return np.array(intervals_result) def persistence_pairs(self): - """This function returns a list of persistence birth and death simplices pairs. + """Return a list of persistence birth and death simplex pairs. :returns: A list of persistence simplices intervals. :rtype: list of pair of list of int :note: persistence_pairs function requires @@ -399,13 +387,13 @@ def persistence_pairs(self): if self.pcohptr is not None: persistence_pairs_result = self.pcohptr.persistence_pairs() else: - print("persistence_pairs function requires persistence function" - " to be launched first.") + print("`persistence_pairs` requires persistence function to be " + "launched first.") return persistence_pairs_result def write_persistence_diagram(self, persistence_file=''): - """This function writes the persistence intervals of the simplicial - complex in a user given file name. + """Write the persistence intervals of the simplicial complex in a + user-given file name. :param persistence_file: The specific dimension. :type persistence_file: string. :note: intervals_in_dim function requires @@ -416,7 +404,7 @@ def write_persistence_diagram(self, persistence_file=''): if persistence_file != '': self.pcohptr.write_output_diagram(str.encode(persistence_file)) else: - print("persistence_file must be specified") + print("`persistence_file` must be specified") else: - print("intervals_in_dim function requires persistence function" - " to be launched first.") + print("`intervals_in_dim` requires persistence function to be " + "launched first.") diff --git a/setup.py b/setup.py index 8586ac9c4..e9c328286 100755 --- a/setup.py +++ b/setup.py @@ -13,68 +13,67 @@ from setuptools.command.build_ext import build_ext -version_file = os.path.join('gtda', '_version.py') +version_file = os.path.join("gtda", "_version.py") with open(version_file) as f: exec(f.read()) -with open('requirements.txt') as f: +with open("requirements.txt") as f: requirements = f.read().splitlines() -DISTNAME = 'giotto-tda' -DESCRIPTION = 'Toolbox for Machine Learning using Topological Data Analysis.' -with codecs.open('README.rst', encoding='utf-8-sig') as f: +DISTNAME = "giotto-tda" +DESCRIPTION = "Toolbox for Machine Learning using Topological Data Analysis." +with codecs.open("README.rst", encoding="utf-8-sig") as f: LONG_DESCRIPTION = f.read() -LONG_DESCRIPTION_TYPE = 'text/x-rst' -MAINTAINER = 'Umberto Lupo, Lewis Tunstall' -MAINTAINER_EMAIL = 'maintainers@giotto.ai' -URL = 'https://github.com/giotto-ai/giotto-tda' -LICENSE = 'GNU AGPLv3' -DOWNLOAD_URL = 'https://github.com/giotto-ai/giotto-tda/tarball/v0.2.2' +LONG_DESCRIPTION_TYPE = "text/x-rst" +MAINTAINER = "Umberto Lupo, Lewis Tunstall" +MAINTAINER_EMAIL = "maintainers@giotto.ai" +URL = "https://github.com/giotto-ai/giotto-tda" +LICENSE = "GNU AGPLv3" +DOWNLOAD_URL = "https://github.com/giotto-ai/giotto-tda/tarball/v0.2.2" VERSION = __version__ # noqa -CLASSIFIERS = ['Intended Audience :: Science/Research', - 'Intended Audience :: Developers', - 'License :: OSI Approved', - 'Programming Language :: C++', - 'Programming Language :: Python', - 'Topic :: Software Development', - 'Topic :: Scientific/Engineering', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8'] -KEYWORDS = 'machine learning, topological data analysis, persistent ' + \ - 'homology, persistence diagrams, Mapper' +CLASSIFIERS = ["Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "License :: OSI Approved", + "Programming Language :: C++", + "Programming Language :: Python", + "Topic :: Software Development", + "Topic :: Scientific/Engineering", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8"] +KEYWORDS = "machine learning, topological data analysis, persistent " \ + "homology, persistence diagrams, Mapper" INSTALL_REQUIRES = requirements EXTRAS_REQUIRE = { - 'tests': [ - 'pytest', - 'pytest-cov', - 'pytest-azurepipelines', - 'pytest-benchmark', - 'jupyter_contrib_nbextensions', - 'flake8', - 'hypothesis'], - 'doc': [ - 'openml', - 'sphinx', - 'nbconvert', - 'sphinx-issues', - 'sphinx_rtd_theme', - 'numpydoc'], - 'examples': [ - 'jupyter', - 'pandas', - 'openml', - 'matplotlib'] + "tests": [ + "pytest", + "pytest-cov", + "pytest-azurepipelines", + "pytest-benchmark", + "jupyter_contrib_nbextensions", + "flake8", + "hypothesis"], + "doc": [ + "openml", + "sphinx", + "nbconvert", + "sphinx-issues", + "sphinx_rtd_theme", + "numpydoc"], + "examples": [ + "jupyter", + "pandas", + "openml", + "matplotlib"] } def combine_requirements(base_keys): - return list( - set(k for v in base_keys for k in EXTRAS_REQUIRE[v])) + return list(set(k for v in base_keys for k in EXTRAS_REQUIRE[v])) EXTRAS_REQUIRE["dev"] = combine_requirements( @@ -82,7 +81,7 @@ def combine_requirements(base_keys): class CMakeExtension(Extension): - def __init__(self, name, sourcedir=''): + def __init__(self, name, sourcedir=""): Extension.__init__(self, name, sources=[]) self.sourcedir = os.path.abspath(sourcedir) @@ -90,16 +89,17 @@ def __init__(self, name, sourcedir=''): class CMakeBuild(build_ext): def run(self): try: - out = subprocess.check_output(['cmake', '--version']) + out = subprocess.check_output(["cmake", "--version"]) except OSError: - raise RuntimeError("CMake must be installed to build the " - " following extensions: " + - " , ".join(e.name for e in self.extensions)) + raise RuntimeError( + f"CMake must be installed to build the following extensions: " + f"{', '.join(e.name for e in self.extensions)}" + ) if platform.system() == "Windows": - cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', + cmake_version = LooseVersion(re.search(r"version\s*([\d.]+)", out.decode()).group(1)) - if cmake_version < '3.1.0': + if cmake_version < "3.1.0": raise RuntimeError("CMake >= 3.1.0 is required on Windows") self.install_dependencies() @@ -109,45 +109,44 @@ def run(self): def install_dependencies(self): dir_start = os.getcwd() - dir_pybind11 = os.path.join(dir_start, - 'gtda', 'externals', 'pybind11') + dir_pybind11 = os.path.join(dir_start, "gtda", "externals", "pybind11") if os.path.exists(dir_pybind11): return 0 os.mkdir(dir_pybind11) - subprocess.check_call(['git', 'clone', '--branch', 'v2.5.0', - 'https://github.com/pybind/pybind11.git', + subprocess.check_call(["git", "clone", "--branch", "v2.5.0", + "https://github.com/pybind/pybind11.git", dir_pybind11]) - subprocess.check_call(['git', 'submodule', 'update', - '--init', '--recursive']) + subprocess.check_call(["git", "submodule", "update", + "--init", "--recursive"]) def build_extension(self, ext): extdir = os.path.abspath(os.path.join(os.path.dirname( - self.get_ext_fullpath(ext.name)), 'gtda', 'externals', 'modules')) - cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - '-DPYTHON_EXECUTABLE=' + sys.executable] + self.get_ext_fullpath(ext.name)), "gtda", "externals", "modules")) + cmake_args = [f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}", + f"-DPYTHON_EXECUTABLE={sys.executable}"] - cfg = 'Debug' if self.debug else 'Release' - build_args = ['--config', cfg] + cfg = "Debug" if self.debug else "Release" + build_args = ["--config", cfg] - if platform.system() == 'Windows': - cmake_args += [f'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}' - f'={extdir}'] + if platform.system() == "Windows": + cmake_args += [f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}" + f"={extdir}"] if sys.maxsize > 2**32: - cmake_args += ['-A', 'x64'] - build_args += ['--', '/m'] + cmake_args += ["-A", "x64"] + build_args += ["--", "/m"] else: - cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] - build_args += ['--', '-j2'] + cmake_args += [f"-DCMAKE_BUILD_TYPE={cfg}"] + build_args += ["--", "-j2"] env = os.environ.copy() - env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( - env.get('CXXFLAGS', ''), self.distribution.get_version()) + env["CXXFLAGS"] = f"{env.get('CXXFLAGS', '')} -DVERSION_INFO="\ + f"\\'{self.distribution.get_version()}\\'" if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) - subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, + subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) - subprocess.check_call(['cmake', '--build', '.'] + build_args, + subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=self.build_temp) @@ -167,5 +166,5 @@ def build_extension(self, ext): keywords=KEYWORDS, install_requires=INSTALL_REQUIRES, extras_require=EXTRAS_REQUIRE, - ext_modules=[CMakeExtension('gtda')], + ext_modules=[CMakeExtension("gtda")], cmdclass=dict(build_ext=CMakeBuild))