From 2871ff4d2bd23282ad2ee2925bea152262384641 Mon Sep 17 00:00:00 2001 From: vrouvrea Date: Thu, 9 Aug 2018 09:16:14 +0000 Subject: Make soft dependency for matplotlib and numpy dependencies git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/ninja_cmake_warning_fix_vincent@3756 636b058d-ea47-450e-bf9e-a15bfbe3eedb Former-commit-id: 13a2e6e3eea8a36d0b9bdb4ab1c34f940a8932dc --- src/cython/CMakeLists.txt | 104 ++++--- src/cython/cython/persistence_graphical_tools.py | 377 ++++++++++++----------- src/cython/setup.py.in | 6 +- 3 files changed, 249 insertions(+), 238 deletions(-) (limited to 'src/cython') diff --git a/src/cython/CMakeLists.txt b/src/cython/CMakeLists.txt index 8b5d231a..1849a6ec 100644 --- a/src/cython/CMakeLists.txt +++ b/src/cython/CMakeLists.txt @@ -16,11 +16,13 @@ endfunction( add_gudhi_cython_lib ) # THE_TEST is the python test file name (without .py extension) containing tests functions function( add_gudhi_py_test THE_TEST ) - # use ${PYTHON_EXECUTABLE} -B, otherwise a __pycache__ directory is created in sources by python - # use py.test no cache provider, otherwise a .cache file is created in sources by py.test - add_test(NAME ${THE_TEST}_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${PYTHON_EXECUTABLE} -B -m pytest -p no:cacheprovider ${CMAKE_CURRENT_SOURCE_DIR}/test/${THE_TEST}.py) + if(PYTEST_FOUND) + # use ${PYTHON_EXECUTABLE} -B, otherwise a __pycache__ directory is created in sources by python + # use py.test no cache provider, otherwise a .cache file is created in sources by py.test + add_test(NAME ${THE_TEST}_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${PYTHON_EXECUTABLE} -B -m pytest -p no:cacheprovider ${CMAKE_CURRENT_SOURCE_DIR}/test/${THE_TEST}.py) + endif() endfunction( add_gudhi_py_test ) # Set gudhi.__debug_info__ @@ -47,10 +49,10 @@ if(CYTHON_FOUND) add_gudhi_debug_info("Pytest version ${PYTEST_VERSION}") endif() if(MATPLOTLIB_FOUND) - add_gudhi_debug_info("Matplotlib version ${MATPLOTLIB_VERSION}") + add_gudhi_debug_info("Matplotlib version ${MATPLOTLIB_VERSION}") endif() if(NUMPY_FOUND) - add_gudhi_debug_info("Numpy version ${NUMPY_VERSION}") + add_gudhi_debug_info("Numpy version ${NUMPY_VERSION}") endif() if(MATPLOTLIB_FOUND AND NUMPY_FOUND) set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}persistence_graphical_tools;") @@ -213,27 +215,29 @@ if(CYTHON_FOUND) ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_rips_persistence_bottleneck_distance.py" -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) - # Tangential - add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/tangential_complex_plain_homology_from_off_file_example.py" - --no-diagram -i 2 -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off) - - add_gudhi_py_test(test_tangential_complex) - - # Witness complex AND Subsampling - add_test(NAME euclidean_strong_witness_complex_diagram_persistence_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py" - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2) - - add_test(NAME euclidean_witness_complex_diagram_persistence_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py" - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2) + if(MATPLOTLIB_FOUND AND NUMPY_FOUND) + # Tangential + add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/tangential_complex_plain_homology_from_off_file_example.py" + --no-diagram -i 2 -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off) + + add_gudhi_py_test(test_tangential_complex) + + # Witness complex AND Subsampling + add_test(NAME euclidean_strong_witness_complex_diagram_persistence_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py" + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2) + + add_test(NAME euclidean_witness_complex_diagram_persistence_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py" + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 1.0 -n 20 -d 2) + endif() # Subsampling add_gudhi_py_test(test_subsampling) @@ -287,11 +291,13 @@ if(CYTHON_FOUND) COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_from_points_example.py") - add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test + if(MATPLOTLIB_FOUND AND NUMPY_FOUND) + add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/alpha_complex_diagram_persistence_from_off_file_example.py" --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6) + endif() add_gudhi_py_test(test_alpha_complex) @@ -310,26 +316,30 @@ if(CYTHON_FOUND) ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/periodic_cubical_complex_barcode_persistence_from_perseus_file_example.py" --no-barcode -f ${CMAKE_SOURCE_DIR}/data/bitmap/CubicalTwoSphere.txt) - add_test(NAME random_cubical_complex_persistence_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/random_cubical_complex_persistence_example.py" - 10 10 10) + if(NUMPY_FOUND) + add_test(NAME random_cubical_complex_persistence_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/random_cubical_complex_persistence_example.py" + 10 10 10) + endif() add_gudhi_py_test(test_cubical_complex) # Rips - add_test(NAME rips_complex_diagram_persistence_from_distance_matrix_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py" - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/distance_matrix/lower_triangular_distance_matrix.csv -e 12.0 -d 3) - - add_test(NAME rips_complex_diagram_persistence_from_off_file_example_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_off_file_example.py - --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -e 0.25 -d 3) + if(MATPLOTLIB_FOUND AND NUMPY_FOUND) + add_test(NAME rips_complex_diagram_persistence_from_distance_matrix_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py" + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/distance_matrix/lower_triangular_distance_matrix.csv -e 12.0 -d 3) + + add_test(NAME rips_complex_diagram_persistence_from_off_file_example_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_off_file_example.py + --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -e 0.25 -d 3) + endif() add_test(NAME rips_complex_from_points_example_py_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -358,7 +368,7 @@ if(CYTHON_FOUND) add_gudhi_py_test(test_reader_utils) # Documentation generation is available through sphinx - requires all modules - if(SPHINX_PATH AND NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + if(SPHINX_PATH AND MATPLOTLIB_FOUND AND NUMPY_FOUND AND NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/") # User warning - Sphinx is a static pages generator, and configured to work fine with user_version # Images and biblio warnings because not found on developper version @@ -378,5 +388,5 @@ if(CYTHON_FOUND) COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) - endif(SPHINX_PATH AND NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + endif() endif(CYTHON_FOUND) diff --git a/src/cython/cython/persistence_graphical_tools.py b/src/cython/cython/persistence_graphical_tools.py index 216ab8d6..314bd6db 100755 --- a/src/cython/cython/persistence_graphical_tools.py +++ b/src/cython/cython/persistence_graphical_tools.py @@ -1,8 +1,3 @@ -import matplotlib.pyplot as plt -import matplotlib.patches as mpatches -import numpy as np -import os - """This file is part of the Gudhi Library. The Gudhi library (Geometric Understanding in Higher Dimensions) is a generic C++ library for computational topology. @@ -29,187 +24,197 @@ __author__ = "Vincent Rouvreau, Bertrand Michel" __copyright__ = "Copyright (C) 2016 Inria" __license__ = "GPL v3" -def __min_birth_max_death(persistence, band=0.): - """This function returns (min_birth, max_death) from the persistence. - - :param persistence: The persistence to plot. - :type persistence: list of tuples(dimension, tuple(birth, death)). - :param band: band - :type band: float. - :returns: (float, float) -- (min_birth, max_death). - """ - # Look for minimum birth date and maximum death date for plot optimisation - max_death = 0 - min_birth = persistence[0][1][0] - for interval in reversed(persistence): - if float(interval[1][1]) != float('inf'): - if float(interval[1][1]) > max_death: - max_death = float(interval[1][1]) - if float(interval[1][0]) > max_death: - max_death = float(interval[1][0]) - if float(interval[1][0]) < min_birth: - min_birth = float(interval[1][0]) - if band > 0.: - max_death += band - return (min_birth, max_death) +try: + import matplotlib.pyplot as plt + import matplotlib.patches as mpatches + import numpy as np + import os + + def __min_birth_max_death(persistence, band=0.): + """This function returns (min_birth, max_death) from the persistence. + + :param persistence: The persistence to plot. + :type persistence: list of tuples(dimension, tuple(birth, death)). + :param band: band + :type band: float. + :returns: (float, float) -- (min_birth, max_death). + """ + # Look for minimum birth date and maximum death date for plot optimisation + max_death = 0 + min_birth = persistence[0][1][0] + for interval in reversed(persistence): + if float(interval[1][1]) != float('inf'): + if float(interval[1][1]) > max_death: + max_death = float(interval[1][1]) + if float(interval[1][0]) > max_death: + max_death = float(interval[1][0]) + if float(interval[1][0]) < min_birth: + min_birth = float(interval[1][0]) + if band > 0.: + max_death += band + return (min_birth, max_death) -""" -Only 13 colors for the palette -""" -palette = ['#ff0000', '#00ff00', '#0000ff', '#00ffff', '#ff00ff', '#ffff00', - '#000000', '#880000', '#008800', '#000088', '#888800', '#880088', - '#008888'] - -def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6, - max_barcodes=1000, inf_delta=0.1, legend=False): - """This function plots the persistence bar code from persistence values list - or from a :doc:`persistence file `. - - :param persistence: Persistence values list. - :type persistence: list of tuples(dimension, tuple(birth, death)). - :param persistence_file: A :doc:`persistence file ` style name - (reset persistence if both are set). - :type persistence_file: string - :param alpha: barcode transparency value (0.0 transparent through 1.0 opaque - default is 0.6). - :type alpha: float. - :param max_barcodes: number of maximal barcodes to be displayed. - Set it to 0 to see all, Default value is 1000. - (persistence will be sorted by life time if max_barcodes is set) - :type max_barcodes: int. - :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta). - A reasonable value is between 0.05 and 0.5 - default is 0.1. - :type inf_delta: float. - :returns: A matplotlib object containing horizontal bar plot of persistence - (launch `show()` method on it to display it). """ - if persistence_file is not '': - if os.path.isfile(persistence_file): - # Reset persistence - persistence = [] - diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) - for key in diag.keys(): - for persistence_interval in diag[key]: - persistence.append((key, persistence_interval)) - else: - print("file " + persistence_file + " not found.") - return None - - if max_barcodes > 0 and max_barcodes < len(persistence): - # Sort by life time, then takes only the max_plots elements - persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_barcodes] - - persistence = sorted(persistence, key=lambda birth: birth[1][0]) - - (min_birth, max_death) = __min_birth_max_death(persistence) - ind = 0 - delta = ((max_death - min_birth) * inf_delta) - # Replace infinity values with max_death + delta for bar code to be more - # readable - infinity = max_death + delta - axis_start = min_birth - delta - # Draw horizontal bars in loop - for interval in reversed(persistence): - if float(interval[1][1]) != float('inf'): - # Finite death case - plt.barh(ind, (interval[1][1] - interval[1][0]), height=0.8, - left = interval[1][0], alpha=alpha, - color = palette[interval[0]], - linewidth=0) - else: - # Infinite death case for diagram to be nicer - plt.barh(ind, (infinity - interval[1][0]), height=0.8, - left = interval[1][0], alpha=alpha, - color = palette[interval[0]], - linewidth=0) - ind = ind + 1 - - if legend: - dimensions = list(set(item[0] for item in persistence)) - plt.legend(handles=[mpatches.Patch(color=palette[dim], - label=str(dim)) for dim in dimensions], - loc='lower right') - plt.title('Persistence barcode') - # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, 0, ind]) - return plt - -def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, - band=0., max_plots=1000, inf_delta=0.1, legend=False): - """This function plots the persistence diagram from persistence values list - or from a :doc:`persistence file `. - - :param persistence: Persistence values list. - :type persistence: list of tuples(dimension, tuple(birth, death)). - :param persistence_file: A :doc:`persistence file ` style name - (reset persistence if both are set). - :type persistence_file: string - :param alpha: plot transparency value (0.0 transparent through 1.0 opaque - default is 0.6). - :type alpha: float. - :param band: band (not displayed if :math:`\leq` 0. - default is 0.) - :type band: float. - :param max_plots: number of maximal plots to be displayed - Set it to 0 to see all, Default value is 1000. - (persistence will be sorted by life time if max_plots is set) - :type max_plots: int. - :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta). - A reasonable value is between 0.05 and 0.5 - default is 0.1. - :type inf_delta: float. - :returns: A matplotlib object containing diagram plot of persistence - (launch `show()` method on it to display it). + Only 13 colors for the palette """ - if persistence_file is not '': - if os.path.isfile(persistence_file): - # Reset persistence - persistence = [] - diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) - for key in diag.keys(): - for persistence_interval in diag[key]: - persistence.append((key, persistence_interval)) - else: - print("file " + persistence_file + " not found.") - return None - - if max_plots > 0 and max_plots < len(persistence): - # Sort by life time, then takes only the max_plots elements - persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_plots] - - (min_birth, max_death) = __min_birth_max_death(persistence, band) - ind = 0 - delta = ((max_death - min_birth) * inf_delta) - # Replace infinity values with max_death + delta for diagram to be more - # readable - infinity = max_death + delta - axis_start = min_birth - delta - - # line display of equation : birth = death - x = np.linspace(axis_start, infinity, 1000) - # infinity line and text - plt.plot(x, x, color='k', linewidth=1.0) - plt.plot(x, [infinity] * len(x), linewidth=1.0, color='k', alpha=alpha) - plt.text(axis_start, infinity, r'$\infty$', color='k', alpha=alpha) - # bootstrap band - if band > 0.: - plt.fill_between(x, x, x+band, alpha=alpha, facecolor='red') - - # Draw points in loop - for interval in reversed(persistence): - if float(interval[1][1]) != float('inf'): - # Finite death case - plt.scatter(interval[1][0], interval[1][1], alpha=alpha, - color = palette[interval[0]]) - else: - # Infinite death case for diagram to be nicer - plt.scatter(interval[1][0], infinity, alpha=alpha, - color = palette[interval[0]]) - ind = ind + 1 - - if legend: - dimensions = list(set(item[0] for item in persistence)) - plt.legend(handles=[mpatches.Patch(color=palette[dim], label=str(dim)) for dim in dimensions]) - - plt.title('Persistence diagram') - plt.xlabel('Birth') - plt.ylabel('Death') - # Ends plot on infinity value and starts a little bit before min_birth - plt.axis([axis_start, infinity, axis_start, infinity + delta]) - return plt + palette = ['#ff0000', '#00ff00', '#0000ff', '#00ffff', '#ff00ff', '#ffff00', + '#000000', '#880000', '#008800', '#000088', '#888800', '#880088', + '#008888'] + + def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6, + max_barcodes=1000, inf_delta=0.1, legend=False): + """This function plots the persistence bar code from persistence values list + or from a :doc:`persistence file `. + + :param persistence: Persistence values list. + :type persistence: list of tuples(dimension, tuple(birth, death)). + :param persistence_file: A :doc:`persistence file ` style name + (reset persistence if both are set). + :type persistence_file: string + :param alpha: barcode transparency value (0.0 transparent through 1.0 opaque - default is 0.6). + :type alpha: float. + :param max_barcodes: number of maximal barcodes to be displayed. + Set it to 0 to see all, Default value is 1000. + (persistence will be sorted by life time if max_barcodes is set) + :type max_barcodes: int. + :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta). + A reasonable value is between 0.05 and 0.5 - default is 0.1. + :type inf_delta: float. + :returns: A matplotlib object containing horizontal bar plot of persistence + (launch `show()` method on it to display it). + """ + if persistence_file is not '': + if os.path.isfile(persistence_file): + # Reset persistence + persistence = [] + diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) + for key in diag.keys(): + for persistence_interval in diag[key]: + persistence.append((key, persistence_interval)) + else: + print("file " + persistence_file + " not found.") + return None + + if max_barcodes > 0 and max_barcodes < len(persistence): + # Sort by life time, then takes only the max_plots elements + persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_barcodes] + + persistence = sorted(persistence, key=lambda birth: birth[1][0]) + + (min_birth, max_death) = __min_birth_max_death(persistence) + ind = 0 + delta = ((max_death - min_birth) * inf_delta) + # Replace infinity values with max_death + delta for bar code to be more + # readable + infinity = max_death + delta + axis_start = min_birth - delta + # Draw horizontal bars in loop + for interval in reversed(persistence): + if float(interval[1][1]) != float('inf'): + # Finite death case + plt.barh(ind, (interval[1][1] - interval[1][0]), height=0.8, + left = interval[1][0], alpha=alpha, + color = palette[interval[0]], + linewidth=0) + else: + # Infinite death case for diagram to be nicer + plt.barh(ind, (infinity - interval[1][0]), height=0.8, + left = interval[1][0], alpha=alpha, + color = palette[interval[0]], + linewidth=0) + ind = ind + 1 + + if legend: + dimensions = list(set(item[0] for item in persistence)) + plt.legend(handles=[mpatches.Patch(color=palette[dim], + label=str(dim)) for dim in dimensions], + loc='lower right') + plt.title('Persistence barcode') + # Ends plot on infinity value and starts a little bit before min_birth + plt.axis([axis_start, infinity, 0, ind]) + return plt + + def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6, + band=0., max_plots=1000, inf_delta=0.1, legend=False): + """This function plots the persistence diagram from persistence values list + or from a :doc:`persistence file `. + + :param persistence: Persistence values list. + :type persistence: list of tuples(dimension, tuple(birth, death)). + :param persistence_file: A :doc:`persistence file ` style name + (reset persistence if both are set). + :type persistence_file: string + :param alpha: plot transparency value (0.0 transparent through 1.0 opaque - default is 0.6). + :type alpha: float. + :param band: band (not displayed if :math:`\leq` 0. - default is 0.) + :type band: float. + :param max_plots: number of maximal plots to be displayed + Set it to 0 to see all, Default value is 1000. + (persistence will be sorted by life time if max_plots is set) + :type max_plots: int. + :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta). + A reasonable value is between 0.05 and 0.5 - default is 0.1. + :type inf_delta: float. + :returns: A matplotlib object containing diagram plot of persistence + (launch `show()` method on it to display it). + """ + if persistence_file is not '': + if os.path.isfile(persistence_file): + # Reset persistence + persistence = [] + diag = read_persistence_intervals_grouped_by_dimension(persistence_file=persistence_file) + for key in diag.keys(): + for persistence_interval in diag[key]: + persistence.append((key, persistence_interval)) + else: + print("file " + persistence_file + " not found.") + return None + + if max_plots > 0 and max_plots < len(persistence): + # Sort by life time, then takes only the max_plots elements + persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_plots] + + (min_birth, max_death) = __min_birth_max_death(persistence, band) + ind = 0 + delta = ((max_death - min_birth) * inf_delta) + # Replace infinity values with max_death + delta for diagram to be more + # readable + infinity = max_death + delta + axis_start = min_birth - delta + + # line display of equation : birth = death + x = np.linspace(axis_start, infinity, 1000) + # infinity line and text + plt.plot(x, x, color='k', linewidth=1.0) + plt.plot(x, [infinity] * len(x), linewidth=1.0, color='k', alpha=alpha) + plt.text(axis_start, infinity, r'$\infty$', color='k', alpha=alpha) + # bootstrap band + if band > 0.: + plt.fill_between(x, x, x+band, alpha=alpha, facecolor='red') + + # Draw points in loop + for interval in reversed(persistence): + if float(interval[1][1]) != float('inf'): + # Finite death case + plt.scatter(interval[1][0], interval[1][1], alpha=alpha, + color = palette[interval[0]]) + else: + # Infinite death case for diagram to be nicer + plt.scatter(interval[1][0], infinity, alpha=alpha, + color = palette[interval[0]]) + ind = ind + 1 + + if legend: + dimensions = list(set(item[0] for item in persistence)) + plt.legend(handles=[mpatches.Patch(color=palette[dim], label=str(dim)) for dim in dimensions]) + + plt.title('Persistence diagram') + plt.xlabel('Birth') + plt.ylabel('Death') + # Ends plot on infinity value and starts a little bit before min_birth + plt.axis([axis_start, infinity, axis_start, infinity + delta]) + return plt + +except ImportError: + # Continue in case of import error, functions won't be available + pass diff --git a/src/cython/setup.py.in b/src/cython/setup.py.in index ee381a1b..4037aab6 100644 --- a/src/cython/setup.py.in +++ b/src/cython/setup.py.in @@ -46,9 +46,5 @@ setup( version='@GUDHI_VERSION@', url='http://gudhi.gforge.inria.fr/', ext_modules = cythonize(gudhi), - install_requires = [ - "matplotlib", - "numpy", - "cython", - ], + install_requires = ["cython",], ) -- cgit v1.2.3