diff options
-rw-r--r-- | .appveyor.yml | 69 | ||||
-rw-r--r-- | .circleci/config.yml | 91 | ||||
-rw-r--r-- | .travis.yml | 83 | ||||
-rw-r--r-- | Dockerfile_ubuntu | 58 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/Alpha_complex/utilities/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp | 24 | ||||
-rw-r--r-- | src/Alpha_complex/utilities/alphacomplex.md | 1 | ||||
-rw-r--r-- | src/Tangential_complex/doc/Intro_tangential_complex.h | 20 | ||||
-rw-r--r-- | src/Tangential_complex/include/gudhi/Tangential_complex.h | 13 | ||||
-rw-r--r-- | src/Tangential_complex/test/test_tangential_complex.cpp | 30 | ||||
-rw-r--r-- | src/cmake/modules/GUDHI_third_party_libraries.cmake | 23 | ||||
-rw-r--r-- | src/cython/cython/tangential_complex.pyx | 26 | ||||
-rw-r--r-- | src/cython/doc/installation.rst | 56 | ||||
-rw-r--r-- | src/cython/doc/tangential_complex_user.rst | 17 | ||||
-rwxr-xr-x | src/cython/example/tangential_complex_plain_homology_from_off_file_example.py | 1 | ||||
-rw-r--r-- | src/cython/include/Tangential_complex_interface.h | 15 | ||||
-rwxr-xr-x | src/cython/test/test_tangential_complex.py | 9 |
18 files changed, 447 insertions, 99 deletions
diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..86199265 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,69 @@ +# Specify version format +version: "{build}" + +image: + - Visual Studio 2017 + +platform: + - x64 + +# specify custom environment variables +environment: + APPVEYOR_SAVE_CACHE_ON_ERROR: true + +# build configuration, i.e. Debug, Release, etc. +configuration: + - Debug + - Release + +# scripts that are called at very beginning, before repo cloning +init: + - cmd: cmake --version + - cmd: msbuild /version + +# scripts that run after cloning repository +install: + #------------------ + # Windows 10 + #------------------ + # update vcpkg + - cmd: cd C:\tools\vcpkg + - cmd: git pull + - cmd: .\bootstrap-vcpkg.bat + + - cmd: if "%platform%"=="Win32" set VCPKG_ARCH=x86-windows + - cmd: if "%platform%"=="x64" set VCPKG_ARCH=x64-windows + + # remove outdated versions + - cmd: vcpkg remove --outdated --recurse + + # install required dependencies + - cmd: vcpkg install --recurse --triplet %VCPKG_ARCH% zlib boost-date-time boost-program-options boost-system boost-serialization boost-thread boost-units tbb eigen3 cgal[core] + + - cmd: vcpkg integrate install + - cmd: cd "%APPVEYOR_BUILD_FOLDER%" + +# preserve contents of selected directories and files across project builds +for: +- + matrix: + only: + - image: Visual Studio 2017 + cache: + - 'C:\tools\vcpkg\installed' + +build_script: + # get VCG library + - git clone https://github.com/GUDHI/gudhi-devel.git + + #------------------ + # Windows 10 + #------------------ + - cmd: if "%platform%"=="Win32" set CMAKE_GENERATOR=-G"Visual Studio 15 2017" + - cmd: if "%platform%"=="x64" set CMAKE_GENERATOR=-G"Visual Studio 15 2017 Win64" + - cmd: mkdir build && cd build + - cmd: cmake %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%Configuration% -DCMAKE_TOOLCHAIN_FILE="C:\tools\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCG_ROOT="%APPVEYOR_BUILD_FOLDER%\VCG" .. + - cmd: cmake --build . --target ALL_BUILD --config %Configuration% -- /maxcpucount:4 + +test_script: + - cmd: ctest --build-config %Configuration% --output-on-failure diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..b9f0376e --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,91 @@ +version: 2.0 +jobs: + examples: + docker: + - image: gudhi/ci_for_gudhi:latest + steps: + - checkout + - run: + name: Build and test examples + command: | + mkdir build; + cd build; + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=ON -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF ..; + make all test; + + tests: + docker: + - image: gudhi/ci_for_gudhi:latest + steps: + - checkout + - run: + name: Build and test unitary tests + command: | + mkdir build; + cd build; + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF ..; + make all test; + + utils: + docker: + - image: gudhi/ci_for_gudhi:latest + steps: + - checkout + - run: + name: Build and test utilities + command: | + mkdir build; + cd build; + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=OFF ..; + make all test; + + python: + docker: + - image: gudhi/ci_for_gudhi:latest + steps: + - checkout + - run: + name: Build and test python module. Generates and tests the python documentation + command: | + mkdir build; + cd build; + cmake -DUSER_VERSION_DIR=version ..; + make user_version; + cd version; + mkdir build; + cd build; + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=ON -DPython_ADDITIONAL_VERSIONS=3 ..; + make all test sphinx; + cp -R cython/sphinx /tmp/sphinx; + + - store_artifacts: + path: /tmp/sphinx + + doxygen: + docker: + - image: gudhi/ci_for_gudhi:latest + steps: + - checkout + - run: + name: Generates the C++ documentation with doxygen + command: | + mkdir build; + cd build; + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF -DUSER_VERSION_DIR=version ..; + make doxygen 2>&1 | tee dox.log; + grep warning dox.log; + cp dox.log version/doc/html/; + cp -R version/doc/html /tmp/doxygen; + + - store_artifacts: + path: /tmp/doxygen + +workflows: + version: 2 + build: + jobs: + - examples + - tests + - utils + - python + - doxygen
\ No newline at end of file diff --git a/.travis.yml b/.travis.yml index b874123c..b8a080ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,52 +1,68 @@ language: cpp -dist: xenial + sudo: required + git: depth: 3 -env: - matrix: -# Only examples and associated tests - - CMAKE_EXAMPLE='ON' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' -# Only unitary tests - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='ON' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' -# Only utilities and associated tests - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='ON' CMAKE_PYTHON='OFF' MAKE_TARGET='test' -# Only doxygen documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' -# Only Python, associated tests and sphinx documentation - - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + +matrix: + include: + # A. Mac OSX + - os: osx + osx_image: xcode9.4 + compiler: clang + env: + # 1. Only examples and associated tests + - CMAKE_EXAMPLE='ON' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - os: osx + osx_image: xcode9.4 + compiler: clang + env: + # 2. Only unitary tests + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='ON' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - os: osx + osx_image: xcode9.4 + compiler: clang + env: + # 3. Only utilities and associated tests + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='ON' CMAKE_PYTHON='OFF' MAKE_TARGET='test' + - os: osx + osx_image: xcode9.4 + compiler: clang + env: + # 4. Only doxygen documentation + - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='OFF' MAKE_TARGET='doxygen' + # Issue with sphinx-build with sphinx 2.0.1 + # - os: osx + # osx_image: xcode9.4 + # compiler: clang + # env: + # # 5. Only Python, associated tests and sphinx documentation + # - CMAKE_EXAMPLE='OFF' CMAKE_TEST='OFF' CMAKE_UTILITIES='OFF' CMAKE_PYTHON='ON' MAKE_TARGET='test sphinx' + cache: directories: - $HOME/.cache/pip -compiler: gcc + addons: - apt: + homebrew: packages: - cmake - graphviz - doxygen - - libboost-all-dev - - libeigen3-dev - - libgmp3-dev - - libmpfr-dev - - libtbb-dev - - curl + - boost + - eigen + - gmp + - mpfr + - tbb + - cgal - python3 - - python3-pip - - python3-pytest - - python3-setuptools # When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file install: - - curl -LO "https://github.com/CGAL/cgal/releases/download/releases%2FCGAL-4.13/CGAL-4.13.tar.xz" - - tar xf CGAL-4.13.tar.xz - - cd CGAL-4.13 - - cmake -DCMAKE_BUILD_TYPE=Release -DCGAL_HEADER_ONLY=ON . - - make all - - sudo make install - - cd .. - - pip3 install --upgrade pip - - pip3 install --user Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy + - python3 -m pip install --upgrade pip setuptools wheel + - python3 -m pip install --user pytest Cython sphinx sphinxcontrib-bibtex matplotlib numpy scipy + script: - rm -rf build - mkdir -p build @@ -54,6 +70,7 @@ script: - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUDHI_EXAMPLE=${CMAKE_EXAMPLE} -DWITH_GUDHI_TEST=${CMAKE_TEST} -DWITH_GUDHI_UTILITIES=${CMAKE_UTILITIES} -DWITH_GUDHI_PYTHON=${CMAKE_PYTHON} -DUSER_VERSION_DIR=version -DPython_ADDITIONAL_VERSIONS=3 .. - make all ${MAKE_TARGET} - cd .. + notifications: email: on_success: change # default: always diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu new file mode 100644 index 00000000..b80dbc54 --- /dev/null +++ b/Dockerfile_ubuntu @@ -0,0 +1,58 @@ +FROM ubuntu:latest + +# Update and upgrade distribution +RUN apt-get update && \ + apt-get upgrade -y + +# Tools necessary for installing and configuring Ubuntu +RUN apt-get install -y \ + apt-utils \ + locales \ + tzdata + +# Timezone +RUN echo "Europe/Paris" | tee /etc/timezone && \ + ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata + +# Locale with UTF-8 support +RUN echo en_US.UTF-8 UTF-8 >> /etc/locale.gen && \ + locale-gen && \ + update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +# Required for Gudhi compilation +RUN apt-get install -y make \ + g++ \ + cmake \ + graphviz \ + perl \ + texlive-bibtex-extra \ + biber \ + doxygen \ + libboost-all-dev \ + libeigen3-dev \ + libgmp3-dev \ + libmpfr-dev \ + libtbb-dev \ + libcgal-dev \ + locales \ + python3 \ + python3-pip \ + python3-pytest \ + python3-tk \ + libfreetype6-dev \ + pkg-config + +RUN pip3 install \ + numpy \ + matplotlib \ + scipy \ + Cython \ + sphinx \ + sphinxcontrib-bibtex + +# apt clean up +RUN apt autoremove && rm -rf /var/lib/apt/lists/* @@ -1,5 +1,7 @@ [![Build Status](https://travis-ci.org/GUDHI/gudhi-devel.svg?branch=master)](https://travis-ci.org/GUDHI/gudhi-devel) +[![CircleCI](https://circleci.com/gh/GUDHI/gudhi-devel/tree/master.svg?style=svg)](https://circleci.com/gh/GUDHI/gudhi-devel/tree/master) + ![GUDHI](src/common/doc/Gudhi_banner.png "Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding") The GUDHI library is a generic open source C++ library, with a Python interface, for Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding. The library offers state-of-the-art data structures and algorithms to construct simplicial complexes and compute persistent homology. diff --git a/src/Alpha_complex/utilities/CMakeLists.txt b/src/Alpha_complex/utilities/CMakeLists.txt index b12c9690..e76edc5f 100644 --- a/src/Alpha_complex/utilities/CMakeLists.txt +++ b/src/Alpha_complex/utilities/CMakeLists.txt @@ -23,7 +23,7 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_test(NAME Alpha_complex_utilities_alpha_complex_3d COMMAND $<TARGET_FILE:alpha_complex_3d_persistence> "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "alpha.pers") + "-p" "2" "-m" "0.45" "-o" "safe.pers") add_test(NAME Alpha_complex_utilities_exact_alpha_complex_3d COMMAND $<TARGET_FILE:alpha_complex_3d_persistence> "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" @@ -31,13 +31,13 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_test(NAME Alpha_complex_utilities_safe_alpha_complex_3d COMMAND $<TARGET_FILE:alpha_complex_3d_persistence> "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" - "-p" "2" "-m" "0.45" "-o" "safe.pers" "-s") + "-p" "2" "-m" "0.45" "-o" "fast.pers" "-f") if (DIFF_PATH) add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "exact.pers" "alpha.pers") + "exact.pers" "safe.pers") add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH} - "safe.pers" "alpha.pers") + "fast.pers" "safe.pers") endif() add_test(NAME Alpha_complex_utilities_periodic_alpha_complex_3d_persistence COMMAND $<TARGET_FILE:alpha_complex_3d_persistence> diff --git a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp index 19e608ad..09c84eb3 100644 --- a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp +++ b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp @@ -94,11 +94,11 @@ int main(int argc, char **argv) { int coeff_field_characteristic = 0; Filtration_value min_persistence = 0.; bool exact_version = false; - bool safe_version = false; + bool fast_version = false; bool weighted_version = false; bool periodic_version = false; - program_options(argc, argv, off_file_points, exact_version, safe_version, weight_file, cuboid_file, output_file_diag, + program_options(argc, argv, off_file_points, exact_version, fast_version, weight_file, cuboid_file, output_file_diag, alpha_square_max_value, coeff_field_characteristic, min_persistence); std::vector<double> weights; @@ -120,16 +120,16 @@ int main(int argc, char **argv) { periodic_version = true; } - Gudhi::alpha_complex::complexity complexity = Gudhi::alpha_complex::complexity::FAST; + Gudhi::alpha_complex::complexity complexity = Gudhi::alpha_complex::complexity::SAFE; if (exact_version) { - if (safe_version) { - std::cerr << "You cannot set the exact and the safe version." << std::endl; + if (fast_version) { + std::cerr << "You cannot set the exact and the fast version." << std::endl; exit(-1); } complexity = Gudhi::alpha_complex::complexity::EXACT; } - if (safe_version) { - complexity = Gudhi::alpha_complex::complexity::SAFE; + if (fast_version) { + complexity = Gudhi::alpha_complex::complexity::FAST; } Simplex_tree simplex_tree; @@ -258,7 +258,7 @@ int main(int argc, char **argv) { return 0; } -void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &safe, +void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &fast, std::string &weight_file, std::string &cuboid_file, std::string &output_file_diag, Filtration_value &alpha_square_max_value, int &coeff_field_characteristic, Filtration_value &min_persistence) { @@ -270,9 +270,9 @@ void program_options(int argc, char *argv[], std::string &off_file_points, bool po::options_description visible("Allowed options", 100); visible.add_options()("help,h", "produce help message")( "exact,e", po::bool_switch(&exact), - "To activate exact version of Alpha complex 3d (default is false, not available if safe is set)")( - "safe,s", po::bool_switch(&safe), - "To activate safe version of Alpha complex 3d (default is false, not available if exact is set)")( + "To activate exact version of Alpha complex 3d (default is false, not available if fast is set)")( + "fast,f", po::bool_switch(&fast), + "To activate fast version of Alpha complex 3d (default is false, not available if exact is set)")( "weight-file,w", po::value<std::string>(&weight_file)->default_value(std::string()), "Name of file containing a point weights. Format is one weight per line:\n W1\n ...\n Wn ")( "cuboid-file,c", po::value<std::string>(&cuboid_file), @@ -303,7 +303,7 @@ void program_options(int argc, char *argv[], std::string &off_file_points, bool std::cout << std::endl; std::cout << "Compute the persistent homology with coefficient field Z/pZ \n"; std::cout << "of a 3D Alpha complex defined on a set of input points.\n"; - std::cout << "3D Alpha complex can be exact or safe, weighted and/or periodic\n\n"; + std::cout << "3D Alpha complex can be safe (by default) exact or fast, weighted and/or periodic\n\n"; std::cout << "The output diagram contains one bar per line, written with the convention: \n"; std::cout << " p dim b d \n"; std::cout << "where dim is the dimension of the homological feature,\n"; diff --git a/src/Alpha_complex/utilities/alphacomplex.md b/src/Alpha_complex/utilities/alphacomplex.md index 98f56802..50a39d32 100644 --- a/src/Alpha_complex/utilities/alphacomplex.md +++ b/src/Alpha_complex/utilities/alphacomplex.md @@ -107,6 +107,7 @@ It must be in the format described points (one value per line).
* `-e [ --exact ]` for the exact computation version (not compatible with
weight and periodic version).
+* `-f [ --fast ]` for the fast computation version.
**Example**
diff --git a/src/Tangential_complex/doc/Intro_tangential_complex.h b/src/Tangential_complex/doc/Intro_tangential_complex.h index f4fc8ac7..501f4a8b 100644 --- a/src/Tangential_complex/doc/Intro_tangential_complex.h +++ b/src/Tangential_complex/doc/Intro_tangential_complex.h @@ -35,9 +35,11 @@ namespace tangential_complex { \section tangentialdefinition Definition -A Tangential Delaunay complex is a <a target="_blank" href="https://en.wikipedia.org/wiki/Simplicial_complex">simplicial complex</a> +A Tangential Delaunay complex is a +<a target="_blank" href="https://en.wikipedia.org/wiki/Simplicial_complex">simplicial complex</a> designed to reconstruct a \f$k\f$-dimensional smooth manifold embedded in \f$d\f$-dimensional Euclidean space. -The input is a point sample coming from an unknown manifold, which means that the points lie close to a structure of "small" intrinsic dimension. +The input is a point sample coming from an unknown manifold, which means that the points lie close to a structure of +"small" intrinsic dimension. The running time depends only linearly on the extrinsic dimension \f$ d \f$ and exponentially on the intrinsic dimension \f$ k \f$. @@ -46,17 +48,19 @@ An extensive description of the Tangential complex can be found in \cite tangent \subsection whatisthetc What is a Tangential Complex? Let us start with the description of the Tangential complex of a simple example, with \f$ k=1 \f$ and \f$ d=2 \f$. -The input data is 4 points \f$ P \f$ located on a curve embedded in 2D. +The point set \f$ \mathscr P \f$ is located on a closed curve embedded in 2D. +Only 4 points will be displayed (more are required for PCA) to simplify the figures. \image html "tc_example_01.png" "The input" -For each point \f$ p \f$, estimate its tangent subspace \f$ T_p \f$ (e.g. using PCA). +For each point \f$ P \f$, estimate its tangent subspace \f$ T_P \f$ using PCA. \image html "tc_example_02.png" "The estimated normals" -Let us add the Voronoi diagram of the points in orange. For each point \f$ p \f$, construct its star in the Delaunay triangulation of \f$ P \f$ restricted to \f$ T_p \f$. +Let us add the Voronoi diagram of the points in orange. For each point \f$ P \f$, construct its star in the Delaunay +triangulation of \f$ \mathscr P \f$ restricted to \f$ T_P \f$. \image html "tc_example_03.png" "The Voronoi diagram" The Tangential Delaunay complex is the union of those stars. In practice, neither the ambient Voronoi diagram nor the ambient Delaunay triangulation is computed. -Instead, local \f$ k \f$-dimensional regular triangulations are computed with a limited number of points as we only need the star of each point. -More details can be found in \cite tangentialcomplex2014. +Instead, local \f$ k \f$-dimensional regular triangulations are computed with a limited number of points as we only +need the star of each point. More details can be found in \cite tangentialcomplex2014. \subsection inconsistencies Inconsistencies @@ -65,7 +69,7 @@ An inconsistency occurs when a simplex is not in the star of all its vertices. Let us take the same example. \image html "tc_example_07_before.png" "Before" -Let us slightly move the tangent subspace \f$ T_q \f$ +Let us slightly move the tangent subspace \f$ T_Q \f$ \image html "tc_example_07_after.png" "After" Now, the star of \f$ Q \f$ contains \f$ QP \f$, but the star of \f$ P \f$ does not contain \f$ QP \f$. We have an inconsistency. \image html "tc_example_08.png" "After" diff --git a/src/Tangential_complex/include/gudhi/Tangential_complex.h b/src/Tangential_complex/include/gudhi/Tangential_complex.h index 37cdf1b4..4a78127c 100644 --- a/src/Tangential_complex/include/gudhi/Tangential_complex.h +++ b/src/Tangential_complex/include/gudhi/Tangential_complex.h @@ -322,7 +322,11 @@ class Tangential_complex { for (std::size_t i = 0; i < m_points.size(); ++i) m_are_tangent_spaces_computed[i] = true; } - /// Computes the tangential complex. + /** \brief Computes the tangential complex. + * \exception std::invalid_argument In debug mode, if the computed star dimension is too low. Try to set a bigger + * maximal edge length value with `Tangential_complex::set_max_squared_edge_length` if + * this happens. + */ void compute_tangential_complex() { #ifdef GUDHI_TC_PERFORM_EXTRA_CHECKS std::cerr << red << "WARNING: GUDHI_TC_PERFORM_EXTRA_CHECKS is defined. " @@ -1984,6 +1988,13 @@ class Tangential_complex { return os; } + /** \brief Sets the maximal possible squared edge length for the edges in the triangulations. + * + * @param[in] max_squared_edge_length Maximal possible squared edge length. + * + * If the maximal edge length value is too low `Tangential_complex::compute_tangential_complex` will throw an + * exception in debug mode. + */ void set_max_squared_edge_length(FT max_squared_edge_length) { m_max_squared_edge_length = max_squared_edge_length; } private: diff --git a/src/Tangential_complex/test/test_tangential_complex.cpp b/src/Tangential_complex/test/test_tangential_complex.cpp index 4e2d4f65..103b8b30 100644 --- a/src/Tangential_complex/test/test_tangential_complex.cpp +++ b/src/Tangential_complex/test/test_tangential_complex.cpp @@ -126,3 +126,33 @@ BOOST_AUTO_TEST_CASE(test_mini_tangential) { BOOST_CHECK(stree.num_vertices() == 4); BOOST_CHECK(stree.num_simplices() == 6); } + +#ifdef GUDHI_DEBUG +BOOST_AUTO_TEST_CASE(test_basic_example_throw) { + typedef CGAL::Epick_d<CGAL::Dynamic_dimension_tag> Kernel; + typedef Kernel::FT FT; + typedef Kernel::Point_d Point; + typedef Kernel::Vector_d Vector; + typedef tc::Tangential_complex<Kernel, CGAL::Dynamic_dimension_tag,CGAL::Parallel_tag> TC; + + const int INTRINSIC_DIM = 2; + const int AMBIENT_DIM = 3; + const int NUM_POINTS = 1000; + + Kernel k; + + // Generate points on a 2-sphere + CGAL::Random_points_on_sphere_d<Point> generator(AMBIENT_DIM, 3.); + std::vector<Point> points; + points.reserve(NUM_POINTS); + for (int i = 0; i < NUM_POINTS; ++i) + points.push_back(*generator++); + + // Compute the TC + TC tc(points, INTRINSIC_DIM, k); + tc.set_max_squared_edge_length(0.01); + std::cout << "test_basic_example_throw - set_max_squared_edge_length(0.01) to make GUDHI_CHECK fail" << std::endl; + BOOST_CHECK_THROW(tc.compute_tangential_complex(), std::invalid_argument); + +} +#endif diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake index 7b0d350d..57ea7d14 100644 --- a/src/cmake/modules/GUDHI_third_party_libraries.cmake +++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake @@ -148,6 +148,7 @@ if( PYTHONINTERP_FOUND ) find_python_module("matplotlib") find_python_module("numpy") find_python_module("scipy") + find_python_module("sphinx") endif() if(NOT GUDHI_CYTHON_PATH) @@ -157,20 +158,16 @@ endif(NOT GUDHI_CYTHON_PATH) option(WITH_GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS "Build with setting runtime_library_dirs. Usefull when setting rpath is not allowed" ON) if(PYTHONINTERP_FOUND AND CYTHON_FOUND) - # Default found version 2 - if(PYTHON_VERSION_MAJOR EQUAL 2) + if(SPHINX_FOUND) # Documentation generation is available through sphinx find_program( SPHINX_PATH sphinx-build ) - elseif(PYTHON_VERSION_MAJOR EQUAL 3) - # No sphinx-build in Pyton3, just hack it - set(SPHINX_PATH "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${GUDHI_CYTHON_PATH}/doc/python3-sphinx-build.py") - else() - message(FATAL_ERROR "ERROR: Try to compile the Cython interface. Python version ${PYTHON_VERSION_STRING} is not valid.") - endif(PYTHON_VERSION_MAJOR EQUAL 2) - # get PYTHON_SITE_PACKAGES relative path from a python command line - execute_process( - COMMAND "${PYTHON_EXECUTABLE}" -c "from distutils.sysconfig import get_python_lib; print (get_python_lib(prefix='', plat_specific=True))" - OUTPUT_VARIABLE PYTHON_SITE_PACKAGES - OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT SPHINX_PATH) + if(PYTHON_VERSION_MAJOR EQUAL 3) + # In Python3, just hack sphinx-build if it does not exist + set(SPHINX_PATH "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${GUDHI_CYTHON_PATH}/doc/python3-sphinx-build.py") + endif(PYTHON_VERSION_MAJOR EQUAL 3) + endif(NOT SPHINX_PATH) + endif(SPHINX_FOUND) endif(PYTHONINTERP_FOUND AND CYTHON_FOUND) diff --git a/src/cython/cython/tangential_complex.pyx b/src/cython/cython/tangential_complex.pyx index 4bb07076..293ef8cb 100644 --- a/src/cython/cython/tangential_complex.pyx +++ b/src/cython/cython/tangential_complex.pyx @@ -36,6 +36,7 @@ cdef extern from "Tangential_complex_interface.h" namespace "Gudhi": Tangential_complex_interface(int intrisic_dim, vector[vector[double]] points) # bool from_file is a workaround for cython to find the correct signature Tangential_complex_interface(int intrisic_dim, string off_file, bool from_file) + void compute_tangential_complex() except + vector[double] get_point(unsigned vertex) unsigned number_of_vertices() unsigned number_of_simplices() @@ -43,6 +44,7 @@ cdef extern from "Tangential_complex_interface.h" namespace "Gudhi": unsigned number_of_inconsistent_stars() void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree) void fix_inconsistencies_using_perturbation(double max_perturb, double time_limit) + void set_max_squared_edge_length(double max_squared_edge_length) # TangentialComplex python interface cdef class TangentialComplex: @@ -92,6 +94,17 @@ cdef class TangentialComplex: """ return self.thisptr != NULL + def compute_tangential_complex(self): + """This function computes the tangential complex. + + Raises: + ValueError: In debug mode, if the computed star dimension is too + low. Try to set a bigger maximal edge length value with + :func:`~gudhi.Tangential_complex.set_max_squared_edge_length` + if this happens. + """ + self.thisptr.compute_tangential_complex() + def get_point(self, vertex): """This function returns the point corresponding to a given vertex. @@ -152,3 +165,16 @@ cdef class TangentialComplex: """ self.thisptr.fix_inconsistencies_using_perturbation(max_perturb, time_limit) + + def set_max_squared_edge_length(self, max_squared_edge_length): + """Sets the maximal possible squared edge length for the edges in the + triangulations. + + :param max_squared_edge_length: Maximal possible squared edge length. + :type max_squared_edge_length: double + + If the maximal edge length value is too low + :func:`~gudhi.Tangential_complex.compute_tangential_complex` + will throw an exception in debug mode. + """ + self.thisptr.set_max_squared_edge_length(max_squared_edge_length) diff --git a/src/cython/doc/installation.rst b/src/cython/doc/installation.rst index 040f6b4a..855dea44 100644 --- a/src/cython/doc/installation.rst +++ b/src/cython/doc/installation.rst @@ -7,24 +7,23 @@ Installation Compiling ********* -The library uses c++11 and requires `Boost <https://www.boost.org/>`_ ≥ 1.56.0 -and `CMake <https://www.cmake.org/>`_ ≥ 3.1. +The library uses c++11 and requires `Boost <https://www.boost.org/>`_ ≥ 1.56.0, +`CMake <https://www.cmake.org/>`_ ≥ 3.1 to generate makefiles, and +`Cython <https://www.cython.org/>`_ to compile the GUDHI Python module. It is a multi-platform library and compiles on Linux, Mac OSX and Visual Studio 2015. -It also requires cmake to generate makefiles, and cython to compile the -library. On `Windows <https://wiki.python.org/moin/WindowsCompilers>`_ , only Python 3.5 and 3.6 are available because of the required Visual Studio version. -On other systems, if you have several Python/cython installed, the version 2.X +On other systems, if you have several Python/Cython installed, the version 2.X will be used by default, but you can force it by adding :code:`-DPython_ADDITIONAL_VERSIONS=3` to the cmake command. -GUDHI Cythonization -=================== +GUDHI Python module compilation +=============================== -To build the GUDHI cython module, run the following commands in a terminal: +To build the GUDHI Python module, run the following commands in a terminal: .. code-block:: bash @@ -32,7 +31,28 @@ To build the GUDHI cython module, run the following commands in a terminal: mkdir build cd build/ cmake .. - make cython + cd cython + make + +GUDHI Python module installation +================================ + +Once the compilation succeeds, one can add the GUDHI Python module path to the +PYTHONPATH: + +.. code-block:: bash + + # For windows, you have to set PYTHONPATH environment variable + export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/cython' + +Or install it definitely in your Python packages folder: + +.. code-block:: bash + + cd /path-to-gudhi/build/cython + # May require sudo or administrator privileges + make install + Test suites =========== @@ -45,7 +65,7 @@ following command in a terminal: cd /path-to-gudhi/build/cython # For windows, you have to set PYTHONPATH environment variable export PYTHONPATH='$PYTHONPATH:/path-to-gudhi/build/cython' - ctest -R py_test + make test Debugging issues ================ @@ -54,7 +74,7 @@ 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: +If :code:`import gudhi` succeeds, please have a look to debug information: .. code-block:: python @@ -105,13 +125,17 @@ A complete configuration would be : Documentation ============= -To build the documentation, `sphinx-doc <http://http://www.sphinx-doc.org>`_ is -required. Please refer to *conf.py* file to see which -`sphinx-doc <http://http://www.sphinx-doc.org>`_ modules are required to -generate the documentation. Run the following commands in a terminal: +To build the documentation, `sphinx-doc <http://www.sphinx-doc.org>`_ and +`sphinxcontrib-bibtex <https://sphinxcontrib-bibtex.readthedocs.io>`_ are +required. As the documentation is auto-tested, `CGAL`_, `Eigen3`_, +`Matplotlib`_, `NumPy`_ and `SciPy`_ are also mandatory to build the +documentation. + +Run the following commands in a terminal: .. code-block:: bash + cd /path-to-gudhi/build/cython make sphinx Optional third-party library @@ -127,7 +151,7 @@ The :doc:`Alpha complex </alpha_complex_user>`, C++ library which provides easy access to efficient and reliable geometric algorithms. -Having CGAL, the Computational Geometry Algorithms Library, version 4.7.0 or +Having CGAL, the Computational Geometry Algorithms Library, version 4.7.0 or higher installed is recommended. The procedure to install this library according to your operating system is detailed `here <http://doc.cgal.org/latest/Manual/installation.html>`_. diff --git a/src/cython/doc/tangential_complex_user.rst b/src/cython/doc/tangential_complex_user.rst index 5ce69e86..ebfe1e29 100644 --- a/src/cython/doc/tangential_complex_user.rst +++ b/src/cython/doc/tangential_complex_user.rst @@ -23,8 +23,10 @@ What is a Tangential Complex? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Let us start with the description of the Tangential complex of a simple -example, with :math:`k = 1` and :math:`d = 2`. The input data is 4 points -:math:`P` located on a curve embedded in 2D. +example, with :math:`k = 1` and :math:`d = 2`. The point set +:math:`\mathscr P` is located on a closed curve embedded in 2D. +Only 4 points will be displayed (more are required for PCA) to simplify the +figures. .. figure:: ../../doc/Tangential_complex/tc_example_01.png :alt: The input @@ -32,8 +34,7 @@ example, with :math:`k = 1` and :math:`d = 2`. The input data is 4 points The input -For each point :math:`p`, estimate its tangent subspace :math:`T_p` (e.g. -using PCA). +For each point :math:`P`, estimate its tangent subspace :math:`T_P` using PCA. .. figure:: ../../doc/Tangential_complex/tc_example_02.png :alt: The estimated normals @@ -43,8 +44,8 @@ using PCA). Let us add the Voronoi diagram of the points in orange. For each point -:math:`p`, construct its star in the Delaunay triangulation of :math:`P` -restricted to :math:`T_p`. +:math:`P`, construct its star in the Delaunay triangulation of +:math:`\mathscr P` restricted to :math:`T_P`. .. figure:: ../../doc/Tangential_complex/tc_example_03.png :alt: The Voronoi diagram @@ -72,7 +73,7 @@ Let us take the same example. Before -Let us slightly move the tangent subspace :math:`T_q` +Let us slightly move the tangent subspace :math:`T_Q` .. figure:: ../../doc/Tangential_complex/tc_example_07_after.png :alt: After @@ -128,6 +129,7 @@ This example builds the Tangential complex of point set read in an OFF file. import gudhi tc = gudhi.TangentialComplex(intrisic_dim = 1, off_file=gudhi.__root_source_dir__ + '/data/points/alphacomplexdoc.off') + tc.compute_tangential_complex() result_str = 'Tangential contains ' + repr(tc.num_simplices()) + \ ' simplices - ' + repr(tc.num_vertices()) + ' vertices.' print(result_str) @@ -175,6 +177,7 @@ simplices. import gudhi tc = gudhi.TangentialComplex(intrisic_dim = 1, points=[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) + tc.compute_tangential_complex() result_str = 'Tangential contains ' + repr(tc.num_vertices()) + ' vertices.' print(result_str) diff --git a/src/cython/example/tangential_complex_plain_homology_from_off_file_example.py b/src/cython/example/tangential_complex_plain_homology_from_off_file_example.py index 0f8f5e80..536517d1 100755 --- a/src/cython/example/tangential_complex_plain_homology_from_off_file_example.py +++ b/src/cython/example/tangential_complex_plain_homology_from_off_file_example.py @@ -50,6 +50,7 @@ with open(args.file, 'r') as f: print("TangentialComplex creation from points read in a OFF file") tc = gudhi.TangentialComplex(intrisic_dim = args.intrisic_dim, off_file=args.file) + tc.compute_tangential_complex() st = tc.create_simplex_tree() message = "Number of simplices=" + repr(st.num_simplices()) diff --git a/src/cython/include/Tangential_complex_interface.h b/src/cython/include/Tangential_complex_interface.h index 71418886..c4ddbdbe 100644 --- a/src/cython/include/Tangential_complex_interface.h +++ b/src/cython/include/Tangential_complex_interface.h @@ -49,8 +49,6 @@ class Tangential_complex_interface { Dynamic_kernel k; tangential_complex_ = new TC(points, intrisic_dim, k); - tangential_complex_->compute_tangential_complex(); - num_inconsistencies_ = tangential_complex_->number_of_inconsistent_simplices(); } Tangential_complex_interface(int intrisic_dim, const std::string& off_file_name, bool from_file = true) { @@ -60,14 +58,17 @@ class Tangential_complex_interface { std::vector<Point_d> points = off_reader.get_point_cloud(); tangential_complex_ = new TC(points, intrisic_dim, k); - tangential_complex_->compute_tangential_complex(); - num_inconsistencies_ = tangential_complex_->number_of_inconsistent_simplices(); } ~Tangential_complex_interface() { delete tangential_complex_; } + void compute_tangential_complex() { + tangential_complex_->compute_tangential_complex(); + num_inconsistencies_ = tangential_complex_->number_of_inconsistent_simplices(); + } + std::vector<double> get_point(unsigned vh) { std::vector<double> vd; if (vh < tangential_complex_->number_of_vertices()) { @@ -104,7 +105,11 @@ class Tangential_complex_interface { simplex_tree->initialize_filtration(); } - private: + void set_max_squared_edge_length(double max_squared_edge_length) { + tangential_complex_->set_max_squared_edge_length(max_squared_edge_length); + } + +private: TC* tangential_complex_; TC::Num_inconsistencies num_inconsistencies_; }; diff --git a/src/cython/test/test_tangential_complex.py b/src/cython/test/test_tangential_complex.py index 5385a0d3..5c62f278 100755 --- a/src/cython/test/test_tangential_complex.py +++ b/src/cython/test/test_tangential_complex.py @@ -32,6 +32,15 @@ def test_tangential(): tc = TangentialComplex(intrisic_dim = 1, points=point_list) assert tc.__is_defined() == True assert tc.num_vertices() == 4 + assert tc.num_simplices() == 0 + assert tc.num_inconsistent_simplices() == 0 + assert tc.num_inconsistent_stars() == 0 + + tc.compute_tangential_complex() + assert tc.num_vertices() == 4 + assert tc.num_simplices() == 4 + assert tc.num_inconsistent_simplices() == 0 + assert tc.num_inconsistent_stars() == 0 st = tc.create_simplex_tree() assert st.__is_defined() == True |