diff options
author | glisse <glisse@636b058d-ea47-450e-bf9e-a15bfbe3eedb> | 2018-10-06 12:47:24 +0000 |
---|---|---|
committer | glisse <glisse@636b058d-ea47-450e-bf9e-a15bfbe3eedb> | 2018-10-06 12:47:24 +0000 |
commit | a11e065574e45cfa49ff913fdd2c47743cae2286 (patch) | |
tree | 32bd650aa1d9bfe473b7fa15b499886093c52851 /src/cython | |
parent | 42e599ebd89231c276a3c6e1b9121ea13f25d118 (diff) | |
parent | f1d0acdc9f3f8d886996cc078242b48598a3275a (diff) |
merge from trunk
git-svn-id: svn+ssh://scm.gforge.inria.fr/svnroot/gudhi/branches/sparserips-python@3935 636b058d-ea47-450e-bf9e-a15bfbe3eedb
Former-commit-id: 81f81cb6b39ece77718c8b03d408a90789946c0f
Diffstat (limited to 'src/cython')
-rw-r--r-- | src/cython/CMakeLists.txt | 687 | ||||
-rw-r--r-- | src/cython/cython/nerve_gic.pyx | 30 | ||||
-rw-r--r--[-rwxr-xr-x] | src/cython/cython/persistence_graphical_tools.py | 393 | ||||
-rw-r--r-- | src/cython/cython/simplex_tree.pyx | 2 | ||||
-rw-r--r-- | src/cython/cython/subsampling.pyx | 8 | ||||
-rw-r--r-- | src/cython/doc/installation.rst | 63 | ||||
-rw-r--r-- | src/cython/doc/nerve_gic_complex_sum.rst | 2 | ||||
-rw-r--r-- | src/cython/doc/persistence_graphical_tools_ref.rst | 1 | ||||
-rw-r--r-- | src/cython/doc/persistence_graphical_tools_sum.inc | 4 | ||||
-rw-r--r-- | src/cython/doc/persistence_graphical_tools_user.rst | 37 | ||||
-rw-r--r-- | src/cython/gudhi.pyx.in | 1 | ||||
-rw-r--r-- | src/cython/include/Alpha_complex_interface.h | 2 | ||||
-rw-r--r-- | src/cython/include/Nerve_gic_interface.h | 2 | ||||
-rw-r--r-- | src/cython/setup.py.in | 6 |
14 files changed, 791 insertions, 447 deletions
diff --git a/src/cython/CMakeLists.txt b/src/cython/CMakeLists.txt index 8f1dec76..dc2e9278 100644 --- a/src/cython/CMakeLists.txt +++ b/src/cython/CMakeLists.txt @@ -16,306 +16,429 @@ 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) -endfunction( add_gudhi_py_test ) - - -if(CYTHON_FOUND) - message("++ ${PYTHON_EXECUTABLE} v.${PYTHON_VERSION_STRING} - Cython is ${CYTHON_EXECUTABLE} - Sphinx is ${SPHINX_PATH}") - - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_SYSTEM_NO_DEPRECATED', ") - - # Gudhi and CGAL compilation option - if(MSVC) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'/fp:strict', ") - else(MSVC) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-std=c++11', ") - endif(MSVC) - if(CMAKE_COMPILER_IS_GNUCXX) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-frounding-math', ") - endif(CMAKE_COMPILER_IS_GNUCXX) - if (CMAKE_CXX_COMPILER_ID MATCHES Intel) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-fp-model strict', ") - endif(CMAKE_CXX_COMPILER_ID MATCHES Intel) - if (DEBUG_TRACES) - # For programs to be more verbose - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DDEBUG_TRACES', ") + 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 ) - if (EIGEN3_FOUND) - # No problem, even if no CGAL found - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_EIGEN3_ENABLED', ") - endif (EIGEN3_FOUND) - - if (NOT CGAL_VERSION VERSION_LESS 4.8.1) - set(GUDHI_CYTHON_BOTTLENECK_DISTANCE "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/bottleneck_distance.pyx'") - set(GUDHI_CYTHON_NERVE_GIC "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/nerve_gic.pyx'") - endif (NOT CGAL_VERSION VERSION_LESS 4.8.1) - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) - set(GUDHI_CYTHON_SUBSAMPLING "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/subsampling.pyx'") - set(GUDHI_CYTHON_TANGENTIAL_COMPLEX "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/tangential_complex.pyx'") - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) - set(GUDHI_CYTHON_ALPHA_COMPLEX "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/alpha_complex.pyx'") - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) - set(GUDHI_CYTHON_EUCLIDEAN_WITNESS_COMPLEX - "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/euclidean_witness_complex.pyx'\ninclude '${CMAKE_CURRENT_SOURCE_DIR}/cython/euclidean_strong_witness_complex.pyx'\n") - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) - - if(CGAL_FOUND) - can_cgal_use_cxx11_thread_local() - if (NOT CGAL_CAN_USE_CXX11_THREAD_LOCAL_RESULT) - add_gudhi_cython_lib(${Boost_THREAD_LIBRARY}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${Boost_LIBRARY_DIRS}', ") +# Set gudhi.__debug_info__ +# WARNING : to be done before gudhi.pyx.in configure_file +function( add_gudhi_debug_info DEBUG_INFO ) + set(GUDHI_CYTHON_DEBUG_INFO "${GUDHI_CYTHON_DEBUG_INFO} \"${DEBUG_INFO}\\n\" \\\n" PARENT_SCOPE) +endfunction( add_gudhi_debug_info ) + +if(PYTHONINTERP_FOUND) + if(CYTHON_FOUND) + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}off_reader;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}simplex_tree;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}rips_complex;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}cubical_complex;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}periodic_cubical_complex;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}persistence_graphical_tools;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}reader_utils;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}witness_complex;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}strong_witness_complex;") + + add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}") + add_gudhi_debug_info("Cython version ${CYTHON_VERSION}") + if(PYTEST_FOUND) + add_gudhi_debug_info("Pytest version ${PYTEST_VERSION}") + endif() + if(MATPLOTLIB_FOUND) + add_gudhi_debug_info("Matplotlib version ${MATPLOTLIB_VERSION}") + endif() + if(NUMPY_FOUND) + add_gudhi_debug_info("Numpy version ${NUMPY_VERSION}") + endif() + if(SCIPY_FOUND) + add_gudhi_debug_info("Scipy version ${SCIPY_VERSION}") + endif() + if(MATPLOTLIB_FOUND AND NUMPY_FOUND AND SCIPY_FOUND) + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}persistence_graphical_tools;") + else() + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MODULES}persistence_graphical_tools;") endif() - # Add CGAL compilation args - if(CGAL_HEADER_ONLY) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_HEADER_ONLY', ") - else(CGAL_HEADER_ONLY) - add_gudhi_cython_lib(${CGAL_LIBRARY}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${CGAL_LIBRARIES_DIR}', ") - # If CGAL is not header only, CGAL library may link with boost system, - add_gudhi_cython_lib(${Boost_SYSTEM_LIBRARY}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${Boost_LIBRARY_DIRS}', ") - endif(CGAL_HEADER_ONLY) - # GMP and GMPXX are not required, but if present, CGAL will link with them. - if(GMP_FOUND) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMP', ") - add_gudhi_cython_lib(${GMP_LIBRARIES}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${GMP_LIBRARIES_DIR}', ") - if(GMPXX_FOUND) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMPXX', ") - add_gudhi_cython_lib(${GMPXX_LIBRARIES}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${GMPXX_LIBRARIES_DIR}', ") - endif(GMPXX_FOUND) - endif(GMP_FOUND) - endif(CGAL_FOUND) - - # Specific for Mac - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-mmacosx-version-min=10.12', ") - set(GUDHI_CYTHON_EXTRA_LINK_ARGS "${GUDHI_CYTHON_EXTRA_LINK_ARGS}'-mmacosx-version-min=10.12', ") - endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - - # Loop on INCLUDE_DIRECTORIES PROPERTY - get_property(GUDHI_INCLUDE_DIRECTORIES DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) - foreach(GUDHI_INCLUDE_DIRECTORY ${GUDHI_INCLUDE_DIRECTORIES}) - set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${GUDHI_INCLUDE_DIRECTORY}', ") - endforeach() - set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${CMAKE_SOURCE_DIR}/${GUDHI_CYTHON_PATH}/include', ") - - if (TBB_FOUND AND WITH_GUDHI_USE_TBB) - set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DGUDHI_USE_TBB', ") - add_gudhi_cython_lib(${TBB_RELEASE_LIBRARY}) - add_gudhi_cython_lib(${TBB_MALLOC_RELEASE_LIBRARY}) - set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${TBB_LIBRARY_DIRS}', ") - set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${TBB_INCLUDE_DIRS}', ") - endif() - - if(UNIX) - set( GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}") - endif(UNIX) - - # Generate setup.py file to cythonize Gudhi - This file must be named setup.py by convention - configure_file(setup.py.in "${CMAKE_CURRENT_BINARY_DIR}/setup.py" @ONLY) - # Generate gudhi.pyx - Gudhi cython file - configure_file(gudhi.pyx.in "${CMAKE_CURRENT_BINARY_DIR}/gudhi.pyx" @ONLY) - - add_custom_command( - OUTPUT gudhi.so - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/setup.py" "build_ext" "--inplace") - - add_custom_target(cython ALL DEPENDS gudhi.so - COMMENT "Do not forget to add ${CMAKE_CURRENT_BINARY_DIR}/ to your PYTHONPATH before using examples or tests") - - # For installation purpose - # TODO(VR) : files matching pattern mechanism is copying all cython directory - install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" DESTINATION "${PYTHON_SITE_PACKAGES}/" FILES_MATCHING - PATTERN "*.so" - PATTERN "*.dylib" - PATTERN "*.pyd") - - # Test examples - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) - # Bottleneck and Alpha - add_test(NAME alpha_rips_persistence_bottleneck_distance_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_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) + message("++ ${PYTHON_EXECUTABLE} v.${PYTHON_VERSION_STRING} - Cython is ${CYTHON_VERSION} - Sphinx is ${SPHINX_PATH}") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_RESULT_OF_USE_DECLTYPE', ") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_ALL_NO_LIB', ") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DBOOST_SYSTEM_NO_DEPRECATED', ") + + # Gudhi and CGAL compilation option + if(MSVC) + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'/fp:strict', ") + else(MSVC) + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-std=c++11', ") + endif(MSVC) + if(CMAKE_COMPILER_IS_GNUCXX) + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-frounding-math', ") + endif(CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_CXX_COMPILER_ID MATCHES Intel) + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-fp-model strict', ") + endif(CMAKE_CXX_COMPILER_ID MATCHES Intel) + if (DEBUG_TRACES) + # For programs to be more verbose + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DDEBUG_TRACES', ") + endif() - 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 (EIGEN3_FOUND) + add_gudhi_debug_info("Eigen3 version ${EIGEN3_VERSION}") + # No problem, even if no CGAL found + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_EIGEN3_ENABLED', ") + endif (EIGEN3_FOUND) + + if (NOT CGAL_VERSION VERSION_LESS 4.8.1) + set(GUDHI_CYTHON_BOTTLENECK_DISTANCE "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/bottleneck_distance.pyx'") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}bottleneck_distance;") + set(GUDHI_CYTHON_NERVE_GIC "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/nerve_gic.pyx'") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}nerve_gic;") + else() + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}bottleneck_distance;") + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}nerve_gic;") + endif () + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + set(GUDHI_CYTHON_SUBSAMPLING "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/subsampling.pyx'") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}subsampling;") + set(GUDHI_CYTHON_TANGENTIAL_COMPLEX "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/tangential_complex.pyx'") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}tangential_complex;") + else() + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}subsampling;") + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}tangential_complex;") + endif () + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) + set(GUDHI_CYTHON_ALPHA_COMPLEX "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/alpha_complex.pyx'") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}alpha_complex;") + else() + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}alpha_complex;") + endif () + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) + set(GUDHI_CYTHON_EUCLIDEAN_WITNESS_COMPLEX + "include '${CMAKE_CURRENT_SOURCE_DIR}/cython/euclidean_witness_complex.pyx'\ninclude '${CMAKE_CURRENT_SOURCE_DIR}/cython/euclidean_strong_witness_complex.pyx'\n") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}euclidean_witness_complex;") + set(GUDHI_CYTHON_MODULES "${GUDHI_CYTHON_MODULES}euclidean_strong_witness_complex;") + else() + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}euclidean_witness_complex;") + set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MISSING_MODULES}euclidean_strong_witness_complex;") + endif () + + add_gudhi_debug_info("Installed modules are: ${GUDHI_CYTHON_MODULES}") + if(GUDHI_CYTHON_MISSING_MODULES) + add_gudhi_debug_info("Missing modules are: ${GUDHI_CYTHON_MISSING_MODULES}") + endif() - # Subsampling - add_gudhi_py_test(test_subsampling) + if(CGAL_FOUND) + can_cgal_use_cxx11_thread_local() + if (NOT CGAL_CAN_USE_CXX11_THREAD_LOCAL_RESULT) + if(CMAKE_BUILD_TYPE MATCHES Debug) + add_gudhi_cython_lib("${Boost_THREAD_LIBRARY_DEBUG}") + else() + add_gudhi_cython_lib("${Boost_THREAD_LIBRARY_RELEASE}") + endif() + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${Boost_LIBRARY_DIRS}', ") + endif() + # Add CGAL compilation args + if(CGAL_HEADER_ONLY) + add_gudhi_debug_info("CGAL header only version ${CGAL_VERSION}") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_HEADER_ONLY', ") + else(CGAL_HEADER_ONLY) + add_gudhi_debug_info("CGAL version ${CGAL_VERSION}") + add_gudhi_cython_lib("${CGAL_LIBRARY}") + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${CGAL_LIBRARIES_DIR}', ") + # If CGAL is not header only, CGAL library may link with boost system, + if(CMAKE_BUILD_TYPE MATCHES Debug) + add_gudhi_cython_lib("${Boost_SYSTEM_LIBRARY_DEBUG}") + else() + add_gudhi_cython_lib("${Boost_SYSTEM_LIBRARY_RELEASE}") + endif() + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${Boost_LIBRARY_DIRS}', ") + endif(CGAL_HEADER_ONLY) + # GMP and GMPXX are not required, but if present, CGAL will link with them. + if(GMP_FOUND) + add_gudhi_debug_info("GMP_LIBRARIES = ${GMP_LIBRARIES}") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMP', ") + add_gudhi_cython_lib("${GMP_LIBRARIES}") + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${GMP_LIBRARIES_DIR}', ") + if(GMPXX_FOUND) + add_gudhi_debug_info("GMPXX_LIBRARIES = ${GMPXX_LIBRARIES}") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DCGAL_USE_GMPXX', ") + add_gudhi_cython_lib("${GMPXX_LIBRARIES}") + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${GMPXX_LIBRARIES_DIR}', ") + endif(GMPXX_FOUND) + endif(GMP_FOUND) + endif(CGAL_FOUND) + + # Specific for Mac + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-mmacosx-version-min=10.12', ") + set(GUDHI_CYTHON_EXTRA_LINK_ARGS "${GUDHI_CYTHON_EXTRA_LINK_ARGS}'-mmacosx-version-min=10.12', ") + endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + + # Loop on INCLUDE_DIRECTORIES PROPERTY + get_property(GUDHI_INCLUDE_DIRECTORIES DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + foreach(GUDHI_INCLUDE_DIRECTORY ${GUDHI_INCLUDE_DIRECTORIES}) + set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${GUDHI_INCLUDE_DIRECTORY}', ") + endforeach() + set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${CMAKE_SOURCE_DIR}/${GUDHI_CYTHON_PATH}/include', ") + + if (TBB_FOUND AND WITH_GUDHI_USE_TBB) + add_gudhi_debug_info("TBB version ${TBB_INTERFACE_VERSION} found and used") + set(GUDHI_CYTHON_EXTRA_COMPILE_ARGS "${GUDHI_CYTHON_EXTRA_COMPILE_ARGS}'-DGUDHI_USE_TBB', ") + if(CMAKE_BUILD_TYPE MATCHES Debug) + add_gudhi_cython_lib("${TBB_DEBUG_LIBRARY}") + add_gudhi_cython_lib("${TBB_MALLOC_DEBUG_LIBRARY}") + else() + add_gudhi_cython_lib("${TBB_RELEASE_LIBRARY}") + add_gudhi_cython_lib("${TBB_MALLOC_RELEASE_LIBRARY}") + endif() + set(GUDHI_CYTHON_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}'${TBB_LIBRARY_DIRS}', ") + set(GUDHI_CYTHON_INCLUDE_DIRS "${GUDHI_CYTHON_INCLUDE_DIRS}'${TBB_INCLUDE_DIRS}', ") + endif() - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) - if (NOT CGAL_VERSION VERSION_LESS 4.8.1) - # Bottleneck - add_test(NAME bottleneck_basic_example_py_test + if(UNIX) + set( GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}") + endif(UNIX) + + # Generate setup.py file to cythonize Gudhi - This file must be named setup.py by convention + configure_file(setup.py.in "${CMAKE_CURRENT_BINARY_DIR}/setup.py" @ONLY) + # Generate gudhi.pyx - Gudhi cython file + configure_file(gudhi.pyx.in "${CMAKE_CURRENT_BINARY_DIR}/gudhi.pyx" @ONLY) + + add_custom_command( + OUTPUT gudhi.so + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/setup.py" "build_ext" "--inplace") + + add_custom_target(cython ALL DEPENDS gudhi.so + COMMENT "Do not forget to add ${CMAKE_CURRENT_BINARY_DIR}/ to your PYTHONPATH before using examples or tests") + + # For installation purpose + # TODO(VR) : files matching pattern mechanism is copying all cython directory + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" DESTINATION "${PYTHON_SITE_PACKAGES}/" FILES_MATCHING + PATTERN "*.so" + PATTERN "*.dylib" + PATTERN "*.pyd") + + # Test examples + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + # Bottleneck and Alpha + add_test(NAME alpha_rips_persistence_bottleneck_distance_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_rips_persistence_bottleneck_distance.py" + -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -t 0.15 -d 3) + + 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) + + endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + if (NOT CGAL_VERSION VERSION_LESS 4.8.1) + # Bottleneck + add_test(NAME bottleneck_basic_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/bottleneck_basic_example.py") + + add_gudhi_py_test(test_bottleneck_distance) + + # Cover complex + file(COPY ${CMAKE_SOURCE_DIR}/data/points/human.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat_PCA1 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + add_test(NAME cover_complex_nerve_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/nerve_of_a_covering.py" + -f human.off -c 2 -r 10 -g 0.3) + + add_test(NAME cover_complex_coordinate_gic_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/coordinate_graph_induced_complex.py" + -f human.off -c 0 -v) + + add_test(NAME cover_complex_functional_gic_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/functional_graph_induced_complex.py" + -o lucky_cat.off + -f lucky_cat_PCA1 -v) + + add_test(NAME cover_complex_voronoi_gic_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/voronoi_graph_induced_complex.py" + -f human.off -n 700 -v) + + add_gudhi_py_test(test_cover_complex) + endif (NOT CGAL_VERSION VERSION_LESS 4.8.1) + + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) + # Alpha + add_test(NAME alpha_complex_from_points_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_from_points_example.py") + + 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) + + endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) + + if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) + # Euclidean witness + add_gudhi_py_test(test_euclidean_witness_complex) + + endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) + + # Cubical + add_test(NAME periodic_cubical_complex_barcode_persistence_from_perseus_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/bottleneck_basic_example.py") + ${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) + + 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_bottleneck_distance) + add_gudhi_py_test(test_cubical_complex) + + # Rips + 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() - # Cover complex - file(COPY ${CMAKE_SOURCE_DIR}/data/points/human.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat.off DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - file(COPY ${CMAKE_SOURCE_DIR}/data/points/COIL_database/lucky_cat_PCA1 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) - add_test(NAME cover_complex_nerve_example_py_test + add_test(NAME rips_complex_from_points_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/nerve_of_a_covering.py" - -f human.off -c 2 -r 10 -g 0.3) + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_from_points_example.py) - add_test(NAME cover_complex_coordinate_gic_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/coordinate_graph_induced_complex.py" - -f human.off -c 0 -v) + add_gudhi_py_test(test_rips_complex) - add_test(NAME cover_complex_functional_gic_example_py_test + # Simplex tree + add_test(NAME simplex_tree_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/functional_graph_induced_complex.py" - -o lucky_cat.off - -f lucky_cat_PCA1 -v) + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/simplex_tree_example.py) - add_test(NAME cover_complex_voronoi_gic_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/voronoi_graph_induced_complex.py" - -f human.off -n 700 -v) - - add_gudhi_py_test(test_cover_complex) - endif (NOT CGAL_VERSION VERSION_LESS 4.8.1) - - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) - # Alpha - add_test(NAME alpha_complex_from_points_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_from_points_example.py") + add_gudhi_py_test(test_simplex_tree) - add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test + # Witness + add_test(NAME witness_complex_from_nearest_landmark_table_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) - - add_gudhi_py_test(test_alpha_complex) - - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0) - - if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) - # Euclidean witness - add_gudhi_py_test(test_euclidean_witness_complex) - - endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0) - - # Cubical - add_test(NAME periodic_cubical_complex_barcode_persistence_from_perseus_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/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) - - 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) - - add_test(NAME rips_complex_from_points_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_from_points_example.py) - - add_gudhi_py_test(test_rips_complex) - - # Simplex tree - add_test(NAME simplex_tree_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/simplex_tree_example.py) - - add_gudhi_py_test(test_simplex_tree) - - # Witness - add_test(NAME witness_complex_from_nearest_landmark_table_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/witness_complex_from_nearest_landmark_table.py) - - add_gudhi_py_test(test_witness_complex) - - # Reader utils - 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) - 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 - if (GUDHI_CYTHON_PATH STREQUAL "src/cython") - set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") - endif() - # sphinx target requires gudhi.so, because conf.py reads gudhi version from it - add_custom_target(sphinx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" - ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" - COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) - - add_test(NAME sphinx_py_test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - 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(CYTHON_FOUND) + ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/witness_complex_from_nearest_landmark_table.py) + + add_gudhi_py_test(test_witness_complex) + + # Reader utils + add_gudhi_py_test(test_reader_utils) + + # Documentation generation is available through sphinx - requires all modules + if(SPHINX_PATH) + if(MATPLOTLIB_FOUND) + if(NUMPY_FOUND) + if(SCIPY_FOUND) + if(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 + if (GUDHI_CYTHON_PATH STREQUAL "src/cython") + set (GUDHI_SPHINX_MESSAGE "${GUDHI_SPHINX_MESSAGE} \n WARNING : Sphinx is configured for user version, you run it on developper version. Images and biblio will miss") + endif() + # sphinx target requires gudhi.so, because conf.py reads gudhi version from it + add_custom_target(sphinx + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b html ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/sphinx + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gudhi.so" + COMMENT "${GUDHI_SPHINX_MESSAGE}" VERBATIM) + + add_test(NAME sphinx_py_test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest) + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MODULES") + else(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + message("++ Python documentation module will not be compiled because it requires a CGAL with Eigen3 version greater or equal than 4.8.1") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1) + else(SCIPY_FOUND) + message("++ Python documentation module will not be compiled because scipy was not found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(SCIPY_FOUND) + else(NUMPY_FOUND) + message("++ Python documentation module will not be compiled because numpy was not found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(NUMPY_FOUND) + else(MATPLOTLIB_FOUND) + message("++ Python documentation module will not be compiled because matplotlib was not found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(MATPLOTLIB_FOUND) + else(SPHINX_PATH) + message("++ Python documentation module will not be compiled because sphinx and sphinxcontrib-bibtex were not found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(SPHINX_PATH) + + + # Set missing or not modules + set(GUDHI_MODULES ${GUDHI_MODULES} "python" CACHE INTERNAL "GUDHI_MODULES") + else(CYTHON_FOUND) + message("++ Python module will not be compiled because cython was not found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES") + endif(CYTHON_FOUND) +else(PYTHONINTERP_FOUND) + message("++ Python module will not be compiled because no Python interpreter was found") + set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python" CACHE INTERNAL "GUDHI_MISSING_MODULES") +endif(PYTHONINTERP_FOUND) diff --git a/src/cython/cython/nerve_gic.pyx b/src/cython/cython/nerve_gic.pyx index 30a14d3b..5f01b379 100644 --- a/src/cython/cython/nerve_gic.pyx +++ b/src/cython/cython/nerve_gic.pyx @@ -38,14 +38,14 @@ cdef extern from "Nerve_gic_interface.h" namespace "Gudhi": double compute_distance_from_confidence_level(double alpha) void compute_distribution(int N) double compute_p_value() - void compute_PD() + vector[pair[double, double]] compute_PD() void find_simplices() void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree) bool read_point_cloud(string off_file_name) double set_automatic_resolution() void set_color_from_coordinate(int k) void set_color_from_file(string color_file_name) - void set_color_from_vector(vector[double] color) + void set_color_from_range(vector[double] color) void set_cover_from_file(string cover_file_name) void set_cover_from_function() void set_cover_from_Euclidean_Voronoi(int m) @@ -67,6 +67,8 @@ cdef extern from "Nerve_gic_interface.h" namespace "Gudhi": void write_info() void plot_DOT() void plot_OFF() + void set_point_cloud_from_range(vector[vector[double]] cloud) + void set_distances_from_range(vector[vector[double]] distance_matrix) # CoverComplex python interface cdef class CoverComplex: @@ -102,6 +104,22 @@ cdef class CoverComplex: """ return self.thisptr != NULL + def set_point_cloud_from_range(self, cloud): + """ Reads and stores the input point cloud from a vector stored in memory. + + :param cloud: Input vector containing the point cloud. + :type cloud: vector[vector[double]] + """ + return self.thisptr.set_point_cloud_from_range(cloud) + + def set_distances_from_range(self, distance_matrix): + """ Reads and stores the input distance matrix from a vector stored in memory. + + :param distance_matrix: Input vector containing the distance matrix. + :type distance_matrix: vector[vector[double]] + """ + return self.thisptr.set_distances_from_range(distance_matrix) + def compute_confidence_level_from_distance(self, distance): """Computes the confidence level of a specific bottleneck distance threshold. @@ -145,7 +163,7 @@ cdef class CoverComplex: def compute_PD(self): """Computes the extended persistence diagram of the complex. """ - self.thisptr.compute_PD() + return self.thisptr.compute_PD() def create_simplex_tree(self): """ @@ -162,7 +180,7 @@ cdef class CoverComplex: self.thisptr.find_simplices() def read_point_cloud(self, off_file): - """Reads and stores the input point cloud. + """Reads and stores the input point cloud from .(n)OFF file. :param off_file: Name of the input .OFF or .nOFF file. :type off_file: string @@ -206,14 +224,14 @@ cdef class CoverComplex: else: print("file " + color_file_name + " not found.") - def set_color_from_vector(self, color): + def set_color_from_range(self, color): """Computes the function used to color the nodes of the simplicial complex from a vector stored in memory. :param color: Input vector of values. :type color: vector[double] """ - self.thisptr.set_color_from_vector(color) + self.thisptr.set_color_from_range(color) def set_cover_from_file(self, cover_file_name): """Creates the cover C from a file containing the cover elements of diff --git a/src/cython/cython/persistence_graphical_tools.py b/src/cython/cython/persistence_graphical_tools.py index 216ab8d6..d7be936f 100755..100644 --- 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. @@ -59,157 +54,295 @@ 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): + max_intervals=1000, 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 <fileformats>`. - :param persistence: Persistence values list. + :param persistence: Persistence intervals values list grouped by dimension. :type persistence: list of tuples(dimension, tuple(birth, death)). :param persistence_file: A :doc:`persistence file <fileformats>` 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). + :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. + :param max_intervals: maximal number of intervals to display. + Selected intervals are those with the longest life time. Set it + to 0 to see all. Default value is 1000. + :type max_intervals: int. + :param inf_delta: Infinity is placed at :code:`((max_death - min_birth) x + inf_delta)` above :code:`max_death` value. A reasonable value is + between 0.05 and 0.5 - default is 0.1. :type inf_delta: float. + :param legend: Display the dimension color legend (default is False). + :type legend: boolean. :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 + try: + import matplotlib.pyplot as plt + import matplotlib.patches as mpatches + import numpy as np + import os + + 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 is not 1000: + print('Deprecated parameter. It has been replaced by max_intervals') + max_intervals = max_barcodes + + if max_intervals > 0 and max_intervals < len(persistence): + # Sort by life time, then takes only the max_intervals elements + persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_intervals] + + 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 + + except ImportError: + print("This function is not available, you may be missing numpy and/or matplotlib.") 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 <fileformats>`. + band=0., max_intervals=1000, 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 <fileformats>`. - :param persistence: Persistence values list. + :param persistence: Persistence intervals values list grouped by dimension. :type persistence: list of tuples(dimension, tuple(birth, death)). :param persistence_file: A :doc:`persistence file <fileformats>` 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). + :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. + :param max_intervals: maximal number of intervals to display. + Selected intervals are those with the longest life time. Set it + to 0 to see all. Default value is 1000. + :type max_intervals: int. + :param inf_delta: Infinity is placed at :code:`((max_death - min_birth) x + inf_delta)` above :code:`max_death` value. A reasonable value is + between 0.05 and 0.5 - default is 0.1. :type inf_delta: float. + :param legend: Display the dimension color legend (default is False). + :type legend: boolean. :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') + try: + import matplotlib.pyplot as plt + import matplotlib.patches as mpatches + import numpy as np + import os - # 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]]) + 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 is not 1000: + print('Deprecated parameter. It has been replaced by max_intervals') + max_intervals = max_plots + + if max_intervals > 0 and max_intervals < len(persistence): + # Sort by life time, then takes only the max_intervals elements + persistence = sorted(persistence, key=lambda life_time: life_time[1][1]-life_time[1][0], reverse=True)[:max_intervals] + + (min_birth, max_death) = __min_birth_max_death(persistence, band) + 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]]) + + 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: + print("This function is not available, you may be missing numpy and/or matplotlib.") + +def plot_persistence_density(persistence=[], persistence_file='', + nbins=300, bw_method=None, + max_intervals=1000, dimension=None, + cmap=None, legend=False): + """This function plots the persistence density from persistence + values list or from a :doc:`persistence file <fileformats>`. Be + aware that this function does not distinguish the dimension, it is + up to you to select the required one. This function also does not handle + degenerate data set (scipy correlation matrix inversion can fail). + + :param persistence: Persistence intervals values list grouped by dimension. + :type persistence: list of tuples(dimension, tuple(birth, death)). + :param persistence_file: A :doc:`persistence file <fileformats>` + style name (reset persistence if both are set). + :type persistence_file: string + :param nbins: Evaluate a gaussian kde on a regular grid of nbins x + nbins over data extents (default is 300) + :type nbins: int. + :param bw_method: The method used to calculate the estimator + bandwidth. This can be 'scott', 'silverman', a scalar constant + or a callable. If a scalar, this will be used directly as + kde.factor. If a callable, it should take a gaussian_kde + instance as only parameter and return a scalar. If None + (default), 'scott' is used. See + `scipy.stats.gaussian_kde documentation + <http://scipy.github.io/devdocs/generated/scipy.stats.gaussian_kde.html>`_ + for more details. + :type bw_method: str, scalar or callable, optional. + :param max_intervals: maximal number of points used in the density + estimation. + Selected intervals are those with the longest life time. Set it + to 0 to see all. Default value is 1000. + :type max_intervals: int. + :param dimension: the dimension to be selected in the intervals + (default is None to mix all dimensions). + :type dimension: int. + :param cmap: A matplotlib colormap (default is + matplotlib.pyplot.cm.hot_r). + :type cmap: cf. matplotlib colormap. + :param legend: Display the color bar values (default is False). + :type legend: boolean. + :returns: A matplotlib object containing diagram plot of persistence + (launch `show()` method on it to display it). + """ + try: + import matplotlib.pyplot as plt + import numpy as np + from scipy.stats import kde + import os + import math + + 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 + + persistence_dim = [] + if dimension is not None: + persistence_dim = [(dim_interval) for dim_interval in persistence if (dim_interval[0] == dimension)] 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 + persistence_dim = persistence + + if max_intervals > 0 and max_intervals < len(persistence_dim): + # Sort by life time, then takes only the max_intervals elements + persistence_dim = sorted(persistence_dim, + key=lambda life_time: life_time[1][1]-life_time[1][0], + reverse=True)[:max_intervals] + + # Set as numpy array birth and death (remove undefined values - inf and NaN) + birth = np.asarray([(interval[1][0]) for interval in persistence_dim if (math.isfinite(interval[1][1]) and math.isfinite(interval[1][0]))]) + death = np.asarray([(interval[1][1]) for interval in persistence_dim if (math.isfinite(interval[1][1]) and math.isfinite(interval[1][0]))]) + + # line display of equation : birth = death + x = np.linspace(death.min(), birth.max(), 1000) + plt.plot(x, x, color='k', linewidth=1.0) + + # Evaluate a gaussian kde on a regular grid of nbins x nbins over data extents + k = kde.gaussian_kde([birth,death], bw_method=bw_method) + xi, yi = np.mgrid[birth.min():birth.max():nbins*1j, death.min():death.max():nbins*1j] + zi = k(np.vstack([xi.flatten(), yi.flatten()])) + + # default cmap value cannot be done at argument definition level as matplotlib is not yet defined. + if cmap is None: + cmap = plt.cm.hot_r + # Make the plot + plt.pcolormesh(xi, yi, zi.reshape(xi.shape), cmap=cmap) + + if legend: + plt.colorbar() + + plt.title('Persistence density') + plt.xlabel('Birth') + plt.ylabel('Death') + return plt + + except ImportError: + print("This function is not available, you may be missing numpy, matplotlib and/or scipy.") diff --git a/src/cython/cython/simplex_tree.pyx b/src/cython/cython/simplex_tree.pyx index e302486b..8397d9d9 100644 --- a/src/cython/cython/simplex_tree.pyx +++ b/src/cython/cython/simplex_tree.pyx @@ -515,7 +515,7 @@ cdef class SimplexTree: :returns: The persistence intervals. :rtype: list of pair of list of int - :note: intervals_in_dim function requires + :note: persistence_pairs function requires :func:`persistence()<gudhi.SimplexTree.persistence>` function to be launched first. """ diff --git a/src/cython/cython/subsampling.pyx b/src/cython/cython/subsampling.pyx index ac09b7a3..e9d61a37 100644 --- a/src/cython/cython/subsampling.pyx +++ b/src/cython/cython/subsampling.pyx @@ -112,7 +112,8 @@ def pick_n_random_points(points=None, off_file='', nb_points=0): return subsampling_n_random_points(points, nb_points) def sparsify_point_set(points=None, off_file='', min_squared_dist=0.0): - """Subsample a point set by picking random vertices. + """Outputs a subset of the input points so that the squared distance + between any two points is greater than or equal to min_squared_dist. :param points: The input point set. :type points: vector[vector[double]]. @@ -122,8 +123,9 @@ def sparsify_point_set(points=None, off_file='', min_squared_dist=0.0): :param off_file: An OFF file style name. :type off_file: string - :param min_squared_dist: Number of points of the subsample. - :type min_squared_dist: unsigned. + :param min_squared_dist: Minimum squared distance separating the output \ + points. + :type min_squared_dist: float. :returns: The subsample point set. :rtype: vector[vector[double]] """ diff --git a/src/cython/doc/installation.rst b/src/cython/doc/installation.rst index 43ff85c5..ef2f7af2 100644 --- a/src/cython/doc/installation.rst +++ b/src/cython/doc/installation.rst @@ -47,9 +47,61 @@ following command in a terminal: export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/cython' ctest -R py_test -If tests fail, please try to :code:`import gudhi` and check the errors. +Debugging issues +================ + +If tests fail, please check your PYTHONPATH and try to :code:`import gudhi` +and check the errors. The problem can come from a third-party library bad link or installation. +If :code:`import gudhi` succeeds, please have a look to debug informations: + +.. code-block:: python + + import gudhi + print(gudhi.__debug_info__) + +You shall have something like: + +.. code-block:: none + + Python version 2.7.15 + Cython version 0.26.1 + Eigen3 version 3.1.1 + Installed modules are: off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; + persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex;alpha_complex; + euclidean_witness_complex;euclidean_strong_witness_complex; + Missing modules are: bottleneck_distance;nerve_gic;subsampling;tangential_complex;persistence_graphical_tools; + CGAL version 4.7.1000 + GMP_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmp.so + GMPXX_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmpxx.so + TBB version 9107 found and used + +Here, you can see that bottleneck_distance, nerve_gic, subsampling and +tangential_complex are missing because of the CGAL version. +persistence_graphical_tools is not available as numpy and matplotlib are not +available. +Unitary tests cannot be run as pytest is missing. + +A complete configuration would be : + +.. code-block:: none + + Python version 3.6.5 + Cython version 0.28.2 + Pytest version 3.3.2 + Matplotlib version 2.2.2 + Numpy version 1.14.5 + Eigen3 version 3.3.4 + Installed modules are: off_reader;simplex_tree;rips_complex;cubical_complex;periodic_cubical_complex; + persistence_graphical_tools;reader_utils;witness_complex;strong_witness_complex;persistence_graphical_tools; + bottleneck_distance;nerve_gic;subsampling;tangential_complex;alpha_complex;euclidean_witness_complex; + euclidean_strong_witness_complex; + CGAL header only version 4.11.0 + GMP_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmp.so + GMPXX_LIBRARIES = /usr/lib/x86_64-linux-gnu/libgmpxx.so + TBB version 9107 found and used + Documentation ============= @@ -143,7 +195,7 @@ The following examples require the `Matplotlib <http://matplotlib.org>`_: * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` -Numpy +NumPy ===== The :doc:`persistence graphical tools </persistence_graphical_tools_user>` @@ -164,6 +216,13 @@ The following examples require the `NumPy <http://numpy.org>`_: * :download:`euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_strong_witness_complex_diagram_persistence_from_off_file_example.py>` * :download:`euclidean_witness_complex_diagram_persistence_from_off_file_example.py <../example/euclidean_witness_complex_diagram_persistence_from_off_file_example.py>` +SciPy +===== + +The :doc:`persistence graphical tools </persistence_graphical_tools_user>` +module requires `SciPy <http://scipy.org>`_, a Python-based ecosystem of +open-source software for mathematics, science, and engineering. + Threading Building Blocks ========================= diff --git a/src/cython/doc/nerve_gic_complex_sum.rst b/src/cython/doc/nerve_gic_complex_sum.rst index 72782c7a..523c119f 100644 --- a/src/cython/doc/nerve_gic_complex_sum.rst +++ b/src/cython/doc/nerve_gic_complex_sum.rst @@ -1,5 +1,5 @@ ================================================================= =================================== =================================== -:Author: Mathieu Carrière :Introduced in: GUDHI 2.1.0 :Copyright: GPL v3 +:Author: Mathieu Carrière :Introduced in: GUDHI 2.3.0 :Copyright: GPL v3 :Requires: CGAL :math:`\geq` 4.8.1 ================================================================= =================================== =================================== diff --git a/src/cython/doc/persistence_graphical_tools_ref.rst b/src/cython/doc/persistence_graphical_tools_ref.rst index a2c6bcef..54aff4bc 100644 --- a/src/cython/doc/persistence_graphical_tools_ref.rst +++ b/src/cython/doc/persistence_graphical_tools_ref.rst @@ -9,3 +9,4 @@ Persistence graphical tools reference manual .. autofunction:: gudhi.__min_birth_max_death .. autofunction:: gudhi.plot_persistence_barcode .. autofunction:: gudhi.plot_persistence_diagram +.. autofunction:: gudhi.plot_persistence_density diff --git a/src/cython/doc/persistence_graphical_tools_sum.inc b/src/cython/doc/persistence_graphical_tools_sum.inc index d602daa7..5577cf99 100644 --- a/src/cython/doc/persistence_graphical_tools_sum.inc +++ b/src/cython/doc/persistence_graphical_tools_sum.inc @@ -1,11 +1,11 @@ ================================================================= =================================== =================================== :Author: Vincent Rouvreau :Introduced in: GUDHI 2.0.0 :Copyright: GPL v3 -:Requires: Matplotlib Numpy +:Requires: matplotlib numpy scipy ================================================================= =================================== =================================== +-----------------------------------------------------------------+-----------------------------------------------------------------------+ | .. figure:: | These graphical tools comes on top of persistence results and allows | -| img/graphical_tools_representation.png | the user to build easily barcode and persistence diagram. | +| img/graphical_tools_representation.png | the user to build easily persistence barcode, diagram or density. | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------------+ | :doc:`persistence_graphical_tools_user` | :doc:`persistence_graphical_tools_ref` | diff --git a/src/cython/doc/persistence_graphical_tools_user.rst b/src/cython/doc/persistence_graphical_tools_user.rst index 292915eb..b2124fdd 100644 --- a/src/cython/doc/persistence_graphical_tools_user.rst +++ b/src/cython/doc/persistence_graphical_tools_user.rst @@ -12,6 +12,9 @@ Definition Show persistence as a barcode ----------------------------- +.. note:: + this function requires matplotlib and numpy to be available + This function can display the persistence result as a barcode: .. plot:: @@ -19,16 +22,22 @@ This function can display the persistence result as a barcode: import gudhi - perseus_file = gudhi.__root_source_dir__ + '/data/bitmap/3d_torus.txt' - periodic_cc = gudhi.PeriodicCubicalComplex(perseus_file=perseus_file) - diag = periodic_cc.persistence() - print("diag = ", diag) - plt = gudhi.plot_persistence_barcode(diag) - plt.show() + off_file = gudhi.__root_source_dir__ + '/data/points/tore3D_300.off' + point_cloud = gudhi.read_off(off_file=off_file) + + rips_complex = gudhi.RipsComplex(points=point_cloud, max_edge_length=0.7) + simplex_tree = rips_complex.create_simplex_tree(max_dimension=3) + diag = simplex_tree.persistence(min_persistence=0.4) + + plot = gudhi.plot_persistence_barcode(diag) + plot.show() Show persistence as a diagram ----------------------------- +.. note:: + this function requires matplotlib and numpy to be available + This function can display the persistence result as a diagram: .. plot:: @@ -43,6 +52,12 @@ This function can display the persistence result as a diagram: legend=True) plt.show() +Persistence density +------------------- + +.. note:: + this function requires matplotlib, numpy and scipy to be available + If you want more information on a specific dimension, for instance: .. plot:: @@ -50,13 +65,9 @@ If you want more information on a specific dimension, for instance: import gudhi + # rips_on_tore3D_1307.pers obtained from write_persistence_diagram method persistence_file=gudhi.__root_source_dir__ + \ '/data/persistence_diagram/rips_on_tore3D_1307.pers' - diag = \ - gudhi.read_persistence_intervals_grouped_by_dimension(persistence_file=\ - persistence_file) - dim = 1 - # Display all points with some transparency - plt = gudhi.plot_persistence_diagram([(dim,interval) for interval in diag[dim]], - max_plots=0, alpha=0.1) + plt = gudhi.plot_persistence_density(persistence_file=persistence_file, + max_intervals=0, dimension=1, legend=True) plt.show() diff --git a/src/cython/gudhi.pyx.in b/src/cython/gudhi.pyx.in index 56a72b04..0d4b966b 100644 --- a/src/cython/gudhi.pyx.in +++ b/src/cython/gudhi.pyx.in @@ -26,6 +26,7 @@ __license__ = "GPL v3" __version__ = "@GUDHI_VERSION@" # This variable is used by doctest to find files __root_source_dir__ = "@CMAKE_SOURCE_DIR@" +__debug_info__ = @GUDHI_CYTHON_DEBUG_INFO@ include '@CMAKE_CURRENT_SOURCE_DIR@/cython/off_reader.pyx' include '@CMAKE_CURRENT_SOURCE_DIR@/cython/simplex_tree.pyx' diff --git a/src/cython/include/Alpha_complex_interface.h b/src/cython/include/Alpha_complex_interface.h index 8cf527fc..faa059d1 100644 --- a/src/cython/include/Alpha_complex_interface.h +++ b/src/cython/include/Alpha_complex_interface.h @@ -60,7 +60,7 @@ class Alpha_complex_interface { Point_d ph = alpha_complex_->get_point(vh); for (auto coord = ph.cartesian_begin(); coord < ph.cartesian_end(); coord++) vd.push_back(*coord); - } catch (std::out_of_range outofrange) { + } catch (std::out_of_range const&) { // std::out_of_range is thrown in case not found. Other exceptions must be re-thrown } return vd; diff --git a/src/cython/include/Nerve_gic_interface.h b/src/cython/include/Nerve_gic_interface.h index 90edd544..aa71e2a6 100644 --- a/src/cython/include/Nerve_gic_interface.h +++ b/src/cython/include/Nerve_gic_interface.h @@ -47,7 +47,7 @@ class Nerve_gic_interface : public Cover_complex<std::vector<double>> { set_cover_from_Voronoi(Gudhi::Euclidean_distance(), m); } double set_graph_from_automatic_euclidean_rips(int N) { - set_graph_from_automatic_rips(Gudhi::Euclidean_distance(), N); + return set_graph_from_automatic_rips(Gudhi::Euclidean_distance(), N); } void set_graph_from_euclidean_rips(double threshold) { set_graph_from_rips(threshold, Gudhi::Euclidean_distance()); 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",], ) |