summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml5
-rw-r--r--azure-pipelines.yml2
-rw-r--r--src/cmake/modules/GUDHI_third_party_libraries.cmake2
-rw-r--r--src/cmake/modules/GUDHI_user_version_target.cmake13
-rw-r--r--src/python/CMakeLists.txt10
-rw-r--r--src/python/doc/nerve_gic_complex_user.rst5
-rwxr-xr-xsrc/python/doc/python3-sphinx-build.py11
-rw-r--r--src/python/doc/rips_complex_ref.rst13
-rw-r--r--src/python/doc/rips_complex_sum.inc3
-rw-r--r--src/python/doc/rips_complex_user.rst24
-rw-r--r--src/python/gudhi/dtm_rips_complex.py51
-rw-r--r--src/python/test/test_dtm_rips_complex.py32
-rw-r--r--src/python/test/test_weighted_rips_complex.py (renamed from src/python/test/test_weighted_rips.py)0
13 files changed, 149 insertions, 22 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 34f42dea..d072a366 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -48,8 +48,9 @@ install:
- python --version
- pip --version
- python -m pip install --upgrade pip
- - pip install -U setuptools numpy matplotlib scipy Cython pytest
- - pip install -U POT pybind11
+ - python -m pip install --user -r .github/build-requirements.txt
+ # No PyKeOps on windows, let's workaround this one.
+ - for /F "tokens=*" %%A in (.github/test-requirements.txt) do python -m pip install --user %%A
build_script:
- mkdir build
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 7b5334a7..29ec23d0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -33,5 +33,5 @@ jobs:
cmake -DCMAKE_BUILD_TYPE:STRING=$(cmakeBuildType) -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=ON -DPython_ADDITIONAL_VERSIONS=3 ..
make -j 4
make doxygen
- ctest -j 4 --output-on-failure -E sphinx # remove sphinx build as it fails
+ ctest -j 4 --output-on-failure # -E sphinx remove sphinx build as it fails
displayName: 'Build, test and documentation generation'
diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake
index 0abe66b7..f92fe93e 100644
--- a/src/cmake/modules/GUDHI_third_party_libraries.cmake
+++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake
@@ -199,7 +199,7 @@ if(PYTHONINTERP_FOUND AND CYTHON_FOUND)
if(NOT SPHINX_PATH)
if(PYTHON_VERSION_MAJOR EQUAL 3)
# In Python3, just hack sphinx-build if it does not exist
- set(SPHINX_PATH "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${GUDHI_PYTHON_PATH}/doc/python3-sphinx-build.py")
+ set(SPHINX_PATH "${PYTHON_EXECUTABLE}" "-m" "sphinx.cmd.build")
endif(PYTHON_VERSION_MAJOR EQUAL 3)
endif(NOT SPHINX_PATH)
endif(SPHINX_FOUND)
diff --git a/src/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake
index 9cf648e3..e99bb42d 100644
--- a/src/cmake/modules/GUDHI_user_version_target.cmake
+++ b/src/cmake/modules/GUDHI_user_version_target.cmake
@@ -49,8 +49,17 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
copy ${CMAKE_SOURCE_DIR}/CMakeGUDHIVersion.txt ${GUDHI_USER_VERSION_DIR}/CMakeGUDHIVersion.txt)
-add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
- copy_directory ${CMAKE_SOURCE_DIR}/${GUDHI_PYTHON_PATH} ${GUDHI_USER_VERSION_DIR}/python)
+# As cython generates .cpp files in source, we have to copy all except cpp files from python directory
+file(GLOB_RECURSE PYTHON_FILES ${CMAKE_SOURCE_DIR}/${GUDHI_PYTHON_PATH}/*)
+foreach(PYTHON_FILE ${PYTHON_FILES})
+ get_filename_component(PYTHON_FILE_EXT ${PYTHON_FILE} EXT)
+ if (NOT "${PYTHON_FILE_EXT}" STREQUAL ".cpp")
+ string(REPLACE "${CMAKE_SOURCE_DIR}/${GUDHI_PYTHON_PATH}/" "" RELATIVE_PYTHON_FILE ${PYTHON_FILE})
+ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
+ copy ${PYTHON_FILE} ${GUDHI_USER_VERSION_DIR}/python/${RELATIVE_PYTHON_FILE})
+ endif()
+endforeach()
+
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
copy_directory ${CMAKE_SOURCE_DIR}/data ${GUDHI_USER_VERSION_DIR}/data)
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
index 6939a4a6..55d05cae 100644
--- a/src/python/CMakeLists.txt
+++ b/src/python/CMakeLists.txt
@@ -59,6 +59,7 @@ if(PYTHONINTERP_FOUND)
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'point_cloud', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'weighted_rips_complex', ")
+ set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'dtm_rips_complex', ")
add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}")
add_gudhi_debug_info("Cython version ${CYTHON_VERSION}")
@@ -237,6 +238,7 @@ if(PYTHONINTERP_FOUND)
file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
file(COPY "gudhi/clustering" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py")
file(COPY "gudhi/weighted_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
+ file(COPY "gudhi/dtm_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
add_custom_command(
OUTPUT gudhi.so
@@ -500,9 +502,15 @@ if(PYTHONINTERP_FOUND)
# Weighted Rips
if(SCIPY_FOUND)
- add_gudhi_py_test(test_weighted_rips)
+ add_gudhi_py_test(test_weighted_rips_complex)
endif()
+ # DTM Rips
+ if(SCIPY_FOUND)
+ add_gudhi_py_test(test_dtm_rips_complex)
+ endif()
+
+
# Set missing or not modules
set(GUDHI_MODULES ${GUDHI_MODULES} "python" CACHE INTERNAL "GUDHI_MODULES")
else(CYTHON_FOUND)
diff --git a/src/python/doc/nerve_gic_complex_user.rst b/src/python/doc/nerve_gic_complex_user.rst
index 0e67fc78..0b820abf 100644
--- a/src/python/doc/nerve_gic_complex_user.rst
+++ b/src/python/doc/nerve_gic_complex_user.rst
@@ -50,7 +50,7 @@ The cover C comes from the preimages of intervals (10 intervals with gain 0.3)
covering the height function (coordinate 2),
which are then refined into their connected components using the triangulation of the .OFF file.
-.. testcode::
+.. code-block:: python
import gudhi
nerve_complex = gudhi.CoverComplex()
@@ -99,9 +99,6 @@ the program output is:
[-0.171433, 0.367393]
[-0.909111, 0.745853]
0 interval(s) in dimension 1:
-
-.. testoutput::
-
Nerve is of dimension 1 - 41 simplices - 21 vertices.
[0]
[1]
diff --git a/src/python/doc/python3-sphinx-build.py b/src/python/doc/python3-sphinx-build.py
deleted file mode 100755
index 84d158cf..00000000
--- a/src/python/doc/python3-sphinx-build.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Emulate sphinx-build for python3
-"""
-
-from sys import exit, argv
-from sphinx import main
-
-if __name__ == '__main__':
- exit(main(argv))
diff --git a/src/python/doc/rips_complex_ref.rst b/src/python/doc/rips_complex_ref.rst
index 5f3e46c1..2aa6b268 100644
--- a/src/python/doc/rips_complex_ref.rst
+++ b/src/python/doc/rips_complex_ref.rst
@@ -25,3 +25,16 @@ Weighted Rips complex reference manual
:show-inheritance:
.. automethod:: gudhi.weighted_rips_complex.WeightedRipsComplex.__init__
+
+.. _dtm-rips-complex-reference-manual:
+
+=================================
+DTM Rips complex reference manual
+=================================
+
+.. autoclass:: gudhi.dtm_rips_complex.DTMRipsComplex
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ .. automethod:: gudhi.dtm_rips_complex.DTMRipsComplex.__init__ \ No newline at end of file
diff --git a/src/python/doc/rips_complex_sum.inc b/src/python/doc/rips_complex_sum.inc
index f7580714..9cd8074b 100644
--- a/src/python/doc/rips_complex_sum.inc
+++ b/src/python/doc/rips_complex_sum.inc
@@ -14,6 +14,9 @@
| | | |
| | Weighted Rips complex constructs a simplicial complex from a distance | |
| | matrix and weights on vertices. | |
+ | | | |
+ | | DTM Rips complex builds a simplicial complex from a point set or | |
+ | | a distance matrix. | |
+----------------------------------------------------------------+------------------------------------------------------------------------+----------------------------------------------------------------------+
| * :doc:`rips_complex_user` | * :doc:`rips_complex_ref` |
+----------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
diff --git a/src/python/doc/rips_complex_user.rst b/src/python/doc/rips_complex_user.rst
index 819568be..dd2f2cc0 100644
--- a/src/python/doc/rips_complex_user.rst
+++ b/src/python/doc/rips_complex_user.rst
@@ -378,6 +378,7 @@ Example from a point cloud combined with DistanceToMeasure
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Combining with DistanceToMeasure, one can compute the DTM-filtration of a point set, as in `this notebook <https://github.com/GUDHI/TDA-tutorial/blob/master/Tuto-GUDHI-DTM-filtrations.ipynb>`_.
+Remark that `DTMRipsComplex <rips_complex_user.html#dtm-rips-complex>`_ class provides exactly this function.
.. testcode::
@@ -398,3 +399,26 @@ The output is:
.. testoutput::
[(0, (3.1622776601683795, inf)), (0, (3.1622776601683795, 5.39834563766817)), (0, (3.1622776601683795, 5.39834563766817))]
+
+.. _dtm-rips-complex:
+
+DTM Rips Complex
+----------------
+
+`DTMRipsComplex <rips_complex_ref.html#dtm-rips-complex-reference-manual>`_ builds a simplicial complex from a point set or a full distance matrix (in the form of ndarray), as described in the above example.
+This class constructs a weighted Rips complex giving larger weights to outliers, which reduces their impact on the persistence diagram. See `this notebook <https://github.com/GUDHI/TDA-tutorial/blob/master/Tuto-GUDHI-DTM-filtrations.ipynb>`_ for some experiments.
+
+.. testcode::
+
+ import numpy as np
+ from gudhi.dtm_rips_complex import DTMRipsComplex
+ pts = np.array([[2.0, 2.0], [0.0, 1.0], [3.0, 4.0]])
+ dtm_rips = DTMRipsComplex(points=pts, k=2)
+ st = dtm_rips.create_simplex_tree(max_dimension=2)
+ print(st.persistence())
+
+The output is:
+
+.. testoutput::
+
+ [(0, (3.1622776601683795, inf)), (0, (3.1622776601683795, 5.39834563766817)), (0, (3.1622776601683795, 5.39834563766817))]
diff --git a/src/python/gudhi/dtm_rips_complex.py b/src/python/gudhi/dtm_rips_complex.py
new file mode 100644
index 00000000..63c9b138
--- /dev/null
+++ b/src/python/gudhi/dtm_rips_complex.py
@@ -0,0 +1,51 @@
+# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
+# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
+# Author(s): Yuichi Ike, Raphaƫl Tinarrage
+#
+# Copyright (C) 2020 Inria, Copyright (C) 2020 FUjitsu Laboratories Ltd.
+#
+# Modification(s):
+# - YYYY/MM Author: Description of the modification
+
+
+from gudhi.weighted_rips_complex import WeightedRipsComplex
+from gudhi.point_cloud.dtm import DistanceToMeasure
+from scipy.spatial.distance import cdist
+
+class DTMRipsComplex(WeightedRipsComplex):
+ """
+ Class to generate a DTM Rips complex from a distance matrix or a point set,
+ in the way described in :cite:`dtmfiltrations`.
+ Remark that all the filtration values are doubled compared to the definition in the paper
+ for the consistency with RipsComplex.
+ :Requires: `SciPy <installation.html#scipy>`_
+ """
+ def __init__(self,
+ points=None,
+ distance_matrix=None,
+ k=1,
+ q=2,
+ max_filtration=float('inf')):
+ """
+ Args:
+ points (numpy.ndarray): array of points.
+ distance_matrix (numpy.ndarray): full distance matrix.
+ k (int): number of neighbors for the computation of DTM. Defaults to 1, which is equivalent to the usual Rips complex.
+ q (float): order used to compute the distance to measure. Defaults to 2.
+ max_filtration (float): specifies the maximal filtration value to be considered.
+ """
+ if distance_matrix is None:
+ if points is None:
+ # Empty Rips construction
+ points=[]
+ distance_matrix = cdist(points,points)
+ self.distance_matrix = distance_matrix
+
+ # TODO: address the error when k is too large
+ if k <= 1:
+ self.weights = [0] * len(distance_matrix)
+ else:
+ dtm = DistanceToMeasure(k, q=q, metric="precomputed")
+ self.weights = dtm.fit_transform(distance_matrix)
+ self.max_filtration = max_filtration
+
diff --git a/src/python/test/test_dtm_rips_complex.py b/src/python/test/test_dtm_rips_complex.py
new file mode 100644
index 00000000..e1c0ee44
--- /dev/null
+++ b/src/python/test/test_dtm_rips_complex.py
@@ -0,0 +1,32 @@
+""" This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
+ See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
+ Author(s): Yuichi Ike
+
+ Copyright (C) 2020 Inria, Copyright (C) 2020 FUjitsu Laboratories Ltd.
+
+ Modification(s):
+ - YYYY/MM Author: Description of the modification
+"""
+
+from gudhi.dtm_rips_complex import DTMRipsComplex
+from gudhi import RipsComplex
+import numpy as np
+from math import sqrt
+import pytest
+
+def test_dtm_rips_complex():
+ pts = np.array([[2.0, 2.0], [0.0, 1.0], [3.0, 4.0]])
+ dtm_rips = DTMRipsComplex(points=pts, k=2)
+ st = dtm_rips.create_simplex_tree(max_dimension=2)
+ st.persistence()
+ persistence_intervals0 = st.persistence_intervals_in_dimension(0)
+ assert persistence_intervals0 == pytest.approx(np.array([[3.16227766, 5.39834564],[3.16227766, 5.39834564], [3.16227766, float("inf")]]))
+
+def test_compatibility_with_rips():
+ distance_matrix = np.array([[0, 1, 1, sqrt(2)], [1, 0, sqrt(2), 1], [1, sqrt(2), 0, 1], [sqrt(2), 1, 1, 0]])
+ dtm_rips = DTMRipsComplex(distance_matrix=distance_matrix, max_filtration=42)
+ st = dtm_rips.create_simplex_tree(max_dimension=1)
+ rips_complex = RipsComplex(distance_matrix=distance_matrix, max_edge_length=42)
+ st_from_rips = rips_complex.create_simplex_tree(max_dimension=1)
+ assert list(st.get_filtration()) == list(st_from_rips.get_filtration())
+
diff --git a/src/python/test/test_weighted_rips.py b/src/python/test/test_weighted_rips_complex.py
index 7ef48333..7ef48333 100644
--- a/src/python/test/test_weighted_rips.py
+++ b/src/python/test/test_weighted_rips_complex.py