diff options
author | Marc Glisse <marc.glisse@inria.fr> | 2020-05-29 17:58:18 +0200 |
---|---|---|
committer | Marc Glisse <marc.glisse@inria.fr> | 2020-05-29 17:58:18 +0200 |
commit | 02baef8170649f917882db727156636315bae8cf (patch) | |
tree | dd685c0ae0d6778c3fef95d87225449e78671204 /src/python | |
parent | 1218f4540d51859b9527d2dd436ea8c50c429d68 (diff) | |
parent | c53567c85f936f78000471fcee6234e75f7742ca (diff) |
Merge remote-tracking branch 'origin/master' into tomato2
Diffstat (limited to 'src/python')
-rw-r--r-- | src/python/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/python/doc/nerve_gic_complex_user.rst | 5 | ||||
-rwxr-xr-x | src/python/doc/python3-sphinx-build.py | 11 | ||||
-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 |
9 files changed, 133 insertions, 16 deletions
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 |