diff options
Diffstat (limited to 'src/python')
-rw-r--r-- | src/python/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/python/doc/rips_complex_ref.rst | 13 | ||||
-rw-r--r-- | src/python/doc/rips_complex_sum.inc | 3 | ||||
-rw-r--r-- | src/python/doc/rips_complex_user.rst | 24 | ||||
-rw-r--r-- | src/python/gudhi/dtm_rips_complex.py | 51 | ||||
-rw-r--r-- | src/python/test/test_dtm_rips_complex.py | 32 | ||||
-rw-r--r-- | src/python/test/test_weighted_rips_complex.py (renamed from src/python/test/test_weighted_rips.py) | 0 |
7 files changed, 132 insertions, 1 deletions
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index ab08cd6d..96dd3f6f 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -58,6 +58,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}") @@ -234,6 +235,7 @@ if(PYTHONINTERP_FOUND) file(COPY "gudhi/wasserstein" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") 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 @@ -492,9 +494,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/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 |