summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml61
-rw-r--r--.circleci/config.yml91
-rw-r--r--.gitignore17
-rw-r--r--.travis.yml77
-rw-r--r--CMakeGUDHIVersion.txt2
-rw-r--r--CMakeLists.txt15
-rw-r--r--Dockerfile_ubuntu58
-rw-r--r--README11
-rw-r--r--README.md18
-rw-r--r--biblio/bibliography.bib58
-rw-r--r--data/bitmap/sinusoid.txt77
-rw-r--r--data/points/bunny.COPYRIGHT2
-rw-r--r--data/points/grid_5_5_5_in_0_1.off127
-rwxr-xr-xscripts/create_gudhi_version.sh66
-rw-r--r--src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp278
-rw-r--r--src/Alpha_complex/benchmark/CMakeLists.txt9
-rw-r--r--src/Alpha_complex/concept/SimplicialComplexForAlpha.h14
-rw-r--r--src/Alpha_complex/concept/SimplicialComplexForAlpha3d.h57
-rw-r--r--src/Alpha_complex/doc/Intro_alpha_complex.h103
-rw-r--r--src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp56
-rw-r--r--src/Alpha_complex/example/Alpha_complex_from_points.cpp20
-rw-r--r--src/Alpha_complex/example/CMakeLists.txt23
-rw-r--r--src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp52
-rw-r--r--src/Alpha_complex/example/weightedalpha3dfrompoints_for_doc.txt31
-rw-r--r--src/Alpha_complex/include/gudhi/Alpha_complex.h83
-rw-r--r--src/Alpha_complex/include/gudhi/Alpha_complex_3d.h584
-rw-r--r--src/Alpha_complex/include/gudhi/Alpha_complex_options.h45
-rw-r--r--src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp164
-rw-r--r--src/Alpha_complex/test/CMakeLists.txt30
-rw-r--r--src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp190
-rw-r--r--src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp147
-rw-r--r--src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp239
-rw-r--r--src/Alpha_complex/utilities/CMakeLists.txt101
-rw-r--r--src/Alpha_complex/utilities/alpha_complex_3d_helper.h74
-rw-r--r--src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp368
-rw-r--r--src/Alpha_complex/utilities/alphacomplex.md146
-rw-r--r--src/Alpha_complex/utilities/exact_alpha_complex_3d_persistence.cpp265
-rw-r--r--src/Alpha_complex/utilities/periodic_alpha_complex_3d_persistence.cpp302
-rw-r--r--src/Alpha_complex/utilities/weighted_alpha_complex_3d_persistence.cpp316
-rw-r--r--src/Alpha_complex/utilities/weighted_periodic_alpha_complex_3d_persistence.cpp288
-rw-r--r--src/Bitmap_cubical_complex/doc/Gudhi_Cubical_Complex_doc.h11
-rw-r--r--src/Bitmap_cubical_complex/include/gudhi/Bitmap_cubical_complex_base.h37
-rw-r--r--src/Bitmap_cubical_complex/test/Bitmap_test.cpp15
-rw-r--r--src/Bitmap_cubical_complex/test/CMakeLists.txt3
-rw-r--r--src/Bottleneck_distance/doc/Intro_bottleneck_distance.h3
-rw-r--r--src/Bottleneck_distance/include/gudhi/Neighbors_finder.h3
-rw-r--r--src/Bottleneck_distance/utilities/bottleneckdistance.md2
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/Doxyfile.in3
-rw-r--r--src/GudhUI/CMakeLists.txt81
-rw-r--r--src/Nerve_GIC/include/gudhi/GIC.h127
-rw-r--r--src/Persistent_cohomology/example/persistence_from_file.cpp1
-rw-r--r--src/Rips_complex/concept/SimplicialComplexForRips.h21
-rw-r--r--src/Rips_complex/doc/Intro_rips_complex.h29
-rw-r--r--src/Rips_complex/include/gudhi/Rips_complex.h4
-rw-r--r--src/Rips_complex/include/gudhi/Sparse_rips_complex.h79
-rw-r--r--src/Rips_complex/utilities/ripscomplex.md6
-rw-r--r--src/Rips_complex/utilities/sparse_rips_persistence.cpp4
-rw-r--r--src/Simplex_tree/example/cech_complex_cgal_mini_sphere_3d.cpp2
-rw-r--r--src/Simplex_tree/include/gudhi/Simplex_tree.h140
-rw-r--r--src/Simplex_tree/test/CMakeLists.txt8
-rw-r--r--src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp137
-rw-r--r--src/Simplex_tree/test/simplex_tree_unit_test.cpp56
-rw-r--r--src/Tangential_complex/doc/Intro_tangential_complex.h20
-rw-r--r--src/Tangential_complex/example/example_basic.cpp5
-rw-r--r--src/Tangential_complex/include/gudhi/Tangential_complex.h70
-rw-r--r--src/Tangential_complex/test/test_tangential_complex.cpp30
-rw-r--r--src/Toplex_map/benchmark/CMakeLists.txt3
-rw-r--r--src/Toplex_map/benchmark/benchmark_tm.cpp137
-rw-r--r--src/Toplex_map/doc/Intro_Toplex_map.h61
-rw-r--r--src/Toplex_map/doc/map.pngbin0 -> 71815 bytes
-rw-r--r--src/Toplex_map/example/CMakeLists.txt4
-rw-r--r--src/Toplex_map/example/simple_toplex_map.cpp165
-rw-r--r--src/Toplex_map/include/gudhi/Lazy_toplex_map.h271
-rw-r--r--src/Toplex_map/include/gudhi/Toplex_map.h338
-rw-r--r--src/Toplex_map/test/CMakeLists.txt11
-rw-r--r--src/Toplex_map/test/lazy_toplex_map_unit_test.cpp170
-rw-r--r--src/Toplex_map/test/toplex_map_unit_test.cpp138
-rw-r--r--src/Witness_complex/example/example_nearest_landmark_table.cpp21
-rw-r--r--src/Witness_complex/include/gudhi/Strong_witness_complex.h7
-rw-r--r--src/Witness_complex/include/gudhi/Witness_complex.h11
-rw-r--r--src/Witness_complex/utilities/witnesscomplex.md2
-rw-r--r--src/cmake/modules/GUDHI_compilation_flags.cmake13
-rw-r--r--src/cmake/modules/GUDHI_doxygen_target.cmake8
-rw-r--r--src/cmake/modules/GUDHI_modules.cmake5
-rw-r--r--src/cmake/modules/GUDHI_third_party_libraries.cmake36
-rw-r--r--src/cmake/modules/GUDHI_user_version_target.cmake2
-rw-r--r--src/common/benchmark/CMakeLists.txt3
-rw-r--r--src/common/benchmark/Graph_simplicial_complex_benchmark.cpp150
-rw-r--r--src/common/doc/examples.h5
-rw-r--r--src/common/doc/file_formats.h6
-rw-r--r--src/common/doc/installation.h34
-rw-r--r--src/common/doc/main_page.h24
-rw-r--r--src/common/include/gudhi/Unitary_tests_utils.h2
-rw-r--r--src/common/include/gudhi/graph_simplicial_complex.h2
-rw-r--r--src/common/include/gudhi/reader_utils.h4
-rw-r--r--src/cython/CMakeLists.txt679
-rw-r--r--src/cython/cython/nerve_gic.pyx30
-rw-r--r--[-rwxr-xr-x]src/cython/cython/persistence_graphical_tools.py310
-rw-r--r--src/cython/cython/rips_complex.pyx56
-rw-r--r--src/cython/cython/simplex_tree.pyx14
-rw-r--r--src/cython/cython/strong_witness_complex.pyx12
-rw-r--r--src/cython/cython/tangential_complex.pyx26
-rw-r--r--src/cython/cython/witness_complex.pyx12
-rw-r--r--src/cython/doc/cubical_complex_user.rst10
-rw-r--r--src/cython/doc/examples.rst1
-rw-r--r--src/cython/doc/fileformats.rst10
-rw-r--r--src/cython/doc/installation.rst65
-rw-r--r--src/cython/doc/nerve_gic_complex_ref.rst4
-rw-r--r--src/cython/doc/nerve_gic_complex_sum.rst2
-rw-r--r--src/cython/doc/nerve_gic_complex_user.rst4
-rw-r--r--src/cython/doc/persistence_graphical_tools_ref.rst1
-rw-r--r--src/cython/doc/persistence_graphical_tools_sum.inc4
-rw-r--r--src/cython/doc/persistence_graphical_tools_user.rst37
-rw-r--r--src/cython/doc/persistent_cohomology_user.rst14
-rw-r--r--src/cython/doc/rips_complex_sum.inc6
-rw-r--r--src/cython/doc/rips_complex_user.rst67
-rw-r--r--src/cython/doc/tangential_complex_user.rst17
-rwxr-xr-xsrc/cython/example/sparse_rips_persistence_diagram.py43
-rwxr-xr-xsrc/cython/example/tangential_complex_plain_homology_from_off_file_example.py1
-rwxr-xr-xsrc/cython/example/witness_complex_from_nearest_landmark_table.py12
-rw-r--r--src/cython/include/Alpha_complex_interface.h2
-rw-r--r--src/cython/include/Rips_complex_interface.h34
-rw-r--r--src/cython/include/Tangential_complex_interface.h15
-rwxr-xr-xsrc/cython/test/test_rips_complex.py20
-rwxr-xr-xsrc/cython/test/test_tangential_complex.py9
126 files changed, 6217 insertions, 2508 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 00000000..294744b1
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,61 @@
+image:
+ - Visual Studio 2017
+
+build:
+ parallel: true
+ verbosity: detailed
+
+configuration:
+ - Release
+
+environment:
+ # update the vcpkg cache even if build fails
+ APPVEYOR_SAVE_CACHE_ON_ERROR: true
+
+ matrix:
+# - target: Examples
+# CMAKE_FLAGS: -DWITH_GUDHI_EXAMPLE=ON -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF
+# PYTHON: "C:\\Python37-x64"
+#
+# - target: UnitaryTests
+# CMAKE_FLAGS: -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=ON -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=OFF
+# PYTHON: "C:\\Python37-x64"
+#
+# - target: Utilities
+# CMAKE_FLAGS: -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=ON -DWITH_GUDHI_PYTHON=OFF
+# PYTHON: "C:\\Python37-x64"
+
+ - target: Python
+ CMAKE_FLAGS: -DWITH_GUDHI_EXAMPLE=OFF -DWITH_GUDHI_TEST=OFF -DWITH_GUDHI_UTILITIES=OFF -DWITH_GUDHI_PYTHON=ON -DGMP_INCLUDE_DIR="c:/Tools/vcpkg/installed/x64-windows/include" -DGMP_LIBRARIES="c:/Tools/vcpkg/installed/x64-windows/lib/mpir.lib" -DMPFR_INCLUDE_DIR="c:/Tools/vcpkg/installed/x64-windows/include" -DMPFR_LIBRARIES="c:/Tools/vcpkg/installed/x64-windows/lib/mpir.lib"
+ PYTHON: "C:\\Python37-x64"
+
+
+cache:
+ - c:\Tools\vcpkg\installed
+ - '%LOCALAPPDATA%\pip\Cache'
+
+init:
+ - echo %target%
+
+
+install:
+ - vcpkg install tbb:x64-windows boost-disjoint-sets:x64-windows boost-serialization:x64-windows boost-date-time:x64-windows boost-system:x64-windows boost-filesystem:x64-windows boost-units:x64-windows boost-thread:x64-windows boost-program-options:x64-windows eigen3:x64-windows mpfr:x64-windows mpir:x64-windows cgal:x64-windows
+ - SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%
+ - CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64
+ - if [%target%]==[Python] ( python --version &
+ pip --version &
+ python -m pip install --upgrade pip &
+ pip install -U setuptools numpy matplotlib scipy Cython pytest )
+
+build_script:
+ - mkdir build
+ - cd build
+ - cmake -G "Visual Studio 15 2017 Win64" %CMAKE_FLAGS% -DCMAKE_TOOLCHAIN_FILE=c:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
+ - if [%target%]==[Python] (
+ cd src/cython &
+ python setup.py install &
+ MSBuild RUN_TESTS.vcxproj
+ ) else (
+ MSBuild GUDHIdev.sln /m /p:Configuration=Release /p:Platform=x64 &
+ ctest -j 1 -C Release -E diff_files
+ )
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/.gitignore b/.gitignore
new file mode 100644
index 00000000..5c2195be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+# Classical CMake build directory
+build/
+
+# Generated by tests
+data/points/COIL_database/lucky_cat.off_dist
+data/points/COIL_database/lucky_cat.off_sc.dot
+data/points/KleinBottle5D.off_dist
+data/points/KleinBottle5D.off_sc.dot
+data/points/human.off_dist
+data/points/human.off_sc.off
+data/points/human.off_sc.txt
+
+# IDE specific
+# CLion
+.idea/
+cmake-build-debug/
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..b8a080ea
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,77 @@
+language: cpp
+
+sudo: required
+
+git:
+ depth: 3
+
+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
+
+addons:
+ homebrew:
+ packages:
+ - cmake
+ - graphviz
+ - doxygen
+ - boost
+ - eigen
+ - gmp
+ - mpfr
+ - tbb
+ - cgal
+ - python3
+
+# When installing through libcgal-dev apt, CMake Error at CGAL Exports.cmake The imported target "CGAL::CGAL Qt5" references the file
+install:
+ - 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
+ - cd build
+ - 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
+ on_failure: always # default: always
diff --git a/CMakeGUDHIVersion.txt b/CMakeGUDHIVersion.txt
index 6811d7e1..ebaddd47 100644
--- a/CMakeGUDHIVersion.txt
+++ b/CMakeGUDHIVersion.txt
@@ -1,5 +1,5 @@
set (GUDHI_MAJOR_VERSION 2)
-set (GUDHI_MINOR_VERSION 2)
+set (GUDHI_MINOR_VERSION 3)
set (GUDHI_PATCH_VERSION 0)
set(GUDHI_VERSION ${GUDHI_MAJOR_VERSION}.${GUDHI_MINOR_VERSION}.${GUDHI_PATCH_VERSION})
diff --git a/CMakeLists.txt b/CMakeLists.txt
index afacede9..9b97947f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,6 +6,10 @@ include(CMakeGUDHIVersion.txt)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/src/cmake/modules/")
+# Reset cache
+set(GUDHI_MODULES "" CACHE INTERNAL "GUDHI_MODULES")
+set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES")
+
# This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path
set(GUDHI_CYTHON_PATH "src/cython")
@@ -13,8 +17,6 @@ set(GUDHI_CYTHON_PATH "src/cython")
include(GUDHI_third_party_libraries NO_POLICY_SCOPE)
include(GUDHI_compilation_flags)
-# Only for dev version
-add_cxx_compiler_flag("-pedantic")
# Add your new module in the list, order is not important
include(GUDHI_modules)
@@ -34,11 +36,10 @@ add_gudhi_module(Skeleton_blocker)
add_gudhi_module(Spatial_searching)
add_gudhi_module(Subsampling)
add_gudhi_module(Tangential_complex)
+add_gudhi_module(Toplex_map)
add_gudhi_module(Witness_complex)
add_gudhi_module(Nerve_GIC)
-message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"")
-
# Include module CMake subdirectories
# GUDHI_SUB_DIRECTORIES is managed in CMAKE_MODULE_PATH/GUDHI_modules.cmake
foreach(GUDHI_MODULE ${GUDHI_MODULES})
@@ -54,9 +55,15 @@ add_subdirectory(src/GudhUI)
if (WITH_GUDHI_PYTHON)
# specific for cython module
add_subdirectory(${GUDHI_CYTHON_PATH})
+else()
+ message("++ Python module will not be compiled because WITH_GUDHI_PYTHON is set to OFF")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python")
endif()
# For "make user_version" - Requires GUDHI_modules to be performed
include(GUDHI_user_version_target)
# For "make doxygen" - Requires GUDHI_USER_VERSION_DIR to be set - Done in GUDHI_user_version_target for dev version
include(GUDHI_doxygen_target)
+
+message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"")
+message("++ GUDHI_MISSING_MODULES list is:\"${GUDHI_MISSING_MODULES}\"")
diff --git a/Dockerfile_ubuntu b/Dockerfile_ubuntu
new file mode 100644
index 00000000..e149a33a
--- /dev/null
+++ b/Dockerfile_ubuntu
@@ -0,0 +1,58 @@
+FROM ubuntu:19.04
+
+# 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/*
diff --git a/README b/README
deleted file mode 100644
index 2bbe5d14..00000000
--- a/README
+++ /dev/null
@@ -1,11 +0,0 @@
-The Gudhi library is release under the GNU GENERAL PUBLIC LICENSE Version 3. A
-copy of the licence may be found in the file COPYING.
-
-
-To build the library, run in a Terminal:
-
-cd /path-to-gudhi/
-mkdir build
-cd build/
-cmake ..
-make
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..6cc05ff6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+
+[![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.
+
+# Compilation and installation
+
+To install GUDHI, you can follow the [C++ compilation procedure](https://gudhi.inria.fr/doc/latest/installation.html), the [Python compilation procedure](https://gudhi.inria.fr/python/latest/installation.html), use our [conda-forge package](https://gudhi.inria.fr/conda/), or [go with Docker](https://gudhi.inria.fr/dockerfile/).
+
+# More information
+
+* [The GUDHI website](https://gudhi.inria.fr/)
+* [Contact us](https://gudhi.inria.fr/contact/)
+* [Subscribe to the GUDHI users mailing list](https://gudhi.inria.fr/keepintouch/)
+* [Tutorials](https://gudhi.inria.fr/tutorials/)
diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib
index 9dfabdb2..d1b2f558 100644
--- a/biblio/bibliography.bib
+++ b/biblio/bibliography.bib
@@ -1073,9 +1073,10 @@ language={English}
@article{buchet16efficient,
title = {Efficient and Robust Persistent Homology for Measures},
author = {Micka\"{e}l Buchet and Fr\'{e}d\'{e}ric Chazal and Steve Y. Oudot and Donald Sheehy},
- booktitle = {Computational Geometry: Theory and Applications},
+ journal = {Computational Geometry: Theory and Applications},
volume = {58},
pages = {70--96},
+ doi = "https://doi.org/10.1016/j.comgeo.2016.07.001",
year = {2016}
}
@@ -1083,6 +1084,7 @@ language={English}
author = {Nicholas J. Cavanna and Mahmoodreza Jahanseir and Donald R. Sheehy},
booktitle = {Proceedings of the Canadian Conference on Computational Geometry},
title = {A Geometric Perspective on Sparse Filtrations},
+ url = {http://research.cs.queensu.ca/cccg2015/CCCG15-papers/01.pdf},
year = {2015}
}
@@ -1101,9 +1103,63 @@ language={English}
volume = {49},
number = {4},
pages = {778--796},
+ doi = "10.1007/s00454-013-9513-1",
year = {2013}
}
+@InProceedings{boissonnat_et_al:LIPIcs:2015:5098,
+ author = {Jean-Daniel Boissonnat and Karthik C. S. and S{\'e}bastien Tavenas},
+ title = {{Building Efficient and Compact Data Structures for Simplicial Complexes}},
+ booktitle = {31st International Symposium on Computational Geometry (SoCG 2015)},
+ pages = {642--656},
+ series = {Leibniz International Proceedings in Informatics (LIPIcs)},
+ ISBN = {978-3-939897-83-5},
+ ISSN = {1868-8969},
+ year = {2015},
+ volume = {34},
+ editor = {Lars Arge and J{\'a}nos Pach},
+ publisher = {Schloss Dagstuhl--Leibniz-Zentrum fuer Informatik},
+ address = {Dagstuhl, Germany},
+ URL = {http://drops.dagstuhl.de/opus/volltexte/2015/5098},
+ URN = {urn:nbn:de:0030-drops-50981},
+ doi = {10.4230/LIPIcs.SOCG.2015.642},
+ annote = {Keywords: Simplicial complex, Compact data structures, Automaton, NP-hard}
+}
+
+@article{DBLP:journals/corr/BoissonnatS16,
+ author = {Jean{-}Daniel Boissonnat and
+ {Karthik {C. S.}}},
+ title = {An Efficient Representation for Filtrations of Simplicial Complexes},
+ journal = {CoRR},
+ volume = {abs/1607.08449},
+ year = {2016},
+ url = {http://arxiv.org/abs/1607.08449},
+ archivePrefix = {arXiv},
+ eprint = {1607.08449},
+ timestamp = {Mon, 13 Aug 2018 16:46:26 +0200},
+ biburl = {https://dblp.org/rec/bib/journals/corr/BoissonnatS16},
+ bibsource = {dblp computer science bibliography, https://dblp.org}
+}
+
+@article{Kerber:2017:GHC:3047249.3064175,
+ author = {Kerber, Michael and Morozov, Dmitriy and Nigmetov, Arnur},
+ title = {Geometry Helps to Compare Persistence Diagrams},
+ journal = {J. Exp. Algorithmics},
+ issue_date = {2017},
+ volume = {22},
+ month = sep,
+ year = {2017},
+ issn = {1084-6654},
+ pages = {1.4:1--1.4:20},
+ articleno = {1.4},
+ numpages = {20},
+ url = {http://doi.acm.org/10.1145/3064175},
+ doi = {10.1145/3064175},
+ acmid = {3064175},
+ publisher = {ACM},
+ address = {New York, NY, USA},
+ keywords = {Assignment problems, bipartite matching, k-d tree, persistent homology},
+}
@InProceedings{pmlr-v70-carriere17a,
title = {Sliced {W}asserstein Kernel for Persistence Diagrams},
author = {Mathieu Carri{\`e}re and Marco Cuturi and Steve Oudot},
diff --git a/data/bitmap/sinusoid.txt b/data/bitmap/sinusoid.txt
new file mode 100644
index 00000000..4318a790
--- /dev/null
+++ b/data/bitmap/sinusoid.txt
@@ -0,0 +1,77 @@
+1
+75
+10.0
+inf
+6.581647338757176
+4.961839035093842
+3.4462687716225506
+2.0647730896031224
+0.843170409296631
+-0.19751272994853286
+-1.041671820306659
+-1.679592041609764
+-2.1077394076819154
+-2.3288666774217717
+-2.351930856552615
+-2.1918247624544565
+-1.8689306968915753
+-1.4085096027502502
+-0.8399440113300294
+-0.19585746852629526
+0.4888631699173285
+1.178113272962392
+1.835652464698967
+2.4261907229474
+2.916445996426541
+3.2761416444450404
+3.47891380654902
+3.503101508895332
+3.332395828417585
+2.956328659790991
+2.3705864371935323
+1.5771394088427577
+0.5841825900410704
+-0.5941098356746579
+-1.9380093068542301
+-3.4228241352585
+-5.019639498406313
+-6.696183996487342
+-8.417796803538899
+-10.148466428045634
+-11.851909952036825
+-13.492660404643832
+-15.037129682296172
+-16.454615160584872
+-17.71821983034286
+-18.80565838211054
+-19.699925081201553
+-20.389803417622236
+-20.870202256994908
+-21.142308418277846
+-21.213551105635062
+-21.0973792604076
+-20.812858505791425
+-20.38409976350673
+-19.839536666415423
+-19.211073422523185
+-18.53312866828296
+-17.841603966244676
+-17.172807860451854
+-16.562367734888753
+-16.044162085815913
+-15.649305207041936
+-15.405214716400426
+-15.334789869008524
+-15.455725282681154
+-15.779980642668242
+-16.3134222782912
+-17.055647353175157
+-17.999995937972063
+-19.13375060376363
+-20.438517554470188
+-21.890777875994743
+-23.46259238155293
+-25.122438929469578
+-26.836157119615592
+-28.567972056597156
+-30.281566498233445
diff --git a/data/points/bunny.COPYRIGHT b/data/points/bunny.COPYRIGHT
index 310ca5b5..e8c47dd9 100644
--- a/data/points/bunny.COPYRIGHT
+++ b/data/points/bunny.COPYRIGHT
@@ -1,4 +1,4 @@
-The bunny pointset comes from the Stanford Computer Graphics Laboratory.
+The bunny pointsets comes from the Stanford Computer Graphics Laboratory.
http://graphics.stanford.edu/data/3Dscanrep/
Please acknowledge...
diff --git a/data/points/grid_5_5_5_in_0_1.off b/data/points/grid_5_5_5_in_0_1.off
new file mode 100644
index 00000000..148bf773
--- /dev/null
+++ b/data/points/grid_5_5_5_in_0_1.off
@@ -0,0 +1,127 @@
+OFF
+125 0 0
+0.0, 0.0, 0.2
+0.0, 0.0, 0.4
+0.0, 0.0, 0.6
+0.0, 0.0, 0.8
+0.0, 0.2, 0.0
+0.0, 0.2, 0.2
+0.0, 0.2, 0.4
+0.0, 0.2, 0.6
+0.0, 0.2, 0.8
+0.0, 0.4, 0.0
+0.0, 0.4, 0.2
+0.0, 0.4, 0.4
+0.0, 0.4, 0.6
+0.0, 0.4, 0.8
+0.0, 0.6, 0.0
+0.0, 0.6, 0.2
+0.0, 0.6, 0.4
+0.0, 0.6, 0.6
+0.0, 0.6, 0.8
+0.0, 0.8, 0.0
+0.0, 0.8, 0.2
+0.0, 0.8, 0.4
+0.0, 0.8, 0.6
+0.0, 0.8, 0.8
+0.2, 0.0, 0.0
+0.2, 0.0, 0.2
+0.2, 0.0, 0.4
+0.2, 0.0, 0.6
+0.2, 0.0, 0.8
+0.2, 0.2, 0.0
+0.2, 0.2, 0.2
+0.2, 0.2, 0.4
+0.2, 0.2, 0.6
+0.2, 0.2, 0.8
+0.2, 0.4, 0.0
+0.2, 0.4, 0.2
+0.2, 0.4, 0.4
+0.2, 0.4, 0.6
+0.2, 0.4, 0.8
+0.2, 0.6, 0.0
+0.2, 0.6, 0.2
+0.2, 0.6, 0.4
+0.2, 0.6, 0.6
+0.2, 0.6, 0.8
+0.0, 0.0, 0.0
+0.2, 0.8, 0.0
+0.2, 0.8, 0.2
+0.2, 0.8, 0.4
+0.2, 0.8, 0.6
+0.2, 0.8, 0.8
+0.4, 0.0, 0.0
+0.4, 0.0, 0.2
+0.4, 0.0, 0.4
+0.4, 0.0, 0.6
+0.4, 0.0, 0.8
+0.4, 0.2, 0.0
+0.4, 0.2, 0.2
+0.4, 0.2, 0.4
+0.4, 0.2, 0.6
+0.4, 0.2, 0.8
+0.4, 0.4, 0.0
+0.4, 0.4, 0.2
+0.4, 0.4, 0.4
+0.4, 0.4, 0.6
+0.4, 0.4, 0.8
+0.4, 0.6, 0.0
+0.4, 0.6, 0.2
+0.4, 0.6, 0.4
+0.4, 0.6, 0.6
+0.4, 0.6, 0.8
+0.4, 0.8, 0.0
+0.4, 0.8, 0.2
+0.4, 0.8, 0.4
+0.4, 0.8, 0.6
+0.4, 0.8, 0.8
+0.6, 0.0, 0.0
+0.6, 0.0, 0.2
+0.6, 0.0, 0.4
+0.6, 0.0, 0.6
+0.6, 0.0, 0.8
+0.6, 0.1, 0.0
+0.6, 0.2, 0.0
+0.6, 0.2, 0.2
+0.6, 0.2, 0.4
+0.6, 0.2, 0.6
+0.6, 0.2, 0.8
+0.6, 0.4, 0.0
+0.6, 0.4, 0.2
+0.6, 0.4, 0.4
+0.6, 0.4, 0.6
+0.6, 0.4, 0.8
+0.6, 0.6, 0.0
+0.6, 0.6, 0.2
+0.6, 0.6, 0.4
+0.6, 0.6, 0.6
+0.6, 0.6, 0.8
+0.6, 0.8, 0.0
+0.6, 0.8, 0.2
+0.6, 0.8, 0.4
+0.6, 0.8, 0.6
+0.6, 0.8, 0.8
+0.8, 0.0, 0.0
+0.8, 0.0, 0.2
+0.8, 0.0, 0.4
+0.8, 0.0, 0.6
+0.8, 0.0, 0.8
+0.8, 0.2, 0.0
+0.8, 0.2, 0.2
+0.8, 0.2, 0.4
+0.8, 0.2, 0.6
+0.8, 0.2, 0.8
+0.8, 0.4, 0.0
+0.8, 0.4, 0.2
+0.8, 0.4, 0.4
+0.8, 0.4, 0.6
+0.8, 0.4, 0.8
+0.8, 0.6, 0.0
+0.8, 0.6, 0.2
+0.8, 0.6, 0.4
+0.8, 0.6, 0.6
+0.8, 0.6, 0.8
+0.8, 0.8, 0.0
+0.8, 0.8, 0.2
+0.8, 0.8, 0.4
+0.8, 0.8, 0.6
diff --git a/scripts/create_gudhi_version.sh b/scripts/create_gudhi_version.sh
new file mode 100755
index 00000000..f2a9233f
--- /dev/null
+++ b/scripts/create_gudhi_version.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+login="vrouvrea"
+version="2.3.0"
+cgaldir="/home/vincent/workspace/CGAL-4.11-HO/build"
+cpucount=7
+
+
+# We start from scripts dir in the dev branch
+cd ..
+RELATIVEURL=`svn info . |grep -F "Relative URL:" | awk '{print $NF}'`
+
+if [ "$RELATIVEURL" != "^/trunk" ]
+then
+echo "Script must be launched in trunk and not in $RELATIVEURL"
+exit
+fi
+
+rm -rf build; mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_TRACES=ON -DCGAL_DIR=${cgaldir} -DWITH_GUDHI_EXAMPLE=ON -DWITH_GUDHI_BENCHMARK=ON -DPython_ADDITIONAL_VERSIONS=3 ..
+cmake -DCMAKE_BUILD_TYPE=Debug .
+
+CURRENTDIRECTORY=`pwd`
+export PYTHONPATH=$CURRENTDIRECTORY/src/cython:$PYTHONPATH
+
+make -j ${cpucount} all test
+
+cd ..
+svn st | grep -v GUDHIVersion.cmake | grep "^\?" | awk "{print \$2}" | xargs rm -rf
+
+svn copy svn+ssh://${login}@scm.gforge.inria.fr/svnroot/gudhi/trunk svn+ssh://${login}@scm.gforge.inria.fr/svnroot/gudhi/tags/gudhi-release-${version} \
+ -m "Creating a tag of Gudhi release version ${version}."
+
+cd build
+make user_version
+
+userversiondir=`find . -type d -name "*_GUDHI_${version}" | sed 's/\.\///g'`
+echo "User version directory = ${userversiondir}"
+
+tar -czvf ${userversiondir}.tar.gz ${userversiondir}
+
+userdocdir=${userversiondir/GUDHI/GUDHI_DOC}
+echo "User documentation directory = ${userdocdir}"
+mkdir ${userdocdir}
+make doxygen
+
+cp -R ${userversiondir}/doc/html ${userdocdir}/cpp
+cd ${userversiondir}
+rm -rf build; mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./installed -DCGAL_DIR=${cgaldir} -DWITH_GUDHI_EXAMPLE=ON -DPython_ADDITIONAL_VERSIONS=3 ..
+
+CURRENTDIRECTORY=`pwd`
+export PYTHONPATH=$CURRENTDIRECTORY/cython:$PYTHONPATH
+
+make sphinx
+
+cp -R cython/sphinx ../../${userdocdir}/python
+cd ../..
+tar -czvf ${userdocdir}.tar.gz ${userdocdir}
+
+cd ${userversiondir}/build
+make -j ${cpucount} all test install
+
+cd ../..
+actualdir=`pwd`
+echo "Library is available at ${actualdir}/${userversiondir}.tar.gz"
+sha256sum ${userversiondir}.tar.gz
+echo "Documentation is available at ${actualdir}/${userdocdir}.tar.gz"
diff --git a/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp
new file mode 100644
index 00000000..005a712a
--- /dev/null
+++ b/src/Alpha_complex/benchmark/Alpha_complex_3d_benchmark.cpp
@@ -0,0 +1,278 @@
+#include <gudhi/Alpha_complex_3d.h>
+#include <gudhi/Alpha_complex.h>
+// to construct a simplex_tree from alpha complex
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/random_point_generators.h>
+#include <gudhi/Clock.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <limits> // for numeric limits
+#include <fstream>
+
+#include <CGAL/Epick_d.h>
+#include <CGAL/Epeck_d.h>
+#include <CGAL/Random.h>
+
+std::ofstream results_csv("results.csv");
+
+template <typename Kernel>
+void benchmark_points_on_torus_dD(const std::string& msg) {
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"" << msg << "\";" << std::endl;
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"alpha_creation_time(sec.)\";"
+ << "\"complex_creation_time(sec.)\";" << std::endl;
+
+ using K = CGAL::Epick_d<CGAL::Dimension_tag<3>>;
+ for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) {
+ std::cout << " Alpha complex dD on torus with " << nb_points << " points." << std::endl;
+ std::vector<K::Point_d> points_on_torus = Gudhi::generate_points_on_torus_3D<K>(nb_points, 1.0, 0.5);
+ std::vector<typename Kernel::Point_d> points;
+
+ for (auto p : points_on_torus) {
+ points.push_back(typename Kernel::Point_d(p.begin(), p.end()));
+ }
+
+ Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_dD - Alpha complex 3d creation");
+ ac_create_clock.begin();
+ Gudhi::alpha_complex::Alpha_complex<Kernel> alpha_complex_from_points(points);
+ ac_create_clock.end();
+ std::cout << ac_create_clock;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_points_on_torus_dD - complex creation");
+ st_create_clock.begin();
+ alpha_complex_from_points.create_complex(complex);
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << nb_points << ";" << complex.num_simplices() << ";" << ac_create_clock.num_seconds() << ";"
+ << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_points_on_torus_dD - nb simplices = " << complex.num_simplices() << std::endl;
+ }
+}
+
+template <typename Alpha_complex_3d>
+void benchmark_points_on_torus_3D(const std::string& msg) {
+ using K = CGAL::Epick_d<CGAL::Dimension_tag<3>>;
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"" << msg << "\";" << std::endl;
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"alpha_creation_time(sec.)\";"
+ << "\"complex_creation_time(sec.)\";" << std::endl;
+
+ for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) {
+ std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl;
+ std::vector<K::Point_d> points_on_torus = Gudhi::generate_points_on_torus_3D<K>(nb_points, 1.0, 0.5);
+ std::vector<typename Alpha_complex_3d::Point_3> points;
+
+ for (auto p : points_on_torus) {
+ points.push_back(typename Alpha_complex_3d::Point_3(p[0], p[1], p[2]));
+ }
+
+ Gudhi::Clock ac_create_clock(" benchmark_points_on_torus_3D - Alpha complex 3d creation");
+ ac_create_clock.begin();
+ Alpha_complex_3d alpha_complex_from_points(points);
+ ac_create_clock.end();
+ std::cout << ac_create_clock;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_points_on_torus_3D - complex creation");
+ st_create_clock.begin();
+ alpha_complex_from_points.create_complex(complex);
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << nb_points << ";" << complex.num_simplices() << ";" << ac_create_clock.num_seconds() << ";"
+ << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_points_on_torus_3D - nb simplices = " << complex.num_simplices() << std::endl;
+ }
+}
+
+template <typename Weighted_alpha_complex_3d>
+void benchmark_weighted_points_on_torus_3D(const std::string& msg) {
+ using K = CGAL::Epick_d<CGAL::Dimension_tag<3>>;
+
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"" << msg << "\";" << std::endl;
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"alpha_creation_time(sec.)\";"
+ << "\"complex_creation_time(sec.)\";" << std::endl;
+
+ CGAL::Random random(8);
+
+ for (int nb_points = 1000; nb_points <= 125000; nb_points *= 5) {
+ std::cout << " Alpha complex 3d on torus with " << nb_points << " points." << std::endl;
+ std::vector<K::Point_d> points_on_torus = Gudhi::generate_points_on_torus_3D<K>(nb_points, 1.0, 0.5);
+
+ using Point = typename Weighted_alpha_complex_3d::Point_3;
+ using Weighted_point = typename Weighted_alpha_complex_3d::Weighted_point_3;
+
+ std::vector<Weighted_point> points;
+
+ for (auto p : points_on_torus) {
+ points.push_back(Weighted_point(Point(p[0], p[1], p[2]), 0.9 + random.get_double(0., 0.01)));
+ }
+
+ Gudhi::Clock ac_create_clock(" benchmark_weighted_points_on_torus_3D - Alpha complex 3d creation");
+ ac_create_clock.begin();
+ Weighted_alpha_complex_3d alpha_complex_from_points(points);
+ ac_create_clock.end();
+ std::cout << ac_create_clock;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_weighted_points_on_torus_3D - complex creation");
+ st_create_clock.begin();
+ alpha_complex_from_points.create_complex(complex);
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << nb_points << ";" << complex.num_simplices() << ";" << ac_create_clock.num_seconds() << ";"
+ << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_weighted_points_on_torus_3D - nb simplices = " << complex.num_simplices() << std::endl;
+ }
+}
+
+template <typename Periodic_alpha_complex_3d>
+void benchmark_periodic_points(const std::string& msg) {
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"" << msg << "\";" << std::endl;
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"alpha_creation_time(sec.)\";"
+ << "\"complex_creation_time(sec.)\";" << std::endl;
+
+ CGAL::Random random(8);
+
+ for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) {
+ std::cout << " Periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points." << std::endl;
+ using Point = typename Periodic_alpha_complex_3d::Point_3;
+ std::vector<Point> points;
+
+ for (double i = 0; i < nb_points; i++) {
+ for (double j = 0; j < nb_points; j++) {
+ for (double k = 0; k < nb_points; k++) {
+ points.push_back(
+ Point(i + random.get_double(0., 0.1), j + random.get_double(0., 0.1), k + random.get_double(0., 0.1)));
+ }
+ }
+ }
+
+ Gudhi::Clock ac_create_clock(" benchmark_periodic_points - Alpha complex 3d creation");
+ ac_create_clock.begin();
+ Periodic_alpha_complex_3d alpha_complex_from_points(points, 0., 0., 0., nb_points, nb_points, nb_points);
+ ac_create_clock.end();
+ std::cout << ac_create_clock;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_periodic_points - complex creation");
+ st_create_clock.begin();
+ alpha_complex_from_points.create_complex(complex);
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << nb_points * nb_points * nb_points << ";" << complex.num_simplices() << ";"
+ << ac_create_clock.num_seconds() << ";" << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_periodic_points - nb simplices = " << complex.num_simplices() << std::endl;
+ }
+}
+
+template <typename Weighted_periodic_alpha_complex_3d>
+void benchmark_weighted_periodic_points(const std::string& msg) {
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"" << msg << "\";" << std::endl;
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"alpha_creation_time(sec.)\";"
+ << "\"complex_creation_time(sec.)\";" << std::endl;
+
+ CGAL::Random random(8);
+
+ for (double nb_points = 10.; nb_points <= 40.; nb_points += 10.) {
+ std::cout << " Weighted periodic alpha complex 3d with " << nb_points * nb_points * nb_points << " points."
+ << std::endl;
+
+ using Point = typename Weighted_periodic_alpha_complex_3d::Point_3;
+ using Weighted_point = typename Weighted_periodic_alpha_complex_3d::Weighted_point_3;
+ std::vector<Weighted_point> points;
+
+ for (double i = 0; i < nb_points; i++) {
+ for (double j = 0; j < nb_points; j++) {
+ for (double k = 0; k < nb_points; k++) {
+ points.push_back(Weighted_point(
+ Point(i + random.get_double(0., 0.1), j + random.get_double(0., 0.1), k + random.get_double(0., 0.1)),
+ random.get_double(0., (nb_points * nb_points) / 64.)));
+ }
+ }
+ }
+
+ Gudhi::Clock ac_create_clock(" benchmark_weighted_periodic_points - Alpha complex 3d creation");
+ ac_create_clock.begin();
+ Weighted_periodic_alpha_complex_3d alpha_complex_from_points(points, 0., 0., 0., nb_points, nb_points, nb_points);
+ ac_create_clock.end();
+ std::cout << ac_create_clock;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_weighted_periodic_points - complex creation");
+ st_create_clock.begin();
+ alpha_complex_from_points.create_complex(complex);
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << nb_points * nb_points * nb_points << ";" << complex.num_simplices() << ";"
+ << ac_create_clock.num_seconds() << ";" << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_weighted_periodic_points - nb simplices = " << complex.num_simplices() << std::endl;
+ }
+}
+
+int main(int argc, char** argv) {
+ benchmark_points_on_torus_dD<CGAL::Epick_d<CGAL::Dimension_tag<3>>>("Fast static dimension version");
+ benchmark_points_on_torus_dD<CGAL::Epick_d<CGAL::Dynamic_dimension_tag>>("Fast dynamic dimension version");
+ benchmark_points_on_torus_dD<CGAL::Epeck_d<CGAL::Dimension_tag<3>>>("Exact static dimension version");
+ benchmark_points_on_torus_dD<CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>>("Exact dynamic dimension version");
+
+ benchmark_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, false>>("Fast version");
+ benchmark_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>>("Safe version");
+ benchmark_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, false>>("Exact version");
+
+ benchmark_weighted_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, false>>("Fast version");
+ benchmark_weighted_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>>("Safe version");
+ benchmark_weighted_points_on_torus_3D<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, false>>("Exact version");
+
+ benchmark_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, true>>("Fast version");
+ benchmark_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, true>>("Safe version");
+ benchmark_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, true>>("Exact version");
+
+ benchmark_weighted_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, true>>("Fast version");
+ benchmark_weighted_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, true>>("Safe version");
+ benchmark_weighted_periodic_points<
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, true>>("Exact version");
+
+ return 0;
+}
diff --git a/src/Alpha_complex/benchmark/CMakeLists.txt b/src/Alpha_complex/benchmark/CMakeLists.txt
new file mode 100644
index 00000000..622963dc
--- /dev/null
+++ b/src/Alpha_complex/benchmark/CMakeLists.txt
@@ -0,0 +1,9 @@
+project(Alpha_complex_benchmark)
+
+if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
+ add_executable(Alpha_complex_3d_benchmark Alpha_complex_3d_benchmark.cpp)
+ target_link_libraries(Alpha_complex_3d_benchmark ${CGAL_LIBRARY})
+ if (TBB_FOUND)
+ target_link_libraries(Alpha_complex_3d_benchmark ${TBB_LIBRARIES})
+ endif()
+endif ()
diff --git a/src/Alpha_complex/concept/SimplicialComplexForAlpha.h b/src/Alpha_complex/concept/SimplicialComplexForAlpha.h
index a51df127..ba97c802 100644
--- a/src/Alpha_complex/concept/SimplicialComplexForAlpha.h
+++ b/src/Alpha_complex/concept/SimplicialComplexForAlpha.h
@@ -41,9 +41,6 @@ struct SimplicialComplexForAlpha {
/** Returns the number of vertices in the simplicial complex. */
std::size_t num_vertices();
- /** Sets the simplicial complex dimension. */
- void set_dimension(int dimension);
-
/** Gets the 'simplex' dimension. */
int dimension(Simplex_handle simplex);
@@ -65,8 +62,7 @@ struct SimplicialComplexForAlpha {
* 'value type' must be 'Vertex_handle'.*/
typedef unspecified Simplex_vertex_range;
- /** \brief Returns a range over vertices of a given
- * simplex. */
+ /** \brief Returns a range over vertices of a given simplex. */
Simplex_vertex_range simplex_vertex_range(Simplex_handle const & simplex);
/** \brief Iterator over the boundaries of the complex, in an arbitrary order.
@@ -77,6 +73,14 @@ struct SimplicialComplexForAlpha {
/** \brief Returns a range over boundaries of a given simplex. */
Boundary_simplex_range boundary_simplex_range(Simplex_handle const & simplex);
+ /** \brief Iterator over the simplices of the skeleton of the complex, for a given dimension.
+ *
+ * 'value_type' must be 'Simplex_handle'. */
+ typedef unspecified Skeleton_simplex_range;
+ /** \brief Returns a range over the simplices of the skeleton of the simplicial complex, for a given
+ * dimension. */
+ Skeleton_simplex_range skeleton_simplex_range;
+
/** \brief Return type of an insertion of a simplex
*/
typedef unspecified Insertion_result_type;
diff --git a/src/Alpha_complex/concept/SimplicialComplexForAlpha3d.h b/src/Alpha_complex/concept/SimplicialComplexForAlpha3d.h
new file mode 100644
index 00000000..7acdf105
--- /dev/null
+++ b/src/Alpha_complex/concept/SimplicialComplexForAlpha3d.h
@@ -0,0 +1,57 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2018 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONCEPT_ALPHA_COMPLEX_SIMPLICIAL_COMPLEX_FOR_ALPHA_3D_H_
+#define CONCEPT_ALPHA_COMPLEX_SIMPLICIAL_COMPLEX_FOR_ALPHA_3D_H_
+
+namespace Gudhi {
+
+namespace alpha_complex {
+
+/** \brief The concept SimplicialComplexForAlpha3d describes the requirements for a type to implement a simplicial
+ * complex, that can be created from a `Alpha_complex_3d`.
+ */
+struct SimplicialComplexForAlpha3d {
+ /** Handle to specify a vertex. Must be a non-negative integer. */
+ typedef unspecified Vertex_handle;
+ /** Handle to specify the simplex filtration value. */
+ typedef unspecified Filtration_value;
+
+ /** Returns the number of vertices in the simplicial complex. */
+ std::size_t num_vertices();
+
+ /** \brief Inserts a simplex from a given simplex (represented by a vector of Vertex_handle) in the
+ * simplicial complex with the given 'filtration' value. */
+ void insert_simplex(std::vector<Vertex_handle> const& vertex_range, Filtration_value filtration);
+
+ /** Browses the simplicial complex to make the filtration non-decreasing. */
+ void make_filtration_non_decreasing();
+
+ /** Prune the simplicial complex above 'filtration' value given as parameter. */
+ void prune_above_filtration(Filtration_value filtration);
+};
+
+} // namespace alpha_complex
+
+} // namespace Gudhi
+
+#endif // CONCEPT_ALPHA_COMPLEX_SIMPLICIAL_COMPLEX_FOR_ALPHA_3D_H_
diff --git a/src/Alpha_complex/doc/Intro_alpha_complex.h b/src/Alpha_complex/doc/Intro_alpha_complex.h
index 7a375c9f..ab7c4794 100644
--- a/src/Alpha_complex/doc/Intro_alpha_complex.h
+++ b/src/Alpha_complex/doc/Intro_alpha_complex.h
@@ -29,34 +29,34 @@ namespace Gudhi {
namespace alpha_complex {
/** \defgroup alpha_complex Alpha complex
- *
+ *
* \author Vincent Rouvreau
*
* @{
- *
+ *
* \section definition Definition
- *
+ *
* Alpha_complex is a <a target="_blank" href="https://en.wikipedia.org/wiki/Simplicial_complex">simplicial complex</a>
* constructed from the finite cells of a Delaunay Triangulation.
- *
+ *
* The filtration value of each simplex is computed as the square of the circumradius of the simplex if the
* circumsphere is empty (the simplex is then said to be Gabriel), and as the minimum of the filtration
* values of the codimension 1 cofaces that make it not Gabriel otherwise.
- *
+ *
* All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into
* the complex.
- *
+ *
* \image html "alpha_complex_representation.png" "Alpha-complex representation"
- *
+ *
* Alpha_complex is constructing a <a target="_blank"
* href="http://doc.cgal.org/latest/Triangulation/index.html#Chapter_Triangulations">Delaunay Triangulation</a>
* \cite cgal:hdj-t-15b from <a target="_blank" href="http://www.cgal.org/">CGAL</a> (the Computational Geometry
* Algorithms Library \cite cgal:eb-15b) and is able to create a `SimplicialComplexForAlpha`.
- *
+ *
* The complex is a template class requiring an Epick_d <a target="_blank"
* href="http://doc.cgal.org/latest/Kernel_d/index.html#Chapter_dD_Geometry_Kernel">dD Geometry Kernel</a>
* \cite cgal:s-gkd-15b from CGAL as template parameter.
- *
+ *
* \remark
* - When the simplicial complex is constructed with an infinite value of alpha, the complex is a Delaunay
* complex.
@@ -65,30 +65,30 @@ namespace alpha_complex {
* \ref cech_complex can still make sense in higher dimension precisely because you can bound the radii.
*
* \section pointsexample Example from points
- *
+ *
* This example builds the Delaunay triangulation from the given points in a 2D static kernel, and creates a
* `Simplex_tree` with it.
- *
+ *
* Then, it is asked to display information about the simplicial complex.
- *
+ *
* \include Alpha_complex/Alpha_complex_from_points.cpp
- *
+ *
* When launching:
- *
+ *
* \code $> ./Alpha_complex_example_from_points
* \endcode
*
* the program output is:
- *
+ *
* \include Alpha_complex/alphaoffreader_for_doc_60.txt
- *
+ *
* \section createcomplexalgorithm Create complex algorithm
- *
+ *
* \subsection datastructure Data structure
- *
+ *
* In order to create the simplicial complex, first, it is built from the cells of the Delaunay Triangulation.
* The filtration values are set to NaN, which stands for unknown value.
- *
+ *
* In example, :
* \image html "alpha_complex_doc.png" "Simplicial complex structure construction example"
*
@@ -106,7 +106,7 @@ namespace alpha_complex {
* \quad\quad\quad\quad \text{filtration(} \tau \text{) = min( filtration(} \tau \text{), filtration(} \sigma
* \text{) )}\\
* \quad\quad\quad \textbf{else}\\
- * \quad\quad\quad\quad \textbf{if } \textbf{if } \tau \text{ is not Gabriel for } \sigma \textbf{ then}\\
+ * \quad\quad\quad\quad \textbf{if } \tau \text{ is not Gabriel for } \sigma \textbf{ then}\\
* \quad\quad\quad\quad\quad \text{filtration(} \tau \text{) = filtration(} \sigma \text{)}\\
* \quad\quad\quad\quad \textbf{end if}\\
* \quad\quad\quad \textbf{end if}\\
@@ -118,53 +118,78 @@ namespace alpha_complex {
* \f$
*
* \subsubsection dimension2 Dimension 2
- *
+ *
* From the example above, it means the algorithm looks into each triangle ([0,1,2], [0,2,4], [1,2,3], ...),
* computes the filtration value of the triangle, and then propagates the filtration value as described
* here :
* \image html "alpha_complex_doc_420.png" "Filtration value propagation example"
- *
+ *
* \subsubsection dimension1 Dimension 1
- *
+ *
* Then, the algorithm looks into each edge ([0,1], [0,2], [1,2], ...),
* computes the filtration value of the edge (in this case, propagation will have no effect).
- *
+ *
* \subsubsection dimension0 Dimension 0
- *
+ *
* Finally, the algorithm looks into each vertex ([0], [1], [2], [3], [4], [5] and [6]) and
* sets the filtration value (0 in case of a vertex - propagation will have no effect).
- *
+ *
* \subsubsection nondecreasing Non decreasing filtration values
- *
+ *
* As the squared radii computed by CGAL are an approximation, it might happen that these alpha squared values do not
* quite define a proper filtration (i.e. non-decreasing with respect to inclusion).
* We fix that up by calling `SimplicialComplexForAlpha::make_filtration_non_decreasing()`.
- *
+ *
* \subsubsection pruneabove Prune above given filtration value
- *
+ *
* The simplex tree is pruned from the given maximum alpha squared value (cf.
* `SimplicialComplexForAlpha::prune_above_filtration()`).
* In the following example, the value is given by the user as argument of the program.
- *
- *
+ *
+ *
* \section offexample Example from OFF file
- *
+ *
* This example builds the Delaunay triangulation in a dynamic kernel, and initializes the alpha complex with it.
- *
- *
+ *
+ *
* Then, it is asked to display information about the alpha complex.
- *
+ *
* \include Alpha_complex/Alpha_complex_from_off.cpp
- *
+ *
* When launching:
- *
+ *
* \code $> ./Alpha_complex_example_from_off ../../data/points/alphacomplexdoc.off 32.0
* \endcode
*
* the program output is:
- *
+ *
* \include Alpha_complex/alphaoffreader_for_doc_32.txt
- *
+ *
+ *
+ * \section weighted3dexample 3d specific example
+ *
+ * A specific module for Alpha complex is available in 3d (cf. Alpha_complex_3d) and allows to construct standard,
+ * weighted, periodic or weighted and periodic versions of alpha complexes. Alpha values computation can be
+ * Gudhi::alpha_complex::complexity::FAST, Gudhi::alpha_complex::complexity::SAFE (default value) or
+ * Gudhi::alpha_complex::complexity::EXACT.
+ *
+ * This example builds the CGAL 3d weighted alpha shapes from a small molecule, and initializes the alpha complex with
+ * it. This example is taken from <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html#title13">CGAL 3d
+ * weighted alpha shapes</a>.
+ *
+ * Then, it is asked to display information about the alpha complex.
+ *
+ * \include Alpha_complex/Weighted_alpha_complex_3d_from_points.cpp
+ *
+ * When launching:
+ *
+ * \code $> ./Alpha_complex_example_weighted_3d_from_points
+ * \endcode
+ *
+ * the program output is:
+ *
+ * \include Alpha_complex/weightedalpha3dfrompoints_for_doc.txt
+ *
*/
/** @} */ // end defgroup alpha_complex
diff --git a/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp
new file mode 100644
index 00000000..0e359a27
--- /dev/null
+++ b/src/Alpha_complex/example/Alpha_complex_3d_from_points.cpp
@@ -0,0 +1,56 @@
+#include <gudhi/Alpha_complex_3d.h>
+// to construct a simplex_tree from alpha complex
+#include <gudhi/Simplex_tree.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <limits> // for numeric limits
+
+using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>;
+using Point = Alpha_complex_3d::Point_3;
+using Vector_of_points = std::vector<Point>;
+
+int main(int argc, char **argv) {
+ if (argc != 1) {
+ std::cerr << "Error: Number of arguments (" << argc << ") is not correct\n";
+ std::cerr << "Usage: " << (argv[0] - 1) << " \n";
+ exit(-1); // ----- >>
+ }
+
+ // ----------------------------------------------------------------------------
+ // Init of a list of points from a small molecule
+ // ----------------------------------------------------------------------------
+ Vector_of_points points;
+ points.push_back(Point(1, -1, -1));
+ points.push_back(Point(-1, 1, -1));
+ points.push_back(Point(-1, -1, 1));
+ points.push_back(Point(1, 1, 1));
+ points.push_back(Point(2, 2, 2));
+
+ // ----------------------------------------------------------------------------
+ // Init of an alpha complex from the list of points
+ // ----------------------------------------------------------------------------
+ Alpha_complex_3d alpha_complex_from_points(points);
+
+ Gudhi::Simplex_tree<> simplex;
+ if (alpha_complex_from_points.create_complex(simplex)) {
+ // ----------------------------------------------------------------------------
+ // Display information about the alpha complex
+ // ----------------------------------------------------------------------------
+ std::cout << "Alpha complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices()
+ << " simplices - " << simplex.num_vertices() << " vertices." << std::endl;
+
+ std::cout << "Iterator on alpha complex simplices in the filtration order, with [filtration value]:" << std::endl;
+ for (auto f_simplex : simplex.filtration_simplex_range()) {
+ std::cout << " ( ";
+ for (auto vertex : simplex.simplex_vertex_range(f_simplex)) {
+ std::cout << vertex << " ";
+ }
+ std::cout << ") -> "
+ << "[" << simplex.filtration(f_simplex) << "] ";
+ std::cout << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/src/Alpha_complex/example/Alpha_complex_from_points.cpp b/src/Alpha_complex/example/Alpha_complex_from_points.cpp
index c19f7cc8..981aa470 100644
--- a/src/Alpha_complex/example/Alpha_complex_from_points.cpp
+++ b/src/Alpha_complex/example/Alpha_complex_from_points.cpp
@@ -5,29 +5,13 @@
#include <CGAL/Epick_d.h>
#include <iostream>
-#include <string>
#include <vector>
-#include <limits> // for numeric limits
using Kernel = CGAL::Epick_d< CGAL::Dimension_tag<2> >;
using Point = Kernel::Point_d;
using Vector_of_points = std::vector<Point>;
-void usage(int nbArgs, char * const progName) {
- std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n";
- std::cerr << "Usage: " << progName << " [alpha_square_max_value]\n";
- std::cerr << " i.e.: " << progName << " 60.0\n";
- exit(-1); // ----- >>
-}
-
-int main(int argc, char **argv) {
- if ((argc != 1) && (argc != 2)) usage(argc, (argv[0] - 1));
-
- // Delaunay complex if alpha_square_max_value is not given by the user.
- double alpha_square_max_value {std::numeric_limits<double>::infinity()};
- if (argc == 2)
- alpha_square_max_value = atof(argv[1]);
-
+int main() {
// ----------------------------------------------------------------------------
// Init of a list of points
// ----------------------------------------------------------------------------
@@ -46,7 +30,7 @@ int main(int argc, char **argv) {
Gudhi::alpha_complex::Alpha_complex<Kernel> alpha_complex_from_points(points);
Gudhi::Simplex_tree<> simplex;
- if (alpha_complex_from_points.create_complex(simplex, alpha_square_max_value)) {
+ if (alpha_complex_from_points.create_complex(simplex)) {
// ----------------------------------------------------------------------------
// Display information about the alpha complex
// ----------------------------------------------------------------------------
diff --git a/src/Alpha_complex/example/CMakeLists.txt b/src/Alpha_complex/example/CMakeLists.txt
index 2fc62452..c62a220c 100644
--- a/src/Alpha_complex/example/CMakeLists.txt
+++ b/src/Alpha_complex/example/CMakeLists.txt
@@ -1,7 +1,5 @@
project(Alpha_complex_examples)
-# need CGAL 4.7
-# cmake -DCGAL_DIR=~/workspace/CGAL-4.7 ..
if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
add_executable ( Alpha_complex_example_from_points Alpha_complex_from_points.cpp )
target_link_libraries(Alpha_complex_example_from_points ${CGAL_LIBRARY})
@@ -28,8 +26,23 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
add_test(Alpha_complex_example_from_off_32_diff_files ${DIFF_PATH}
${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_result_32.txt ${CMAKE_CURRENT_BINARY_DIR}/alphaoffreader_for_doc_32.txt)
endif()
+endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
- install(TARGETS Alpha_complex_example_from_points DESTINATION bin)
- install(TARGETS Alpha_complex_example_from_off DESTINATION bin)
+if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
+ add_executable ( Alpha_complex_example_weighted_3d_from_points Weighted_alpha_complex_3d_from_points.cpp )
+ target_link_libraries(Alpha_complex_example_weighted_3d_from_points ${CGAL_LIBRARY})
+ if (TBB_FOUND)
+ target_link_libraries(Alpha_complex_example_weighted_3d_from_points ${TBB_LIBRARIES})
+ endif()
+ add_test(NAME Alpha_complex_example_weighted_3d_from_points
+ COMMAND $<TARGET_FILE:Alpha_complex_example_weighted_3d_from_points>)
-endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
+ add_executable ( Alpha_complex_example_3d_from_points Alpha_complex_3d_from_points.cpp )
+ target_link_libraries(Alpha_complex_example_3d_from_points ${CGAL_LIBRARY})
+ if (TBB_FOUND)
+ target_link_libraries(Alpha_complex_example_3d_from_points ${TBB_LIBRARIES})
+ endif()
+ add_test(NAME Alpha_complex_example_3d_from_points
+ COMMAND $<TARGET_FILE:Alpha_complex_example_3d_from_points>)
+
+endif(NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
diff --git a/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp
new file mode 100644
index 00000000..ac11b68c
--- /dev/null
+++ b/src/Alpha_complex/example/Weighted_alpha_complex_3d_from_points.cpp
@@ -0,0 +1,52 @@
+#include <gudhi/Alpha_complex_3d.h>
+// to construct a simplex_tree from alpha complex
+#include <gudhi/Simplex_tree.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <limits> // for numeric limits
+
+// Complexity = FAST, weighted = true, periodic = false
+using Weighted_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>;
+using Point = Weighted_alpha_complex_3d::Point_3;
+using Weighted_point = Weighted_alpha_complex_3d::Weighted_point_3;
+
+int main(int argc, char **argv) {
+ // ----------------------------------------------------------------------------
+ // Init of a list of points and weights from a small molecule
+ // ----------------------------------------------------------------------------
+ std::vector<Weighted_point> weighted_points;
+ weighted_points.push_back(Weighted_point(Point(1, -1, -1), 4.));
+ weighted_points.push_back(Weighted_point(Point(-1, 1, -1), 4.));
+ weighted_points.push_back(Weighted_point(Point(-1, -1, 1), 4.));
+ weighted_points.push_back(Weighted_point(Point(1, 1, 1), 4.));
+ weighted_points.push_back(Weighted_point(Point(2, 2, 2), 1.));
+
+ // ----------------------------------------------------------------------------
+ // Init of an alpha complex from the list of points
+ // ----------------------------------------------------------------------------
+ Weighted_alpha_complex_3d alpha_complex_from_points(weighted_points);
+
+ Gudhi::Simplex_tree<> simplex;
+ if (alpha_complex_from_points.create_complex(simplex)) {
+ // ----------------------------------------------------------------------------
+ // Display information about the alpha complex
+ // ----------------------------------------------------------------------------
+ std::cout << "Alpha complex is of dimension " << simplex.dimension() << " - " << simplex.num_simplices()
+ << " simplices - " << simplex.num_vertices() << " vertices." << std::endl;
+
+ std::cout << "Iterator on alpha complex simplices in the filtration order, with [filtration value]:" << std::endl;
+ for (auto f_simplex : simplex.filtration_simplex_range()) {
+ std::cout << " ( ";
+ for (auto vertex : simplex.simplex_vertex_range(f_simplex)) {
+ std::cout << vertex << " ";
+ }
+ std::cout << ") -> "
+ << "[" << simplex.filtration(f_simplex) << "] ";
+ std::cout << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/src/Alpha_complex/example/weightedalpha3dfrompoints_for_doc.txt b/src/Alpha_complex/example/weightedalpha3dfrompoints_for_doc.txt
new file mode 100644
index 00000000..7a09998d
--- /dev/null
+++ b/src/Alpha_complex/example/weightedalpha3dfrompoints_for_doc.txt
@@ -0,0 +1,31 @@
+Alpha complex is of dimension 3 - 29 simplices - 5 vertices.
+Iterator on alpha complex simplices in the filtration order, with [filtration value]:
+ ( 0 ) -> [-4]
+ ( 1 ) -> [-4]
+ ( 2 ) -> [-4]
+ ( 3 ) -> [-4]
+ ( 1 0 ) -> [-2]
+ ( 2 0 ) -> [-2]
+ ( 2 1 ) -> [-2]
+ ( 3 0 ) -> [-2]
+ ( 3 1 ) -> [-2]
+ ( 3 2 ) -> [-2]
+ ( 2 1 0 ) -> [-1.33333]
+ ( 3 1 0 ) -> [-1.33333]
+ ( 3 2 0 ) -> [-1.33333]
+ ( 3 2 1 ) -> [-1.33333]
+ ( 3 2 1 0 ) -> [-1]
+ ( 4 ) -> [-1]
+ ( 4 2 ) -> [-1]
+ ( 4 0 ) -> [23]
+ ( 4 1 ) -> [23]
+ ( 4 2 0 ) -> [23]
+ ( 4 2 1 ) -> [23]
+ ( 4 3 ) -> [23]
+ ( 4 3 2 ) -> [23]
+ ( 4 1 0 ) -> [95]
+ ( 4 2 1 0 ) -> [95]
+ ( 4 3 0 ) -> [95]
+ ( 4 3 1 ) -> [95]
+ ( 4 3 2 0 ) -> [95]
+ ( 4 3 2 1 ) -> [95]
diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex.h b/src/Alpha_complex/include/gudhi/Alpha_complex.h
index 4c07eddb..af9f59ea 100644
--- a/src/Alpha_complex/include/gudhi/Alpha_complex.h
+++ b/src/Alpha_complex/include/gudhi/Alpha_complex.h
@@ -228,19 +228,14 @@ class Alpha_complex {
}
public:
- template <typename SimplicialComplexForAlpha>
- bool create_complex(SimplicialComplexForAlpha& complex) {
- typedef typename SimplicialComplexForAlpha::Filtration_value Filtration_value;
- return create_complex(complex, std::numeric_limits<Filtration_value>::infinity());
- }
-
/** \brief Inserts all Delaunay triangulation into the simplicial complex.
* It also computes the filtration values accordingly to the \ref createcomplexalgorithm
*
* \tparam SimplicialComplexForAlpha must meet `SimplicialComplexForAlpha` concept.
*
* @param[in] complex SimplicialComplexForAlpha to be created.
- * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$.
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$, and there is very
+ * little point using anything else since it does not save time.
*
* @return true if creation succeeds, false otherwise.
*
@@ -249,8 +244,10 @@ class Alpha_complex {
*
* Initialization can be launched once.
*/
- template <typename SimplicialComplexForAlpha, typename Filtration_value>
- bool create_complex(SimplicialComplexForAlpha& complex, Filtration_value max_alpha_square) {
+ template <typename SimplicialComplexForAlpha,
+ typename Filtration_value = typename SimplicialComplexForAlpha::Filtration_value>
+ bool create_complex(SimplicialComplexForAlpha& complex,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity()) {
// From SimplicialComplexForAlpha type required to insert into a simplicial complex (with or without subfaces).
typedef typename SimplicialComplexForAlpha::Vertex_handle Vertex_handle;
typedef typename SimplicialComplexForAlpha::Simplex_handle Simplex_handle;
@@ -272,7 +269,9 @@ class Alpha_complex {
// --------------------------------------------------------------------------------------------
// Simplex_tree construction from loop on triangulation finite full cells list
if (triangulation_->number_of_vertices() > 0) {
- for (auto cit = triangulation_->finite_full_cells_begin(); cit != triangulation_->finite_full_cells_end(); ++cit) {
+ for (auto cit = triangulation_->finite_full_cells_begin();
+ cit != triangulation_->finite_full_cells_end();
+ ++cit) {
Vector_vertex vertexVector;
#ifdef DEBUG_TRACES
std::cout << "Simplex_tree insertion ";
@@ -333,7 +332,9 @@ class Alpha_complex {
std::cout << "filt(Sigma) is NaN : filt(Sigma) =" << complex.filtration(f_simplex) << std::endl;
#endif // DEBUG_TRACES
}
- propagate_alpha_filtration(complex, f_simplex, decr_dim);
+ // No need to propagate further, unweighted points all have value 0
+ if (decr_dim > 1)
+ propagate_alpha_filtration(complex, f_simplex);
}
}
}
@@ -350,7 +351,7 @@ class Alpha_complex {
private:
template <typename SimplicialComplexForAlpha, typename Simplex_handle>
- void propagate_alpha_filtration(SimplicialComplexForAlpha& complex, Simplex_handle f_simplex, int decr_dim) {
+ void propagate_alpha_filtration(SimplicialComplexForAlpha& complex, Simplex_handle f_simplex) {
// From SimplicialComplexForAlpha type required to assign filtration values.
typedef typename SimplicialComplexForAlpha::Filtration_value Filtration_value;
#ifdef DEBUG_TRACES
@@ -379,46 +380,42 @@ class Alpha_complex {
#endif // DEBUG_TRACES
// ### Else
} else {
- // No need to compute is_gabriel for dimension <= 2
- // i.e. : Sigma = (3,1) => Tau = 1
- if (decr_dim > 1) {
- // insert the Tau points in a vector for is_gabriel function
- Vector_of_CGAL_points pointVector;
+ // insert the Tau points in a vector for is_gabriel function
+ Vector_of_CGAL_points pointVector;
#ifdef DEBUG_TRACES
- Vertex_handle vertexForGabriel = Vertex_handle();
+ Vertex_handle vertexForGabriel = Vertex_handle();
#endif // DEBUG_TRACES
- for (auto vertex : complex.simplex_vertex_range(f_boundary)) {
- pointVector.push_back(get_point(vertex));
- }
- // Retrieve the Sigma point that is not part of Tau - parameter for is_gabriel function
- Point_d point_for_gabriel;
- for (auto vertex : complex.simplex_vertex_range(f_simplex)) {
- point_for_gabriel = get_point(vertex);
- if (std::find(pointVector.begin(), pointVector.end(), point_for_gabriel) == pointVector.end()) {
+ for (auto vertex : complex.simplex_vertex_range(f_boundary)) {
+ pointVector.push_back(get_point(vertex));
+ }
+ // Retrieve the Sigma point that is not part of Tau - parameter for is_gabriel function
+ Point_d point_for_gabriel;
+ for (auto vertex : complex.simplex_vertex_range(f_simplex)) {
+ point_for_gabriel = get_point(vertex);
+ if (std::find(pointVector.begin(), pointVector.end(), point_for_gabriel) == pointVector.end()) {
#ifdef DEBUG_TRACES
- // vertex is not found in Tau
- vertexForGabriel = vertex;
+ // vertex is not found in Tau
+ vertexForGabriel = vertex;
#endif // DEBUG_TRACES
- // No need to continue loop
- break;
- }
+ // No need to continue loop
+ break;
}
- // is_gabriel function initialization
- Is_Gabriel is_gabriel = kernel_.side_of_bounded_sphere_d_object();
- bool is_gab = is_gabriel(pointVector.begin(), pointVector.end(), point_for_gabriel)
- != CGAL::ON_BOUNDED_SIDE;
+ }
+ // is_gabriel function initialization
+ Is_Gabriel is_gabriel = kernel_.side_of_bounded_sphere_d_object();
+ bool is_gab = is_gabriel(pointVector.begin(), pointVector.end(), point_for_gabriel)
+ != CGAL::ON_BOUNDED_SIDE;
#ifdef DEBUG_TRACES
- std::cout << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << vertexForGabriel << std::endl;
+ std::cout << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << vertexForGabriel << std::endl;
#endif // DEBUG_TRACES
- // ### If Tau is not Gabriel of Sigma
- if (false == is_gab) {
- // ### filt(Tau) = filt(Sigma)
- Filtration_value alpha_complex_filtration = complex.filtration(f_simplex);
- complex.assign_filtration(f_boundary, alpha_complex_filtration);
+ // ### If Tau is not Gabriel of Sigma
+ if (false == is_gab) {
+ // ### filt(Tau) = filt(Sigma)
+ Filtration_value alpha_complex_filtration = complex.filtration(f_simplex);
+ complex.assign_filtration(f_boundary, alpha_complex_filtration);
#ifdef DEBUG_TRACES
- std::cout << " | filt(Tau) = filt(Sigma) = " << complex.filtration(f_boundary) << std::endl;
+ std::cout << " | filt(Tau) = filt(Sigma) = " << complex.filtration(f_boundary) << std::endl;
#endif // DEBUG_TRACES
- }
}
}
}
diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h
new file mode 100644
index 00000000..32dfcc16
--- /dev/null
+++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h
@@ -0,0 +1,584 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2018 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALPHA_COMPLEX_3D_H_
+#define ALPHA_COMPLEX_3D_H_
+
+#include <boost/version.hpp>
+#include <boost/variant.hpp>
+
+#include <gudhi/Debug_utils.h>
+#include <gudhi/Alpha_complex_options.h>
+
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Delaunay_triangulation_3.h>
+#include <CGAL/Periodic_3_Delaunay_triangulation_traits_3.h>
+#include <CGAL/Periodic_3_Delaunay_triangulation_3.h>
+#include <CGAL/Periodic_3_regular_triangulation_traits_3.h>
+#include <CGAL/Periodic_3_regular_triangulation_3.h>
+#include <CGAL/Regular_triangulation_3.h>
+#include <CGAL/Alpha_shape_3.h>
+#include <CGAL/Alpha_shape_cell_base_3.h>
+#include <CGAL/Alpha_shape_vertex_base_3.h>
+
+#include <CGAL/Object.h>
+#include <CGAL/tuple.h>
+#include <CGAL/iterator.h>
+#include <CGAL/version.h>
+
+#include <boost/container/static_vector.hpp>
+
+#include <iostream>
+#include <vector>
+#include <unordered_map>
+#include <stdexcept>
+#include <cstddef>
+#include <memory> // for std::unique_ptr
+#include <type_traits> // for std::conditional and std::enable_if
+#include <limits> // for numeric_limits<>
+
+#if CGAL_VERSION_NR < 1041101000
+// Make compilation fail - required for external projects - https://gitlab.inria.fr/GUDHI/gudhi-devel/issues/10
+# error Alpha_complex_3d is only available for CGAL >= 4.11
+#endif
+
+namespace Gudhi {
+
+namespace alpha_complex {
+
+#ifdef GUDHI_CAN_USE_CXX11_THREAD_LOCAL
+thread_local
+#endif // GUDHI_CAN_USE_CXX11_THREAD_LOCAL
+ double RELATIVE_PRECISION_OF_TO_DOUBLE = 0.00001;
+
+// Value_from_iterator returns the filtration value from an iterator on alpha shapes values
+//
+// FAST SAFE EXACT
+// CGAL::to_double(*iterator) CGAL::to_double(*iterator) CGAL::to_double(iterator->exact())
+
+template <complexity Complexity>
+struct Value_from_iterator {
+ template <typename Iterator>
+ static double perform(Iterator it) {
+ // Default behaviour
+ return CGAL::to_double(*it);
+ }
+};
+
+template <>
+struct Value_from_iterator<complexity::EXACT> {
+ template <typename Iterator>
+ static double perform(Iterator it) {
+ return CGAL::to_double(it->exact());
+ }
+};
+
+/**
+ * \class Alpha_complex_3d
+ * \brief Alpha complex data structure for 3d specific case.
+ *
+ * \ingroup alpha_complex
+ *
+ * \details
+ * The data structure is constructing a <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html">CGAL 3D Alpha
+ * Shapes</a> from a range of points (can be read from an OFF file, cf. Points_off_reader).
+ * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous.
+ *
+ * \tparam Complexity shall be `Gudhi::alpha_complex::complexity` type. Default value is
+ * `Gudhi::alpha_complex::complexity::SAFE`.
+ *
+ * \tparam Weighted Boolean used to set/unset the weighted version of Alpha_complex_3d. Default value is false.
+ *
+ * \tparam Periodic Boolean used to set/unset the periodic version of Alpha_complex_3d. Default value is false.
+ *
+ * For the weighted version, weights values are explained on CGAL
+ * <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html#title0">Alpha shapes 3d</a> and
+ * <a href="https://doc.cgal.org/latest/Triangulation_3/index.html#Triangulation3secclassRegulartriangulation">Regular
+ * triangulation</a> documentation.
+ *
+ * For the periodic version, refer to the
+ * <a href="https://doc.cgal.org/latest/Periodic_3_triangulation_3/index.html">CGAL’s 3D Periodic Triangulations User
+ * Manual </a> for more details.
+ * The periodicity is defined by an iso-oriented cuboid with diagonal opposite vertices (x_min, y_min, z_min) and
+ * (x_max, y_max, z_max).
+ *
+ * Please refer to \ref alpha_complex for examples.
+ *
+ * \remark When Alpha_complex_3d is constructed with an infinite value of alpha (default value), the complex is a
+ * 3d Delaunay complex.
+ *
+ */
+template <complexity Complexity = complexity::SAFE, bool Weighted = false, bool Periodic = false>
+class Alpha_complex_3d {
+ // Epick = Exact_predicates_inexact_constructions_kernel
+ // Epeck = Exact_predicates_exact_constructions_kernel
+ // Exact_alpha_comparison_tag = exact version of CGAL Alpha_shape_3 and of its objects (Alpha_shape_vertex_base_3 and
+ // Alpha_shape_cell_base_3). Not available if weighted or periodic.
+ // Can be CGAL::Tag_false or CGAL::Tag_true. Default is False.
+ // cf. https://doc.cgal.org/latest/Alpha_shapes_3/classCGAL_1_1Alpha__shape__3.html
+ //
+ // We could use Epick + CGAL::Tag_true for not weighted nor periodic, but during benchmark, we found a bug
+ // https://github.com/CGAL/cgal/issues/3460
+ // This is the reason we only use Epick + CGAL::Tag_false, or Epeck
+ //
+ // FAST SAFE EXACT
+ // Epick + CGAL::Tag_false Epeck Epeck
+ using Predicates = typename std::conditional<(Complexity == complexity::FAST),
+ CGAL::Exact_predicates_inexact_constructions_kernel,
+ CGAL::Exact_predicates_exact_constructions_kernel>::type;
+
+ // The other way to do a conditional type. Here there are 3 possibilities
+ template <typename Predicates, bool Weighted_version, bool Periodic_version>
+ struct Kernel_3 {};
+
+ template <typename Predicates, bool Is_periodic>
+ struct Kernel_3<Predicates, Is_periodic, false> {
+ using Kernel = Predicates;
+ };
+
+ template <typename Predicates>
+ struct Kernel_3<Predicates, false, true> {
+ using Kernel = CGAL::Periodic_3_Delaunay_triangulation_traits_3<Predicates>;
+ };
+ template <typename Predicates>
+ struct Kernel_3<Predicates, true, true> {
+ using Kernel = CGAL::Periodic_3_regular_triangulation_traits_3<Predicates>;
+ };
+
+ using Kernel = typename Kernel_3<Predicates, Weighted, Periodic>::Kernel;
+
+ using TdsVb = typename std::conditional<Periodic, CGAL::Periodic_3_triangulation_ds_vertex_base_3<>,
+ CGAL::Triangulation_ds_vertex_base_3<>>::type;
+
+ using Tvb = typename std::conditional<Weighted, CGAL::Regular_triangulation_vertex_base_3<Kernel, TdsVb>,
+ CGAL::Triangulation_vertex_base_3<Kernel, TdsVb>>::type;
+
+ using Vb = CGAL::Alpha_shape_vertex_base_3<Kernel, Tvb>;
+
+ using TdsCb = typename std::conditional<Periodic, CGAL::Periodic_3_triangulation_ds_cell_base_3<>,
+ CGAL::Triangulation_ds_cell_base_3<>>::type;
+
+ using Tcb = typename std::conditional<Weighted, CGAL::Regular_triangulation_cell_base_3<Kernel, TdsCb>,
+ CGAL::Triangulation_cell_base_3<Kernel, TdsCb>>::type;
+
+ using Cb = CGAL::Alpha_shape_cell_base_3<Kernel, Tcb>;
+ using Tds = CGAL::Triangulation_data_structure_3<Vb, Cb>;
+
+ // The other way to do a conditional type. Here there 4 possibilities, cannot use std::conditional
+ template <typename Kernel, typename Tds, bool Weighted_version, bool Periodic_version>
+ struct Triangulation_3 {};
+
+ template <typename Kernel, typename Tds>
+ struct Triangulation_3<Kernel, Tds, false, false> {
+ using Dt = CGAL::Delaunay_triangulation_3<Kernel, Tds>;
+ using Weighted_point_3 = void;
+ };
+ template <typename Kernel, typename Tds>
+ struct Triangulation_3<Kernel, Tds, true, false> {
+ using Dt = CGAL::Regular_triangulation_3<Kernel, Tds>;
+ using Weighted_point_3 = typename Dt::Weighted_point;
+ };
+ template <typename Kernel, typename Tds>
+ struct Triangulation_3<Kernel, Tds, false, true> {
+ using Dt = CGAL::Periodic_3_Delaunay_triangulation_3<Kernel, Tds>;
+ using Weighted_point_3 = void;
+ };
+ template <typename Kernel, typename Tds>
+ struct Triangulation_3<Kernel, Tds, true, true> {
+ using Dt = CGAL::Periodic_3_regular_triangulation_3<Kernel, Tds>;
+ using Weighted_point_3 = typename Dt::Weighted_point;
+ };
+
+ /** \brief Is either Delaunay_triangulation_3 (Weighted = false and Periodic = false),
+ * Regular_triangulation_3 (Weighted = true and Periodic = false),
+ * Periodic_3_Delaunay_triangulation_3 (Weighted = false and Periodic = true)
+ * or Periodic_3_regular_triangulation_3 (Weighted = true and Periodic = true).
+ *
+ * This type is required by `Gudhi::alpha_complex::Alpha_complex_3d::Alpha_shape_3`.
+ * */
+ using Dt = typename Triangulation_3<Kernel, Tds, Weighted, Periodic>::Dt;
+
+ public:
+ /** \brief The <a href="https://doc.cgal.org/latest/Alpha_shapes_3/classCGAL_1_1Alpha__shape__3.html">CGAL 3D Alpha
+ * Shapes</a> type.
+ *
+ * The `Gudhi::alpha_complex::Alpha_complex_3d` is a wrapper on top of this class to ease the standard, weighted
+ * and/or periodic build of the Alpha complex 3d.*/
+ using Alpha_shape_3 = CGAL::Alpha_shape_3<Dt>;
+
+ /** \brief The alpha values type.
+ * Must be compatible with double. */
+ using FT = typename Alpha_shape_3::FT;
+
+ /** \brief Gives public access to the Point_3 type. Here is a Point_3 constructor example:
+\code{.cpp}
+using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>;
+
+// x0 = 1., y0 = -1.1, z0 = -1..
+Alpha_complex_3d::Point_3 p0(1., -1.1, -1.);
+\endcode
+ * */
+ using Point_3 = typename Kernel::Point_3;
+
+ /** \brief Gives public access to the Weighted_point_3 type. A Weighted point can be constructed as follows:
+\code{.cpp}
+using Weighted_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>;
+
+// x0 = 1., y0 = -1.1, z0 = -1., weight = 4.
+Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point_3(1., -1.1, -1.), 4.);
+\endcode
+ *
+ * Note: This type is defined to void if Alpha complex is not weighted.
+ *
+ * */
+ using Weighted_point_3 = typename Triangulation_3<Kernel, Tds, Weighted, Periodic>::Weighted_point_3;
+
+ private:
+ using Dispatch =
+ CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<CGAL::Object, FT>,
+ CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<CGAL::Object>>,
+ std::back_insert_iterator<std::vector<FT>>>>;
+
+ using Cell_handle = typename Alpha_shape_3::Cell_handle;
+ using Facet = typename Alpha_shape_3::Facet;
+ using Edge = typename Alpha_shape_3::Edge;
+ using Alpha_vertex_handle = typename Alpha_shape_3::Vertex_handle;
+ using Vertex_list = boost::container::static_vector<Alpha_vertex_handle, 4>;
+
+ public:
+ /** \brief Alpha_complex constructor from a list of points.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or
+ * `Alpha_complex_3d::Weighted_point_3`.
+ *
+ * @pre Available if Alpha_complex_3d is not Periodic.
+ *
+ * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a
+ * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`.
+ */
+ template <typename InputPointRange>
+ Alpha_complex_3d(const InputPointRange& points) {
+ static_assert(!Periodic, "This constructor is not available for periodic versions of Alpha_complex_3d");
+
+ alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(
+ new Alpha_shape_3(std::begin(points), std::end(points), 0, Alpha_shape_3::GENERAL));
+ }
+
+ /** \brief Alpha_complex constructor from a list of points and associated weights.
+ *
+ * @exception std::invalid_argument In debug mode, if points and weights do not have the same size.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`.
+ * @param[in] weights Range of weights on points. Weights shall be in double.
+ *
+ * @pre Available if Alpha_complex_3d is Weighted and not Periodic.
+ *
+ * The type InputPointRange must be a range for which std::begin and
+ * std::end return input iterators on a `Alpha_complex_3d::Point_3`.
+ * The type WeightRange must be a range for which std::begin and
+ * std::end return an input iterator on a double.
+ */
+ template <typename InputPointRange, typename WeightRange>
+ Alpha_complex_3d(const InputPointRange& points, WeightRange weights) {
+ static_assert(Weighted, "This constructor is not available for non-weighted versions of Alpha_complex_3d");
+ static_assert(!Periodic, "This constructor is not available for periodic versions of Alpha_complex_3d");
+ GUDHI_CHECK((weights.size() == points.size()),
+ std::invalid_argument("Points number in range different from weights range number"));
+
+ std::vector<Weighted_point_3> weighted_points_3;
+
+ std::size_t index = 0;
+ weighted_points_3.reserve(points.size());
+ while ((index < weights.size()) && (index < points.size())) {
+ weighted_points_3.push_back(Weighted_point_3(points[index], weights[index]));
+ index++;
+ }
+
+ alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(
+ new Alpha_shape_3(std::begin(weighted_points_3), std::end(weighted_points_3), 0, Alpha_shape_3::GENERAL));
+ }
+
+ /** \brief Alpha_complex constructor from a list of points and an iso-cuboid coordinates.
+ *
+ * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or
+ * `Alpha_complex_3d::Weighted_point_3`.
+ * @param[in] x_min Iso-oriented cuboid x_min.
+ * @param[in] y_min Iso-oriented cuboid y_min.
+ * @param[in] z_min Iso-oriented cuboid z_min.
+ * @param[in] x_max Iso-oriented cuboid x_max.
+ * @param[in] y_max Iso-oriented cuboid y_max.
+ * @param[in] z_max Iso-oriented cuboid z_max.
+ *
+ * @pre Available if Alpha_complex_3d is Periodic.
+ *
+ * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a
+ * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`.
+ *
+ * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length
+ * squared.
+ */
+ template <typename InputPointRange>
+ Alpha_complex_3d(const InputPointRange& points, FT x_min, FT y_min,
+ FT z_min, FT x_max, FT y_max, FT z_max) {
+ static_assert(Periodic, "This constructor is not available for non-periodic versions of Alpha_complex_3d");
+ // Checking if the cuboid is the same in x,y and z direction. If not, CGAL will not process it.
+ GUDHI_CHECK(
+ (x_max - x_min == y_max - y_min) && (x_max - x_min == z_max - z_min) && (z_max - z_min == y_max - y_min),
+ std::invalid_argument("The size of the cuboid in every directions is not the same."));
+
+ // Define the periodic cube
+ Dt pdt(typename Kernel::Iso_cuboid_3(x_min, y_min, z_min, x_max, y_max, z_max));
+ // Heuristic for inserting large point sets (if pts is reasonably large)
+ pdt.insert(std::begin(points), std::end(points), true);
+ // As pdt won't be modified anymore switch to 1-sheeted cover if possible
+ if (!pdt.is_triangulation_in_1_sheet()) {
+ throw std::invalid_argument("Unable to construct a triangulation within a single periodic domain.");
+ }
+ pdt.convert_to_1_sheeted_covering();
+
+ // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode
+ // Maybe need to set it to GENERAL mode
+ alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(new Alpha_shape_3(pdt, 0, Alpha_shape_3::GENERAL));
+ }
+
+ /** \brief Alpha_complex constructor from a list of points, associated weights and an iso-cuboid coordinates.
+ *
+ * @exception std::invalid_argument In debug mode, if points and weights do not have the same size.
+ * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same.
+ * @exception std::invalid_argument In debug mode, if a weight is negative, zero, or greater than 1/64*cuboid length
+ * squared.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`.
+ * @param[in] weights Range of weights on points. Weights shall be in double.
+ * @param[in] x_min Iso-oriented cuboid x_min.
+ * @param[in] y_min Iso-oriented cuboid y_min.
+ * @param[in] z_min Iso-oriented cuboid z_min.
+ * @param[in] x_max Iso-oriented cuboid x_max.
+ * @param[in] y_max Iso-oriented cuboid y_max.
+ * @param[in] z_max Iso-oriented cuboid z_max.
+ *
+ * @pre Available if Alpha_complex_3d is Weighted and Periodic.
+ *
+ * The type InputPointRange must be a range for which std::begin and
+ * std::end return input iterators on a `Alpha_complex_3d::Point_3`.
+ * The type WeightRange must be a range for which std::begin and
+ * std::end return an input iterator on a double.
+ * The type of x_min, y_min, z_min, x_max, y_max and z_max must be a double.
+ */
+ template <typename InputPointRange, typename WeightRange>
+ Alpha_complex_3d(const InputPointRange& points, WeightRange weights, FT x_min, FT y_min,
+ FT z_min, FT x_max, FT y_max, FT z_max) {
+ static_assert(Weighted, "This constructor is not available for non-weighted versions of Alpha_complex_3d");
+ static_assert(Periodic, "This constructor is not available for non-periodic versions of Alpha_complex_3d");
+ GUDHI_CHECK((weights.size() == points.size()),
+ std::invalid_argument("Points number in range different from weights range number"));
+ // Checking if the cuboid is the same in x,y and z direction. If not, CGAL will not process it.
+ GUDHI_CHECK(
+ (x_max - x_min == y_max - y_min) && (x_max - x_min == z_max - z_min) && (z_max - z_min == y_max - y_min),
+ std::invalid_argument("The size of the cuboid in every directions is not the same."));
+
+ std::vector<Weighted_point_3> weighted_points_3;
+
+ std::size_t index = 0;
+ weighted_points_3.reserve(points.size());
+
+#ifdef GUDHI_DEBUG
+ // Defined in GUDHI_DEBUG to avoid unused variable warning for GUDHI_CHECK
+ FT maximal_possible_weight = 0.015625 * (x_max - x_min) * (x_max - x_min);
+#endif
+
+ while ((index < weights.size()) && (index < points.size())) {
+ GUDHI_CHECK((weights[index] < maximal_possible_weight) && (weights[index] >= 0),
+ std::invalid_argument("Invalid weight at index " + std::to_string(index + 1) +
+ ". Must be positive and less than maximal possible weight = 1/64*cuboid length "
+ "squared, which is not an acceptable input."));
+ weighted_points_3.push_back(Weighted_point_3(points[index], weights[index]));
+ index++;
+ }
+
+ // Define the periodic cube
+ Dt pdt(typename Kernel::Iso_cuboid_3(x_min, y_min, z_min, x_max, y_max, z_max));
+ // Heuristic for inserting large point sets (if pts is reasonably large)
+ pdt.insert(std::begin(weighted_points_3), std::end(weighted_points_3), true);
+ // As pdt won't be modified anymore switch to 1-sheeted cover if possible
+ if (!pdt.is_triangulation_in_1_sheet()) {
+ throw std::invalid_argument("Unable to construct a triangulation within a single periodic domain.");
+ }
+ pdt.convert_to_1_sheeted_covering();
+
+ // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode
+ // Maybe need to set it to GENERAL mode
+ alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(new Alpha_shape_3(pdt, 0, Alpha_shape_3::GENERAL));
+ }
+
+ /** \brief Inserts all Delaunay triangulation into the simplicial complex.
+ * It also computes the filtration values accordingly to the \ref createcomplexalgorithm
+ *
+ * \tparam SimplicialComplexForAlpha3d must meet `SimplicialComplexForAlpha3d` concept.
+ *
+ * @param[in] complex SimplicialComplexForAlpha3d to be created.
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$, and there is very
+ * little point using anything else since it does not save time.
+ *
+ * @return true if creation succeeds, false otherwise.
+ *
+ * @pre The simplicial complex must be empty (no vertices).
+ *
+ */
+ template <typename SimplicialComplexForAlpha3d,
+ typename Filtration_value = typename SimplicialComplexForAlpha3d::Filtration_value>
+ bool create_complex(SimplicialComplexForAlpha3d& complex,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity()) {
+ if (complex.num_vertices() > 0) {
+ std::cerr << "Alpha_complex_3d create_complex - complex is not empty\n";
+ return false; // ----- >>
+ }
+
+ // using Filtration_value = typename SimplicialComplexForAlpha3d::Filtration_value;
+ using Complex_vertex_handle = typename SimplicialComplexForAlpha3d::Vertex_handle;
+ using Alpha_shape_simplex_tree_map = std::unordered_map<Alpha_vertex_handle, Complex_vertex_handle>;
+ using Simplex_tree_vector_vertex = std::vector<Complex_vertex_handle>;
+
+#ifdef DEBUG_TRACES
+ std::size_t count_vertices = 0;
+ std::size_t count_edges = 0;
+ std::size_t count_facets = 0;
+ std::size_t count_cells = 0;
+#endif // DEBUG_TRACES
+ std::vector<CGAL::Object> objects;
+ std::vector<FT> alpha_values;
+
+ Dispatch dispatcher = CGAL::dispatch_output<CGAL::Object, FT>(std::back_inserter(objects),
+ std::back_inserter(alpha_values));
+
+ alpha_shape_3_ptr_->filtration_with_alpha_values(dispatcher);
+#ifdef DEBUG_TRACES
+ std::cout << "filtration_with_alpha_values returns : " << objects.size() << " objects" << std::endl;
+#endif // DEBUG_TRACES
+
+ Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
+ using Alpha_value_iterator = typename std::vector<FT>::const_iterator;
+ Alpha_value_iterator alpha_value_iterator = alpha_values.begin();
+ for (auto object_iterator : objects) {
+ Vertex_list vertex_list;
+
+ // Retrieve Alpha shape vertex list from object
+ if (const Cell_handle* cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
+ for (auto i = 0; i < 4; i++) {
+#ifdef DEBUG_TRACES
+ std::cout << "from cell[" << i << "]=" << (*cell)->vertex(i)->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_list.push_back((*cell)->vertex(i));
+ }
+#ifdef DEBUG_TRACES
+ count_cells++;
+#endif // DEBUG_TRACES
+ } else if (const Facet* facet = CGAL::object_cast<Facet>(&object_iterator)) {
+ for (auto i = 0; i < 4; i++) {
+ if ((*facet).second != i) {
+#ifdef DEBUG_TRACES
+ std::cout << "from facet=[" << i << "]" << (*facet).first->vertex(i)->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_list.push_back((*facet).first->vertex(i));
+ }
+ }
+#ifdef DEBUG_TRACES
+ count_facets++;
+#endif // DEBUG_TRACES
+ } else if (const Edge* edge = CGAL::object_cast<Edge>(&object_iterator)) {
+ for (auto i : {(*edge).second, (*edge).third}) {
+#ifdef DEBUG_TRACES
+ std::cout << "from edge[" << i << "]=" << (*edge).first->vertex(i)->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_list.push_back((*edge).first->vertex(i));
+ }
+#ifdef DEBUG_TRACES
+ count_edges++;
+#endif // DEBUG_TRACES
+ } else if (const Alpha_vertex_handle* vertex = CGAL::object_cast<Alpha_vertex_handle>(&object_iterator)) {
+#ifdef DEBUG_TRACES
+ count_vertices++;
+ std::cout << "from vertex=" << (*vertex)->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_list.push_back((*vertex));
+ }
+ // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
+ Simplex_tree_vector_vertex the_simplex;
+ for (auto the_alpha_shape_vertex : vertex_list) {
+ auto the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
+ if (the_map_iterator == map_cgal_simplex_tree.end()) {
+ // alpha shape not found
+ Complex_vertex_handle vertex = map_cgal_simplex_tree.size();
+#ifdef DEBUG_TRACES
+ std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
+#endif // DEBUG_TRACES
+ the_simplex.push_back(vertex);
+ map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
+ } else {
+ // alpha shape found
+ Complex_vertex_handle vertex = the_map_iterator->second;
+#ifdef DEBUG_TRACES
+ std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
+#endif // DEBUG_TRACES
+ the_simplex.push_back(vertex);
+ }
+ }
+ // Construction of the simplex_tree
+ Filtration_value filtr = Value_from_iterator<Complexity>::perform(alpha_value_iterator);
+
+#ifdef DEBUG_TRACES
+ std::cout << "filtration = " << filtr << std::endl;
+#endif // DEBUG_TRACES
+ complex.insert_simplex(the_simplex, static_cast<Filtration_value>(filtr));
+ GUDHI_CHECK(alpha_value_iterator != alpha_values.end(), "CGAL provided more simplices than values");
+ ++alpha_value_iterator;
+ }
+
+#ifdef DEBUG_TRACES
+ std::cout << "vertices \t" << count_vertices << std::endl;
+ std::cout << "edges \t\t" << count_edges << std::endl;
+ std::cout << "facets \t\t" << count_facets << std::endl;
+ std::cout << "cells \t\t" << count_cells << std::endl;
+#endif // DEBUG_TRACES
+ // --------------------------------------------------------------------------------------------
+ // As Alpha value is an approximation, we have to make filtration non decreasing while increasing the dimension
+ complex.make_filtration_non_decreasing();
+ // Remove all simplices that have a filtration value greater than max_alpha_square
+ complex.prune_above_filtration(max_alpha_square);
+ // --------------------------------------------------------------------------------------------
+ return true;
+ }
+
+ private:
+ // use of a unique_ptr on cgal Alpha_shape_3, as copy and default constructor is not available - no need to be freed
+ std::unique_ptr<Alpha_shape_3> alpha_shape_3_ptr_;
+};
+
+} // namespace alpha_complex
+
+} // namespace Gudhi
+
+#endif // ALPHA_COMPLEX_3D_H_
diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_options.h b/src/Alpha_complex/include/gudhi/Alpha_complex_options.h
new file mode 100644
index 00000000..7a555fa1
--- /dev/null
+++ b/src/Alpha_complex/include/gudhi/Alpha_complex_options.h
@@ -0,0 +1,45 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2018 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALPHA_COMPLEX_OPTIONS_H_
+#define ALPHA_COMPLEX_OPTIONS_H_
+
+namespace Gudhi {
+
+namespace alpha_complex {
+
+/**
+ * \brief Alpha complex complexity template parameter possible values.
+ *
+ * \ingroup alpha_complex
+ */
+enum class complexity : char {
+ FAST = 'f', ///< Fast version.
+ SAFE = 's', ///< Safe version.
+ EXACT = 'e', ///< Exact version.
+};
+
+} // namespace alpha_complex
+
+} // namespace Gudhi
+
+#endif // ALPHA_COMPLEX_OPTIONS_H_
diff --git a/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp
new file mode 100644
index 00000000..ec905d5b
--- /dev/null
+++ b/src/Alpha_complex/test/Alpha_complex_3d_unit_test.cpp
@@ -0,0 +1,164 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "alpha_complex_3d"
+#include <boost/test/unit_test.hpp>
+
+#include <cmath> // float comparison
+#include <limits>
+#include <string>
+#include <vector>
+#include <random>
+#include <cstddef> // for std::size_t
+
+#include <gudhi/Alpha_complex_3d.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Unitary_tests_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_3D_off_io.h>
+
+#include <CGAL/Random.h>
+#include <CGAL/point_generators_3.h>
+
+using Fast_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, false>;
+using Safe_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>;
+using Exact_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, false>;
+
+template <typename Point>
+std::vector<Point> get_points() {
+ std::vector<Point> points;
+ points.push_back(Point(0.0, 0.0, 0.0));
+ points.push_back(Point(0.0, 0.0, 0.2));
+ points.push_back(Point(0.2, 0.0, 0.2));
+ points.push_back(Point(0.6, 0.6, 0.0));
+ points.push_back(Point(0.8, 0.8, 0.2));
+ points.push_back(Point(0.2, 0.8, 0.6));
+
+ return points;
+}
+
+
+BOOST_AUTO_TEST_CASE(Alpha_complex_3d_from_points) {
+ // -----------------
+ // Fast version
+ // -----------------
+ std::cout << "Fast alpha complex 3d" << std::endl;
+
+ Fast_alpha_complex_3d alpha_complex(get_points<Fast_alpha_complex_3d::Point_3>());
+
+ Gudhi::Simplex_tree<> stree;
+ alpha_complex.create_complex(stree);
+
+ // -----------------
+ // Exact version
+ // -----------------
+ std::cout << "Exact alpha complex 3d" << std::endl;
+
+ Exact_alpha_complex_3d exact_alpha_complex(get_points<Exact_alpha_complex_3d::Point_3>());
+
+ Gudhi::Simplex_tree<> exact_stree;
+ exact_alpha_complex.create_complex(exact_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ std::cout << "Exact Alpha complex 3d is of dimension " << exact_stree.dimension() << " - Fast is "
+ << stree.dimension() << std::endl;
+ BOOST_CHECK(exact_stree.dimension() == stree.dimension());
+ std::cout << "Exact Alpha complex 3d num_simplices " << exact_stree.num_simplices() << " - Fast is "
+ << stree.num_simplices() << std::endl;
+ BOOST_CHECK(exact_stree.num_simplices() == stree.num_simplices());
+ std::cout << "Exact Alpha complex 3d num_vertices " << exact_stree.num_vertices() << " - Fast is "
+ << stree.num_vertices() << std::endl;
+ BOOST_CHECK(exact_stree.num_vertices() == stree.num_vertices());
+
+ auto sh = stree.filtration_simplex_range().begin();
+ while (sh != stree.filtration_simplex_range().end()) {
+ std::vector<int> simplex;
+ std::vector<int> exact_simplex;
+ std::cout << "Fast ( ";
+ for (auto vertex : stree.simplex_vertex_range(*sh)) {
+ simplex.push_back(vertex);
+ std::cout << vertex << " ";
+ }
+ std::cout << ") -> [" << stree.filtration(*sh) << "] ";
+
+ // Find it in the exact structure
+ auto sh_exact = exact_stree.find(simplex);
+ BOOST_CHECK(sh_exact != exact_stree.null_simplex());
+
+ std::cout << " versus [" << exact_stree.filtration(sh_exact) << "] " << std::endl;
+ // Exact and non-exact version is not exactly the same due to float comparison
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(exact_stree.filtration(sh_exact), stree.filtration(*sh));
+
+ ++sh;
+ }
+ // -----------------
+ // Safe version
+ // -----------------
+ std::cout << "Safe alpha complex 3d" << std::endl;
+
+ Safe_alpha_complex_3d safe_alpha_complex(get_points<Safe_alpha_complex_3d::Point_3>());
+
+ Gudhi::Simplex_tree<> safe_stree;
+ safe_alpha_complex.create_complex(safe_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ std::cout << "Safe Alpha complex 3d is of dimension " << safe_stree.dimension() << " - Fast is "
+ << stree.dimension() << std::endl;
+ BOOST_CHECK(safe_stree.dimension() == stree.dimension());
+ std::cout << "Safe Alpha complex 3d num_simplices " << safe_stree.num_simplices() << " - Fast is "
+ << stree.num_simplices() << std::endl;
+ BOOST_CHECK(safe_stree.num_simplices() == stree.num_simplices());
+ std::cout << "Safe Alpha complex 3d num_vertices " << safe_stree.num_vertices() << " - Fast is "
+ << stree.num_vertices() << std::endl;
+ BOOST_CHECK(safe_stree.num_vertices() == stree.num_vertices());
+
+ auto safe_sh = stree.filtration_simplex_range().begin();
+ while (safe_sh != stree.filtration_simplex_range().end()) {
+ std::vector<int> simplex;
+ std::vector<int> exact_simplex;
+ std::cout << "Fast ( ";
+ for (auto vertex : stree.simplex_vertex_range(*safe_sh)) {
+ simplex.push_back(vertex);
+ std::cout << vertex << " ";
+ }
+ std::cout << ") -> [" << stree.filtration(*safe_sh) << "] ";
+
+ // Find it in the exact structure
+ auto sh_exact = safe_stree.find(simplex);
+ BOOST_CHECK(sh_exact != safe_stree.null_simplex());
+
+ std::cout << " versus [" << safe_stree.filtration(sh_exact) << "] " << std::endl;
+ // Exact and non-exact version is not exactly the same due to float comparison
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(safe_stree.filtration(sh_exact), stree.filtration(*safe_sh), 1e-15);
+
+ ++safe_sh;
+ }
+}
diff --git a/src/Alpha_complex/test/CMakeLists.txt b/src/Alpha_complex/test/CMakeLists.txt
index 9255d3db..7c6bf9aa 100644
--- a/src/Alpha_complex/test/CMakeLists.txt
+++ b/src/Alpha_complex/test/CMakeLists.txt
@@ -1,7 +1,10 @@
project(Alpha_complex_tests)
+include(GUDHI_test_coverage)
if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
- include(GUDHI_test_coverage)
+
+ # Do not forget to copy test files in current binary dir
+ file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
add_executable ( Alpha_complex_test_unit Alpha_complex_unit_test.cpp )
target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
@@ -9,10 +12,29 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
target_link_libraries(Alpha_complex_test_unit ${TBB_LIBRARIES})
endif()
- # Do not forget to copy test files in current binary dir
- file(COPY "${CMAKE_SOURCE_DIR}/data/points/alphacomplexdoc.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
-
gudhi_add_coverage_test(Alpha_complex_test_unit)
endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
+if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
+ add_executable ( Alpha_complex_3d_test_unit Alpha_complex_3d_unit_test.cpp )
+ target_link_libraries(Alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+ add_executable ( Weighted_alpha_complex_3d_test_unit Weighted_alpha_complex_3d_unit_test.cpp )
+ target_link_libraries(Weighted_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+ add_executable ( Periodic_alpha_complex_3d_test_unit Periodic_alpha_complex_3d_unit_test.cpp )
+ target_link_libraries(Periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+ add_executable ( Weighted_periodic_alpha_complex_3d_test_unit Weighted_periodic_alpha_complex_3d_unit_test.cpp )
+ target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${CGAL_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+ if (TBB_FOUND)
+ target_link_libraries(Alpha_complex_3d_test_unit ${TBB_LIBRARIES})
+ target_link_libraries(Weighted_alpha_complex_3d_test_unit ${TBB_LIBRARIES})
+ target_link_libraries(Periodic_alpha_complex_3d_test_unit ${TBB_LIBRARIES})
+ target_link_libraries(Weighted_periodic_alpha_complex_3d_test_unit ${TBB_LIBRARIES})
+ endif()
+
+ gudhi_add_coverage_test(Alpha_complex_3d_test_unit)
+ gudhi_add_coverage_test(Weighted_alpha_complex_3d_test_unit)
+ gudhi_add_coverage_test(Periodic_alpha_complex_3d_test_unit)
+ gudhi_add_coverage_test(Weighted_periodic_alpha_complex_3d_test_unit)
+
+endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
diff --git a/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp
new file mode 100644
index 00000000..ed4cbff0
--- /dev/null
+++ b/src/Alpha_complex/test/Periodic_alpha_complex_3d_unit_test.cpp
@@ -0,0 +1,190 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "alpha_complex_3d"
+#include <boost/test/unit_test.hpp>
+#include <boost/mpl/list.hpp>
+
+#include <cmath> // float comparison
+#include <limits>
+#include <string>
+#include <vector>
+#include <random>
+#include <cstddef> // for std::size_t
+
+#include <gudhi/Alpha_complex_3d.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Unitary_tests_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_3D_off_io.h>
+
+#include <CGAL/Random.h>
+#include <CGAL/point_generators_3.h>
+
+using Fast_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, true>;
+using Safe_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, true>;
+using Exact_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, true>;
+
+#ifdef GUDHI_DEBUG
+typedef boost::mpl::list<Fast_periodic_alpha_complex_3d, Safe_periodic_alpha_complex_3d,
+ Exact_periodic_alpha_complex_3d>
+ periodic_variants_type_list;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_periodic_throw, Periodic_alpha_complex_3d, periodic_variants_type_list) {
+ std::cout << "Periodic alpha complex 3d exception throw" << std::endl;
+ using Point_3 = typename Periodic_alpha_complex_3d::Point_3;
+ std::vector<Point_3> p_points;
+
+ // Not important, this is not what we want to check
+ p_points.push_back(Point_3(0.0, 0.0, 0.0));
+
+ std::cout << "Check exception throw in debug mode" << std::endl;
+ // Check it throws an exception when the cuboid is not iso
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 0.9, 1., 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 1., 0.9, 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 1., 1., 0.9),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 1.1, 1., 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 1., 1.1, 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(Periodic_alpha_complex_3d periodic_alpha_complex(p_points, 0., 0., 0., 1., 1., 1.1),
+ std::invalid_argument);
+}
+#endif
+
+BOOST_AUTO_TEST_CASE(Alpha_complex_periodic) {
+ // ---------------------
+ // Fast periodic version
+ // ---------------------
+ std::cout << "Fast periodic alpha complex 3d" << std::endl;
+
+ using Creator = CGAL::Creator_uniform_3<double, Fast_periodic_alpha_complex_3d::Point_3>;
+ CGAL::Random random(7);
+ CGAL::Random_points_in_cube_3<Fast_periodic_alpha_complex_3d::Point_3, Creator> in_cube(1, random);
+ std::vector<Fast_periodic_alpha_complex_3d::Point_3> p_points;
+
+ for (int i = 0; i < 50; i++) {
+ Fast_periodic_alpha_complex_3d::Point_3 p = *in_cube++;
+ p_points.push_back(p);
+ }
+
+ Fast_periodic_alpha_complex_3d periodic_alpha_complex(p_points, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> stree;
+ periodic_alpha_complex.create_complex(stree);
+
+ // ----------------------
+ // Exact periodic version
+ // ----------------------
+ std::cout << "Exact periodic alpha complex 3d" << std::endl;
+
+ std::vector<Exact_periodic_alpha_complex_3d::Point_3> e_p_points;
+
+ for (auto p : p_points) {
+ e_p_points.push_back(Exact_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2]));
+ }
+
+ Exact_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> exact_stree;
+ exact_alpha_complex.create_complex(exact_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ std::cout << "Exact periodic alpha complex 3d is of dimension " << exact_stree.dimension() << " - Non exact is "
+ << stree.dimension() << std::endl;
+ BOOST_CHECK(exact_stree.dimension() == stree.dimension());
+ std::cout << "Exact periodic alpha complex 3d num_simplices " << exact_stree.num_simplices() << " - Non exact is "
+ << stree.num_simplices() << std::endl;
+ BOOST_CHECK(exact_stree.num_simplices() == stree.num_simplices());
+ std::cout << "Exact periodic alpha complex 3d num_vertices " << exact_stree.num_vertices() << " - Non exact is "
+ << stree.num_vertices() << std::endl;
+ BOOST_CHECK(exact_stree.num_vertices() == stree.num_vertices());
+
+ // We cannot compare as objects from dispatcher on the alpha shape is not deterministic.
+ // cf. https://github.com/CGAL/cgal/issues/3346
+ auto sh = stree.filtration_simplex_range().begin();
+ auto sh_exact = exact_stree.filtration_simplex_range().begin();
+
+ while (sh != stree.filtration_simplex_range().end() || sh_exact != exact_stree.filtration_simplex_range().end()) {
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree.filtration(*sh), exact_stree.filtration(*sh_exact), 1e-14);
+
+ std::vector<int> vh(stree.simplex_vertex_range(*sh).begin(), stree.simplex_vertex_range(*sh).end());
+ std::vector<int> exact_vh(exact_stree.simplex_vertex_range(*sh_exact).begin(),
+ exact_stree.simplex_vertex_range(*sh_exact).end());
+
+ BOOST_CHECK(vh.size() == exact_vh.size());
+ ++sh;
+ ++sh_exact;
+ }
+
+ BOOST_CHECK(sh == stree.filtration_simplex_range().end());
+ BOOST_CHECK(sh_exact == exact_stree.filtration_simplex_range().end());
+
+ // ----------------------
+ // Safe periodic version
+ // ----------------------
+ std::cout << "Safe periodic alpha complex 3d" << std::endl;
+
+ std::vector<Safe_periodic_alpha_complex_3d::Point_3> s_p_points;
+
+ for (auto p : p_points) {
+ s_p_points.push_back(Safe_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2]));
+ }
+
+ Safe_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> safe_stree;
+ safe_alpha_complex.create_complex(safe_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ // We cannot compare as objects from dispatcher on the alpha shape is not deterministic.
+ // cf. https://github.com/CGAL/cgal/issues/3346
+ sh = stree.filtration_simplex_range().begin();
+ auto sh_safe = safe_stree.filtration_simplex_range().begin();
+
+ while (sh != stree.filtration_simplex_range().end() || sh_safe != safe_stree.filtration_simplex_range().end()) {
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree.filtration(*sh), safe_stree.filtration(*sh_safe), 1e-14);
+
+ std::vector<int> vh(stree.simplex_vertex_range(*sh).begin(), stree.simplex_vertex_range(*sh).end());
+ std::vector<int> safe_vh(safe_stree.simplex_vertex_range(*sh_safe).begin(),
+ safe_stree.simplex_vertex_range(*sh_safe).end());
+
+ BOOST_CHECK(vh.size() == safe_vh.size());
+ ++sh;
+ ++sh_safe;
+ }
+
+ BOOST_CHECK(sh == stree.filtration_simplex_range().end());
+ BOOST_CHECK(sh_safe == safe_stree.filtration_simplex_range().end());
+}
diff --git a/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp
new file mode 100644
index 00000000..c16b3718
--- /dev/null
+++ b/src/Alpha_complex/test/Weighted_alpha_complex_3d_unit_test.cpp
@@ -0,0 +1,147 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "alpha_complex_3d"
+#include <boost/test/unit_test.hpp>
+#include <boost/mpl/list.hpp>
+
+#include <cmath> // float comparison
+#include <limits>
+#include <string>
+#include <vector>
+#include <random>
+#include <cstddef> // for std::size_t
+
+#include <gudhi/Alpha_complex_3d.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Unitary_tests_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_3D_off_io.h>
+
+#include <CGAL/Random.h>
+#include <CGAL/point_generators_3.h>
+
+using Fast_weighted_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, false>;
+using Safe_weighted_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>;
+using Exact_weighted_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, false>;
+
+typedef boost::mpl::list<Fast_weighted_alpha_complex_3d, Safe_weighted_alpha_complex_3d,
+ Exact_weighted_alpha_complex_3d>
+ weighted_variants_type_list;
+
+#ifdef GUDHI_DEBUG
+BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_throw, Weighted_alpha_complex_3d, weighted_variants_type_list) {
+ using Point_3 = typename Weighted_alpha_complex_3d::Point_3;
+ std::vector<Point_3> w_points;
+ w_points.push_back(Point_3(0.0, 0.0, 0.0));
+ w_points.push_back(Point_3(0.0, 0.0, 0.2));
+ w_points.push_back(Point_3(0.2, 0.0, 0.2));
+ // w_points.push_back(Point_3(0.6, 0.6, 0.0));
+ // w_points.push_back(Point_3(0.8, 0.8, 0.2));
+ // w_points.push_back(Point_3(0.2, 0.8, 0.6));
+
+ // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode
+ std::vector<double> weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001};
+
+ std::cout << "Check exception throw in debug mode" << std::endl;
+ BOOST_CHECK_THROW(Weighted_alpha_complex_3d wac(w_points, weights), std::invalid_argument);
+}
+#endif
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, weighted_variants_type_list) {
+ std::cout << "Weighted alpha complex 3d from points and weights" << std::endl;
+ using Point_3 = typename Weighted_alpha_complex_3d::Point_3;
+ std::vector<Point_3> w_points;
+ w_points.push_back(Point_3(0.0, 0.0, 0.0));
+ w_points.push_back(Point_3(0.0, 0.0, 0.2));
+ w_points.push_back(Point_3(0.2, 0.0, 0.2));
+ w_points.push_back(Point_3(0.6, 0.6, 0.0));
+ w_points.push_back(Point_3(0.8, 0.8, 0.2));
+ w_points.push_back(Point_3(0.2, 0.8, 0.6));
+
+ // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode
+ std::vector<double> weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001};
+
+ Weighted_alpha_complex_3d alpha_complex_p_a_w(w_points, weights);
+ Gudhi::Simplex_tree<> stree;
+ alpha_complex_p_a_w.create_complex(stree);
+
+ std::cout << "Weighted alpha complex 3d from weighted points" << std::endl;
+ using Weighted_point_3 = typename Weighted_alpha_complex_3d::Weighted_point_3;
+
+ std::vector<Weighted_point_3> weighted_points;
+
+ for (std::size_t i = 0; i < w_points.size(); i++) {
+ weighted_points.push_back(Weighted_point_3(w_points[i], weights[i]));
+ }
+ Weighted_alpha_complex_3d alpha_complex_w_p(weighted_points);
+
+ Gudhi::Simplex_tree<> stree_bis;
+ alpha_complex_w_p.create_complex(stree_bis);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ std::cout << "Weighted alpha complex 3d is of dimension " << stree_bis.dimension() << " - versus "
+ << stree.dimension() << std::endl;
+ BOOST_CHECK(stree_bis.dimension() == stree.dimension());
+ std::cout << "Weighted alpha complex 3d num_simplices " << stree_bis.num_simplices() << " - versus "
+ << stree.num_simplices() << std::endl;
+ BOOST_CHECK(stree_bis.num_simplices() == stree.num_simplices());
+ std::cout << "Weighted alpha complex 3d num_vertices " << stree_bis.num_vertices() << " - versus "
+ << stree.num_vertices() << std::endl;
+ BOOST_CHECK(stree_bis.num_vertices() == stree.num_vertices());
+
+ auto sh = stree.filtration_simplex_range().begin();
+ while (sh != stree.filtration_simplex_range().end()) {
+ std::vector<int> simplex;
+ std::vector<int> exact_simplex;
+#ifdef DEBUG_TRACES
+ std::cout << " ( ";
+#endif
+ for (auto vertex : stree.simplex_vertex_range(*sh)) {
+ simplex.push_back(vertex);
+#ifdef DEBUG_TRACES
+ std::cout << vertex << " ";
+#endif
+ }
+#ifdef DEBUG_TRACES
+ std::cout << ") -> "
+ << "[" << stree.filtration(*sh) << "] ";
+ std::cout << std::endl;
+#endif
+
+ // Find it in the exact structure
+ auto sh_exact = stree_bis.find(simplex);
+ BOOST_CHECK(sh_exact != stree_bis.null_simplex());
+
+ // Exact and non-exact version is not exactly the same due to float comparison
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree_bis.filtration(sh_exact), stree.filtration(*sh));
+
+ ++sh;
+ }
+}
diff --git a/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp
new file mode 100644
index 00000000..e8ac83e5
--- /dev/null
+++ b/src/Alpha_complex/test/Weighted_periodic_alpha_complex_3d_unit_test.cpp
@@ -0,0 +1,239 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "alpha_complex_3d"
+#include <boost/test/unit_test.hpp>
+#include <boost/mpl/list.hpp>
+
+#include <cmath> // float comparison
+#include <limits>
+#include <string>
+#include <vector>
+#include <random>
+#include <cstddef> // for std::size_t
+
+#include <gudhi/Alpha_complex_3d.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Unitary_tests_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_3D_off_io.h>
+
+#include <CGAL/Random.h>
+#include <CGAL/point_generators_3.h>
+
+using Fast_weighted_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, true>;
+using Safe_weighted_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, true>;
+using Exact_weighted_periodic_alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, true>;
+
+
+typedef boost::mpl::list<Fast_weighted_periodic_alpha_complex_3d, Exact_weighted_periodic_alpha_complex_3d,
+ Safe_weighted_periodic_alpha_complex_3d>
+ wp_variants_type_list;
+
+#ifdef GUDHI_DEBUG
+BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_periodic_throw, Weighted_periodic_alpha_complex_3d,
+ wp_variants_type_list) {
+ std::cout << "Weighted periodic alpha complex 3d exception throw" << std::endl;
+
+ using Creator = CGAL::Creator_uniform_3<double, typename Weighted_periodic_alpha_complex_3d::Point_3>;
+ CGAL::Random random(7);
+ CGAL::Random_points_in_cube_3<typename Weighted_periodic_alpha_complex_3d::Point_3, Creator> in_cube(1, random);
+ std::vector<typename Weighted_periodic_alpha_complex_3d::Point_3> wp_points;
+
+ for (int i = 0; i < 50; i++) {
+ typename Weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++;
+ wp_points.push_back(p);
+ }
+ std::vector<double> p_weights;
+ // Weights must be in range ]0, 1/64 = 0.015625[
+ for (std::size_t i = 0; i < wp_points.size(); ++i) {
+ p_weights.push_back(random.get_double(0., 0.01));
+ }
+
+ std::cout << "Cuboid is not iso exception" << std::endl;
+ // Check it throws an exception when the cuboid is not iso
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 0.9, 1., 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 1., 0.9, 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 1., 1., 0.9),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 1.1, 1., 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 1., 1.1, 1.),
+ std::invalid_argument);
+ BOOST_CHECK_THROW(
+ Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, -1., -1., -1., 1., 1., 1.1),
+ std::invalid_argument);
+
+ std::cout << "0 <= point.weight() < 1/64 * domain_size * domain_size exception" << std::endl;
+ // Weights must be in range ]0, 1/64 = 0.015625[
+ double temp = p_weights[25];
+ p_weights[25] = 1.0;
+ BOOST_CHECK_THROW(Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, 0., 0., 0., 1., 1., 1.),
+ std::invalid_argument);
+ // Weights must be in range ]0, 1/64 = 0.015625[
+ p_weights[25] = temp;
+ temp = p_weights[14];
+ p_weights[14] = -1e-10;
+ BOOST_CHECK_THROW(Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, 0., 0., 0., 1., 1., 1.),
+ std::invalid_argument);
+ p_weights[14] = temp;
+
+ std::cout << "wp_points and p_weights size exception" << std::endl;
+ // Weights and points must have the same size
+ // + 1
+ p_weights.push_back(1e-10);
+ BOOST_CHECK_THROW(Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, 0., 0., 0., 1., 1., 1.),
+ std::invalid_argument);
+ // - 1
+ p_weights.pop_back();
+ p_weights.pop_back();
+ BOOST_CHECK_THROW(Weighted_periodic_alpha_complex_3d wp_alpha_complex(wp_points, p_weights, 0., 0., 0., 1., 1., 1.),
+ std::invalid_argument);
+}
+#endif
+
+BOOST_AUTO_TEST_CASE(Alpha_complex_weighted_periodic) {
+ // ---------------------
+ // Fast weighted periodic version
+ // ---------------------
+ std::cout << "Fast weighted periodic alpha complex 3d" << std::endl;
+
+ using Creator = CGAL::Creator_uniform_3<double, Fast_weighted_periodic_alpha_complex_3d::Point_3>;
+ CGAL::Random random(7);
+ CGAL::Random_points_in_cube_3<Fast_weighted_periodic_alpha_complex_3d::Point_3, Creator> in_cube(1, random);
+ std::vector<Fast_weighted_periodic_alpha_complex_3d::Point_3> p_points;
+
+ for (int i = 0; i < 50; i++) {
+ Fast_weighted_periodic_alpha_complex_3d::Point_3 p = *in_cube++;
+ p_points.push_back(p);
+ }
+ std::vector<double> p_weights;
+ // Weights must be in range ]0, 1/64 = 0.015625[
+ for (std::size_t i = 0; i < p_points.size(); ++i) {
+ p_weights.push_back(random.get_double(0., 0.01));
+ }
+
+ Fast_weighted_periodic_alpha_complex_3d periodic_alpha_complex(p_points, p_weights, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> stree;
+ periodic_alpha_complex.create_complex(stree);
+
+ // ----------------------
+ // Exact weighted periodic version
+ // ----------------------
+ std::cout << "Exact weighted periodic alpha complex 3d" << std::endl;
+
+ std::vector<Exact_weighted_periodic_alpha_complex_3d::Point_3> e_p_points;
+
+ for (auto p : p_points) {
+ e_p_points.push_back(Exact_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2]));
+ }
+
+ Exact_weighted_periodic_alpha_complex_3d exact_alpha_complex(e_p_points, p_weights, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> exact_stree;
+ exact_alpha_complex.create_complex(exact_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ std::cout << "Exact weighted periodic alpha complex 3d is of dimension " << exact_stree.dimension()
+ << " - Non exact is " << stree.dimension() << std::endl;
+ BOOST_CHECK(exact_stree.dimension() == stree.dimension());
+ std::cout << "Exact weighted periodic alpha complex 3d num_simplices " << exact_stree.num_simplices()
+ << " - Non exact is " << stree.num_simplices() << std::endl;
+ BOOST_CHECK(exact_stree.num_simplices() == stree.num_simplices());
+ std::cout << "Exact weighted periodic alpha complex 3d num_vertices " << exact_stree.num_vertices()
+ << " - Non exact is " << stree.num_vertices() << std::endl;
+ BOOST_CHECK(exact_stree.num_vertices() == stree.num_vertices());
+
+ // We cannot compare as objects from dispatcher on the alpha shape is not deterministic.
+ // cf. https://github.com/CGAL/cgal/issues/3346
+ auto sh = stree.filtration_simplex_range().begin();
+ auto sh_exact = exact_stree.filtration_simplex_range().begin();
+
+ while (sh != stree.filtration_simplex_range().end() || sh_exact != exact_stree.filtration_simplex_range().end()) {
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree.filtration(*sh), exact_stree.filtration(*sh_exact), 1e-14);
+
+ std::vector<int> vh(stree.simplex_vertex_range(*sh).begin(), stree.simplex_vertex_range(*sh).end());
+ std::vector<int> exact_vh(exact_stree.simplex_vertex_range(*sh_exact).begin(),
+ exact_stree.simplex_vertex_range(*sh_exact).end());
+
+ BOOST_CHECK(vh.size() == exact_vh.size());
+ ++sh;
+ ++sh_exact;
+ }
+
+ BOOST_CHECK(sh == stree.filtration_simplex_range().end());
+ BOOST_CHECK(sh_exact == exact_stree.filtration_simplex_range().end());
+
+ // ----------------------
+ // Safe weighted periodic version
+ // ----------------------
+ std::cout << "Safe weighted periodic alpha complex 3d" << std::endl;
+
+ std::vector<Safe_weighted_periodic_alpha_complex_3d::Point_3> s_p_points;
+
+ for (auto p : p_points) {
+ s_p_points.push_back(Safe_weighted_periodic_alpha_complex_3d::Point_3(p[0], p[1], p[2]));
+ }
+
+ Safe_weighted_periodic_alpha_complex_3d safe_alpha_complex(s_p_points, p_weights, -1., -1., -1., 1., 1., 1.);
+
+ Gudhi::Simplex_tree<> safe_stree;
+ safe_alpha_complex.create_complex(safe_stree);
+
+ // ---------------------
+ // Compare both versions
+ // ---------------------
+ // We cannot compare as objects from dispatcher on the alpha shape is not deterministic.
+ // cf. https://github.com/CGAL/cgal/issues/3346
+ sh = stree.filtration_simplex_range().begin();
+ auto sh_safe = safe_stree.filtration_simplex_range().begin();
+
+ while (sh != stree.filtration_simplex_range().end() || sh_safe != safe_stree.filtration_simplex_range().end()) {
+ GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree.filtration(*sh), safe_stree.filtration(*sh_safe), 1e-14);
+
+ std::vector<int> vh(stree.simplex_vertex_range(*sh).begin(), stree.simplex_vertex_range(*sh).end());
+ std::vector<int> safe_vh(safe_stree.simplex_vertex_range(*sh_safe).begin(),
+ safe_stree.simplex_vertex_range(*sh_safe).end());
+
+ BOOST_CHECK(vh.size() == safe_vh.size());
+ ++sh;
+ ++sh_safe;
+ }
+
+ BOOST_CHECK(sh == stree.filtration_simplex_range().end());
+ BOOST_CHECK(sh_safe == safe_stree.filtration_simplex_range().end());
+}
diff --git a/src/Alpha_complex/utilities/CMakeLists.txt b/src/Alpha_complex/utilities/CMakeLists.txt
index 7ace6064..e76edc5f 100644
--- a/src/Alpha_complex/utilities/CMakeLists.txt
+++ b/src/Alpha_complex/utilities/CMakeLists.txt
@@ -1,64 +1,61 @@
project(Alpha_complex_utilities)
-if(CGAL_FOUND)
- add_executable(alpha_complex_3d_persistence alpha_complex_3d_persistence.cpp)
- target_link_libraries(alpha_complex_3d_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
- add_executable(exact_alpha_complex_3d_persistence exact_alpha_complex_3d_persistence.cpp)
- target_link_libraries(exact_alpha_complex_3d_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
- add_executable(weighted_alpha_complex_3d_persistence weighted_alpha_complex_3d_persistence.cpp)
- target_link_libraries(weighted_alpha_complex_3d_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
+ add_executable (alpha_complex_persistence alpha_complex_persistence.cpp)
+ target_link_libraries(alpha_complex_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
if (TBB_FOUND)
- target_link_libraries(alpha_complex_3d_persistence ${TBB_LIBRARIES})
- target_link_libraries(exact_alpha_complex_3d_persistence ${TBB_LIBRARIES})
- target_link_libraries(weighted_alpha_complex_3d_persistence ${TBB_LIBRARIES})
+ target_link_libraries(alpha_complex_persistence ${TBB_LIBRARIES})
endif(TBB_FOUND)
-
- add_test(NAME Alpha_complex_utilities_alpha_complex_3d_persistence COMMAND $<TARGET_FILE:alpha_complex_3d_persistence>
+ add_test(NAME Alpha_complex_utilities_alpha_complex_persistence COMMAND $<TARGET_FILE:alpha_complex_persistence>
"${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45")
- add_test(NAME Alpha_complex_utilities_exact_alpha_complex_3d COMMAND $<TARGET_FILE:exact_alpha_complex_3d_persistence>
- "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45")
- add_test(NAME Alpha_complex_utilities_weighted_alpha_complex_3d COMMAND $<TARGET_FILE:weighted_alpha_complex_3d_persistence>
- "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.weights" "-p" "2" "-m" "0.45")
-
- install(TARGETS alpha_complex_3d_persistence DESTINATION bin)
- install(TARGETS exact_alpha_complex_3d_persistence DESTINATION bin)
- install(TARGETS weighted_alpha_complex_3d_persistence DESTINATION bin)
-
- if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
- add_executable (alpha_complex_persistence alpha_complex_persistence.cpp)
- target_link_libraries(alpha_complex_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
-
- add_executable(periodic_alpha_complex_3d_persistence periodic_alpha_complex_3d_persistence.cpp)
- target_link_libraries(periodic_alpha_complex_3d_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
-
- if (TBB_FOUND)
- target_link_libraries(alpha_complex_persistence ${TBB_LIBRARIES})
- target_link_libraries(periodic_alpha_complex_3d_persistence ${TBB_LIBRARIES})
- endif(TBB_FOUND)
- add_test(NAME Alpha_complex_utilities_alpha_complex_persistence COMMAND $<TARGET_FILE:alpha_complex_persistence>
- "${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off" "-p" "2" "-m" "0.45")
- add_test(NAME Alpha_complex_utilities_periodic_alpha_complex_3d_persistence COMMAND $<TARGET_FILE:periodic_alpha_complex_3d_persistence>
- "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.off" "${CMAKE_SOURCE_DIR}/data/points/iso_cuboid_3_in_0_1.txt" "-p" "2" "-m" "0")
- install(TARGETS alpha_complex_persistence DESTINATION bin)
- install(TARGETS periodic_alpha_complex_3d_persistence DESTINATION bin)
+ install(TARGETS alpha_complex_persistence DESTINATION bin)
- endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
+endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
- if (NOT CGAL_VERSION VERSION_LESS 4.11.0)
- add_executable(weighted_periodic_alpha_complex_3d_persistence weighted_periodic_alpha_complex_3d_persistence.cpp)
- target_link_libraries(weighted_periodic_alpha_complex_3d_persistence ${CGAL_LIBRARY})
- if (TBB_FOUND)
- target_link_libraries(weighted_periodic_alpha_complex_3d_persistence ${TBB_LIBRARIES})
- endif(TBB_FOUND)
-
- add_test(NAME Alpha_complex_utilities_weigted_periodic_alpha_complex_3d COMMAND $<TARGET_FILE:weighted_periodic_alpha_complex_3d_persistence>
- "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.off" "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.weights"
- "${CMAKE_SOURCE_DIR}/data/points/iso_cuboid_3_in_0_1.txt" "3" "1.0")
+if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
+ add_executable(alpha_complex_3d_persistence alpha_complex_3d_persistence.cpp)
+ target_link_libraries(alpha_complex_3d_persistence ${CGAL_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ if (TBB_FOUND)
+ target_link_libraries(alpha_complex_3d_persistence ${TBB_LIBRARIES})
+ endif(TBB_FOUND)
- install(TARGETS weighted_periodic_alpha_complex_3d_persistence DESTINATION bin)
+ 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" "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"
+ "-p" "2" "-m" "0.45" "-o" "exact.pers" "-e")
+
+ 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" "fast.pers" "-f")
+
+ if (DIFF_PATH)
+ add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH}
+ "exact.pers" "safe.pers")
+ add_test(Alpha_complex_utilities_diff_alpha_complex_3d ${DIFF_PATH}
+ "fast.pers" "safe.pers")
+ endif()
+
+ add_test(NAME Alpha_complex_utilities_periodic_alpha_complex_3d_persistence COMMAND $<TARGET_FILE:alpha_complex_3d_persistence>
+ "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.off"
+ "-c" "${CMAKE_SOURCE_DIR}/data/points/iso_cuboid_3_in_0_1.txt"
+ "-p" "2" "-m" "0")
+
+ add_test(NAME Alpha_complex_utilities_weighted_alpha_complex_3d COMMAND $<TARGET_FILE:alpha_complex_3d_persistence>
+ "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.off"
+ "-w" "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.weights"
+ "-p" "2" "-m" "0")
+
+ add_test(NAME Alpha_complex_utilities_weighted_periodic_alpha_complex_3d COMMAND $<TARGET_FILE:alpha_complex_3d_persistence>
+ "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.off"
+ "-w" "${CMAKE_SOURCE_DIR}/data/points/grid_10_10_10_in_0_1.weights"
+ "-c" "${CMAKE_SOURCE_DIR}/data/points/iso_cuboid_3_in_0_1.txt"
+ "-p" "2" "-m" "0" "-e")
- endif (NOT CGAL_VERSION VERSION_LESS 4.11.0)
+ install(TARGETS alpha_complex_3d_persistence DESTINATION bin)
-endif(CGAL_FOUND)
+endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0)
diff --git a/src/Alpha_complex/utilities/alpha_complex_3d_helper.h b/src/Alpha_complex/utilities/alpha_complex_3d_helper.h
deleted file mode 100644
index a72fd96d..00000000
--- a/src/Alpha_complex/utilities/alpha_complex_3d_helper.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* This file is part of the Gudhi Library. The Gudhi library
- * (Geometric Understanding in Higher Dimensions) is a generic C++
- * library for computational topology.
- *
- * Author(s): Vincent Rouvreau
- *
- * Copyright (C) 2014 Inria
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ALPHA_COMPLEX_3D_HELPER_H_
-#define ALPHA_COMPLEX_3D_HELPER_H_
-
-template <class Vertex_list, class Cell_handle>
-Vertex_list from_cell(const Cell_handle& ch) {
- Vertex_list the_list;
- for (auto i = 0; i < 4; i++) {
-#ifdef DEBUG_TRACES
- std::cout << "from cell[" << i << "]=" << ch->vertex(i)->point() << std::endl;
-#endif // DEBUG_TRACES
- the_list.push_back(ch->vertex(i));
- }
- return the_list;
-}
-
-template <class Vertex_list, class Facet>
-Vertex_list from_facet(const Facet& fct) {
- Vertex_list the_list;
- for (auto i = 0; i < 4; i++) {
- if (fct.second != i) {
-#ifdef DEBUG_TRACES
- std::cout << "from facet=[" << i << "]" << fct.first->vertex(i)->point() << std::endl;
-#endif // DEBUG_TRACES
- the_list.push_back(fct.first->vertex(i));
- }
- }
- return the_list;
-}
-
-template <class Vertex_list, class Edge_3>
-Vertex_list from_edge(const Edge_3& edg) {
- Vertex_list the_list;
- for (auto i : {edg.second, edg.third}) {
-#ifdef DEBUG_TRACES
- std::cout << "from edge[" << i << "]=" << edg.first->vertex(i)->point() << std::endl;
-#endif // DEBUG_TRACES
- the_list.push_back(edg.first->vertex(i));
- }
- return the_list;
-}
-
-template <class Vertex_list, class Vertex_handle>
-Vertex_list from_vertex(const Vertex_handle& vh) {
- Vertex_list the_list;
-#ifdef DEBUG_TRACES
- std::cout << "from vertex=" << vh->point() << std::endl;
-#endif // DEBUG_TRACES
- the_list.push_back(vh);
- return the_list;
-}
-
-#endif // ALPHA_COMPLEX_3D_HELPER_H_
diff --git a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp
index 6e603155..09c84eb3 100644
--- a/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp
+++ b/src/Alpha_complex/utilities/alpha_complex_3d_persistence.cpp
@@ -20,188 +20,219 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <boost/version.hpp>
#include <boost/program_options.hpp>
#include <boost/variant.hpp>
-#if BOOST_VERSION >= 105400
-#include <boost/container/static_vector.hpp>
-#endif
-
+#include <gudhi/Alpha_complex_3d.h>
#include <gudhi/Simplex_tree.h>
#include <gudhi/Persistent_cohomology.h>
#include <gudhi/Points_3D_off_io.h>
-#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
-#include <CGAL/Delaunay_triangulation_3.h>
-#include <CGAL/Alpha_shape_3.h>
-#include <CGAL/Alpha_shape_cell_base_3.h>
-#include <CGAL/Alpha_shape_vertex_base_3.h>
-#include <CGAL/iterator.h>
-
#include <fstream>
-#include <cmath>
#include <string>
-#include <tuple>
-#include <map>
-#include <utility>
#include <vector>
-#include <cstdlib>
-
-#include "alpha_complex_3d_helper.h"
-
-// Alpha_shape_3 templates type definitions
-using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
-using Vb = CGAL::Alpha_shape_vertex_base_3<Kernel>;
-using Fb = CGAL::Alpha_shape_cell_base_3<Kernel>;
-using Tds = CGAL::Triangulation_data_structure_3<Vb, Fb>;
-using Triangulation_3 = CGAL::Delaunay_triangulation_3<Kernel, Tds>;
-using Alpha_shape_3 = CGAL::Alpha_shape_3<Triangulation_3>;
-
-// From file type definition
-using Point_3 = Kernel::Point_3;
-
-// filtration with alpha values needed type definition
-using Alpha_value_type = Alpha_shape_3::FT;
-using Object = CGAL::Object;
-using Dispatch =
- CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<Object, Alpha_value_type>,
- CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<Object> >,
- std::back_insert_iterator<std::vector<Alpha_value_type> > > >;
-using Cell_handle = Alpha_shape_3::Cell_handle;
-using Facet = Alpha_shape_3::Facet;
-using Edge_3 = Alpha_shape_3::Edge;
-using Vertex_handle = Alpha_shape_3::Vertex_handle;
-
-#if BOOST_VERSION >= 105400
-using Vertex_list = boost::container::static_vector<Alpha_shape_3::Vertex_handle, 4>;
-#else
-using Vertex_list = std::vector<Alpha_shape_3::Vertex_handle>;
-#endif
+#include <limits> // for numeric_limits<>
// gudhi type definition
-using ST = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
-using Filtration_value = ST::Filtration_value;
-using Simplex_tree_vertex = ST::Vertex_handle;
-using Alpha_shape_simplex_tree_map = std::map<Alpha_shape_3::Vertex_handle, Simplex_tree_vertex>;
-using Simplex_tree_vector_vertex = std::vector<Simplex_tree_vertex>;
+using Simplex_tree = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
+using Filtration_value = Simplex_tree::Filtration_value;
using Persistent_cohomology =
- Gudhi::persistent_cohomology::Persistent_cohomology<ST, Gudhi::persistent_cohomology::Field_Zp>;
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag,
- int &coeff_field_characteristic, Filtration_value &min_persistence);
-
-int main(int argc, char **argv) {
- std::string off_file_points;
- std::string output_file_diag;
- int coeff_field_characteristic;
- Filtration_value min_persistence;
+ Gudhi::persistent_cohomology::Persistent_cohomology<Simplex_tree, Gudhi::persistent_cohomology::Field_Zp>;
+
+void program_options(int argc, char *argv[], std::string &off_file_points, bool &exact, bool &safe,
+ 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);
+
+bool read_weight_file(const std::string &weight_file, std::vector<double> &weights) {
+ // Read weights information from file
+ std::ifstream weights_ifstr(weight_file);
+ if (weights_ifstr.good()) {
+ double weight = 0.0;
+ // Attempt read the weight in a double format, return false if it fails
+ while (weights_ifstr >> weight) {
+ weights.push_back(weight);
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
- program_options(argc, argv, off_file_points, output_file_diag, coeff_field_characteristic, min_persistence);
+bool read_cuboid_file(const std::string &cuboid_file, double &x_min, double &y_min, double &z_min, double &x_max,
+ double &y_max, double &z_max) {
+ // Read weights information from file
+ std::ifstream iso_cuboid_str(cuboid_file);
+ if (iso_cuboid_str.is_open()) {
+ if (!(iso_cuboid_str >> x_min >> y_min >> z_min >> x_max >> y_max >> z_max)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+template <typename AlphaComplex3d>
+std::vector<typename AlphaComplex3d::Point_3> read_off(const std::string &off_file_points) {
// Read the OFF file (input file name given as parameter) and triangulate points
- Gudhi::Points_3D_off_reader<Point_3> off_reader(off_file_points);
+ Gudhi::Points_3D_off_reader<typename AlphaComplex3d::Point_3> off_reader(off_file_points);
// Check the read operation was correct
if (!off_reader.is_valid()) {
- std::cerr << "Unable to read file " << off_file_points << std::endl;
+ std::cerr << "Unable to read OFF file " << off_file_points << std::endl;
exit(-1);
}
+ return off_reader.get_point_cloud();
+}
- // Retrieve the points
- std::vector<Point_3> lp = off_reader.get_point_cloud();
-
- // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode.
- Alpha_shape_3 as(lp.begin(), lp.end(), 0, Alpha_shape_3::GENERAL);
-#ifdef DEBUG_TRACES
- std::cout << "Alpha shape computed in GENERAL mode" << std::endl;
-#endif // DEBUG_TRACES
-
- // filtration with alpha values from alpha shape
- std::vector<Object> the_objects;
- std::vector<Alpha_value_type> the_alpha_values;
-
- Dispatch disp = CGAL::dispatch_output<Object, Alpha_value_type>(std::back_inserter(the_objects),
- std::back_inserter(the_alpha_values));
-
- as.filtration_with_alpha_values(disp);
-#ifdef DEBUG_TRACES
- std::cout << "filtration_with_alpha_values returns : " << the_objects.size() << " objects" << std::endl;
-#endif // DEBUG_TRACES
-
- Alpha_shape_3::size_type count_vertices = 0;
- Alpha_shape_3::size_type count_edges = 0;
- Alpha_shape_3::size_type count_facets = 0;
- Alpha_shape_3::size_type count_cells = 0;
-
- // Loop on objects vector
- Vertex_list vertex_list;
- ST simplex_tree;
- Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
- std::vector<Alpha_value_type>::iterator the_alpha_value_iterator = the_alpha_values.begin();
- for (auto object_iterator : the_objects) {
- // Retrieve Alpha shape vertex list from object
- if (const Cell_handle *cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
- vertex_list = from_cell<Vertex_list, Cell_handle>(*cell);
- count_cells++;
- } else if (const Facet *facet = CGAL::object_cast<Facet>(&object_iterator)) {
- vertex_list = from_facet<Vertex_list, Facet>(*facet);
- count_facets++;
- } else if (const Edge_3 *edge = CGAL::object_cast<Edge_3>(&object_iterator)) {
- vertex_list = from_edge<Vertex_list, Edge_3>(*edge);
- count_edges++;
- } else if (const Vertex_handle *vertex = CGAL::object_cast<Vertex_handle>(&object_iterator)) {
- count_vertices++;
- vertex_list = from_vertex<Vertex_list, Vertex_handle>(*vertex);
- }
- // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
- Simplex_tree_vector_vertex the_simplex;
- for (auto the_alpha_shape_vertex : vertex_list) {
- Alpha_shape_simplex_tree_map::iterator the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
- if (the_map_iterator == map_cgal_simplex_tree.end()) {
- // alpha shape not found
- Simplex_tree_vertex vertex = map_cgal_simplex_tree.size();
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
- } else {
- // alpha shape found
- Simplex_tree_vertex vertex = the_map_iterator->second;
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- }
+int main(int argc, char **argv) {
+ std::string off_file_points;
+ std::string weight_file;
+ std::string cuboid_file;
+ std::string output_file_diag;
+ Filtration_value alpha_square_max_value = 0.;
+ int coeff_field_characteristic = 0;
+ Filtration_value min_persistence = 0.;
+ bool exact_version = false;
+ bool fast_version = false;
+ bool weighted_version = false;
+ bool periodic_version = false;
+
+ 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;
+ if (weight_file != std::string()) {
+ if (!read_weight_file(weight_file, weights)) {
+ std::cerr << "Unable to read weights file " << weight_file << std::endl;
+ exit(-1);
}
- // Construction of the simplex_tree
- Filtration_value filtr = /*std::sqrt*/ (*the_alpha_value_iterator);
-#ifdef DEBUG_TRACES
- std::cout << "filtration = " << filtr << std::endl;
-#endif // DEBUG_TRACES
- simplex_tree.insert_simplex(the_simplex, filtr);
- GUDHI_CHECK(the_alpha_value_iterator != the_alpha_values.end(), "CGAL provided more simplices than values");
- ++the_alpha_value_iterator;
+ weighted_version = true;
}
-#ifdef DEBUG_TRACES
- std::cout << "vertices \t\t" << count_vertices << std::endl;
- std::cout << "edges \t\t" << count_edges << std::endl;
- std::cout << "facets \t\t" << count_facets << std::endl;
- std::cout << "cells \t\t" << count_cells << std::endl;
+ double x_min = 0., y_min = 0., z_min = 0., x_max = 0., y_max = 0., z_max = 0.;
+ std::ifstream iso_cuboid_str(argv[3]);
+ if (cuboid_file != std::string()) {
+ if (!read_cuboid_file(cuboid_file, x_min, y_min, z_min, x_max, y_max, z_max)) {
+ std::cerr << "Unable to read cuboid file " << cuboid_file << std::endl;
+ exit(-1);
+ }
+ periodic_version = true;
+ }
- std::cout << "Information of the Simplex Tree: " << std::endl;
- std::cout << " Number of vertices = " << simplex_tree.num_vertices() << " ";
- std::cout << " Number of simplices = " << simplex_tree.num_simplices() << std::endl << std::endl;
- std::cout << " Dimension = " << simplex_tree.dimension() << " ";
-#endif // DEBUG_TRACES
+ Gudhi::alpha_complex::complexity complexity = Gudhi::alpha_complex::complexity::SAFE;
+ if (exact_version) {
+ 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 (fast_version) {
+ complexity = Gudhi::alpha_complex::complexity::FAST;
+ }
-#ifdef DEBUG_TRACES
- std::cout << "Iterator on vertices: " << std::endl;
- for (auto vertex : simplex_tree.complex_vertex_range()) {
- std::cout << vertex << " ";
+ Simplex_tree simplex_tree;
+
+ switch (complexity) {
+ case Gudhi::alpha_complex::complexity::FAST:
+ if (weighted_version) {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ } else {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, false, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ }
+ break;
+ case Gudhi::alpha_complex::complexity::EXACT:
+ if (weighted_version) {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ } else {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, false, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ }
+ break;
+ case Gudhi::alpha_complex::complexity::SAFE:
+ if (weighted_version) {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, weights);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ } else {
+ if (periodic_version) {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, true>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points, x_min, y_min, z_min, x_max, y_max, z_max);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ } else {
+ using Alpha_complex_3d =
+ Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>;
+ auto points = read_off<Alpha_complex_3d>(off_file_points);
+ Alpha_complex_3d alpha_complex(points);
+ alpha_complex.create_complex(simplex_tree, alpha_square_max_value);
+ }
+ }
+ break;
+ default:
+ std::cerr << "Unknown complexity value " << std::endl;
+ exit(-1);
+ break;
}
-#endif // DEBUG_TRACES
// Sort the simplices in the order of the filtration
simplex_tree.initialize_filtration();
@@ -227,8 +258,10 @@ int main(int argc, char **argv) {
return 0;
}
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag,
- int &coeff_field_characteristic, Filtration_value &min_persistence) {
+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) {
namespace po = boost::program_options;
po::options_description hidden("Hidden options");
hidden.add_options()("input-file", po::value<std::string>(&off_file_points),
@@ -236,8 +269,20 @@ void program_options(int argc, char *argv[], std::string &off_file_points, std::
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 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),
+ "Name of file describing the periodic domain. Format is:\n min_hx min_hy min_hz\n max_hx max_hy max_hz")(
"output-file,o", po::value<std::string>(&output_file_diag)->default_value(std::string()),
"Name of file in which the persistence diagram is written. Default print in std::cout")(
+ "max-alpha-square-value,r",
+ po::value<Filtration_value>(&alpha_square_max_value)
+ ->default_value(std::numeric_limits<Filtration_value>::infinity()),
+ "Maximal alpha square value for the Alpha complex construction.")(
"field-charac,p", po::value<int>(&coeff_field_characteristic)->default_value(11),
"Characteristic p of the coefficient field Z/pZ for computing homology.")(
"min-persistence,m", po::value<Filtration_value>(&min_persistence),
@@ -254,17 +299,18 @@ void program_options(int argc, char *argv[], std::string &off_file_points, std::
po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm);
po::notify(vm);
- if (vm.count("help") || !vm.count("input-file")) {
+ if (vm.count("help") || !vm.count("input-file") || !vm.count("weight-file")) {
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 \n";
+ std::cout << "of a 3D Alpha complex defined on a set of input points.\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";
std::cout << "b and d are respectively the birth and death of the feature and \n";
- std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;
+ std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients.\n\n";
- std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl;
+ std::cout << "Usage: " << argv[0] << " [options] input-file weight-file\n\n";
std::cout << visible << std::endl;
exit(-1);
}
diff --git a/src/Alpha_complex/utilities/alphacomplex.md b/src/Alpha_complex/utilities/alphacomplex.md
index 0fe98837..50a39d32 100644
--- a/src/Alpha_complex/utilities/alphacomplex.md
+++ b/src/Alpha_complex/utilities/alphacomplex.md
@@ -12,15 +12,18 @@ Leave the lines above as it is required by the web site generator 'Jekyll'
## alpha_complex_persistence ##
-This program computes the persistent homology with coefficient field Z/pZ of the dD alpha complex built from a dD point cloud.
+This program computes the persistent homology with coefficient field Z/pZ of
+the dD alpha complex built from a dD point cloud.
The output diagram contains one bar per line, written with the convention:
```
p dim birth death
```
-where `dim` is the dimension of the homological feature, `birth` and `death` are respectively the birth and death of the feature,
-and `p` is the characteristic of the field *Z/pZ* used for homology coefficients (`p` must be a prime number).
+where `dim` is the dimension of the homological feature, `birth` and `death`
+are respectively the birth and death of the feature, and `p` is the
+characteristic of the field *Z/pZ* used for homology coefficients (`p` must be
+a prime number).
**Usage**
@@ -29,15 +32,20 @@ and `p` is the characteristic of the field *Z/pZ* used for homology coefficients
```
where
-`<input OFF file>` is the path to the input point cloud in [nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
+`<input OFF file>` is the path to the input point cloud in
+[nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
**Allowed options**
* `-h [ --help ]` Produce help message
-* `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output.
-* `-r [ --max-alpha-square-value ]` (default = inf) Maximal alpha square value for the Alpha complex construction.
-* `-p [ --field-charac ]` (default = 11) Characteristic p of the coefficient field Z/pZ for computing homology.
-* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals.
+* `-o [ --output-file ]` Name of file in which the persistence diagram is
+written. Default print in standard output.
+* `-r [ --max-alpha-square-value ]` (default = inf) Maximal alpha square value
+for the Alpha complex construction.
+* `-p [ --field-charac ]` (default = 11) Characteristic p of the
+coefficient field Z/pZ for computing homology.
+* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature
+to be recorded. Enter a negative value to see zero length intervals.
**Example**
@@ -51,13 +59,26 @@ N.B.:
## alpha_complex_3d_persistence ##
-This program computes the persistent homology with coefficient field Z/pZ of the 3D alpha complex built from a 3D point cloud. The output diagram contains one bar per line, written with the convention:
+This program computes the persistent homology with coefficient field Z/pZ of
+the 3D alpha complex built from a 3D point cloud.
+One can use exact computation. It is slower, but it is necessary when points
+are on a grid for instance.
+Alpha complex 3d can be weighted and/or periodic (refer to the
+[CGAL's 3D Periodic Triangulations User Manual](
+https://doc.cgal.org/latest/Periodic_3_triangulation_3/index.html)
+for more details).
+
+The output diagram contains
+one bar per line, written with the convention:
```
p dim birth death
```
-where `dim` is the dimension of the homological feature, `birth` and `death` are respectively the birth and death of the feature, and `p` is the characteristic of the field *Z/pZ* used for homology coefficients (`p` must be a prime number).
+where `dim` is the dimension of the homological feature, `birth` and `death`
+are respectively the birth and death of the feature, and `p` is the
+characteristic of the field *Z/pZ* used for homology coefficients (`p` must be
+a prime number).
**Usage**
@@ -65,14 +86,28 @@ where `dim` is the dimension of the homological feature, `birth` and `death` are
alpha_complex_3d_persistence [options] <input OFF file>
```
-where `<input OFF file>` is the path to the input point cloud in [nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
+where `<input OFF file>` is the path to the input point cloud in
+[nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
**Allowed options**
* `-h [ --help ]` Produce help message
-* `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output.
-* `-p [ --field-charac ]` (default=11) Characteristic p of the coefficient field Z/pZ for computing homology.
-* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals.
+* `-o [ --output-file ]` Name of file in which the persistence diagram is
+written. Default print in standard output.
+* `-r [ --max-alpha-square-value ]` (default = inf) Maximal alpha square value
+for the Alpha complex construction.
+* `-p [ --field-charac ]` (default=11) Characteristic p of the coefficient
+field Z/pZ for computing homology.
+* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature
+to be recorded. Enter a negative value to see zero length intervals.
+* `-c [ --cuboid-file ]` is the path to the file describing the periodic domain.
+It must be in the format described
+[here]({{ site.officialurl }}/doc/latest/fileformats.html#FileFormatsIsoCuboid).
+* `-w [ --weight-file ]` is the path to the file containing the weights of the
+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**
@@ -84,82 +119,7 @@ N.B.:
* `alpha_complex_3d_persistence` only accepts OFF files in dimension 3.
* Filtration values are alpha square values.
-
-
-## exact_alpha_complex_3d_persistence ##
-
-Same as `alpha_complex_3d_persistence`, but using exact computation.
-It is slower, but it is necessary when points are on a grid for instance.
-
-
-
-## weighted_alpha_complex_3d_persistence ##
-
-Same as `alpha_complex_3d_persistence`, but using weighted points.
-
-**Usage**
-
-```
- weighted_alpha_complex_3d_persistence [options] <input OFF file> <weights input file>
-```
-
-where
-
-* `<input OFF file>` is the path to the input point cloud in [nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
-* `<input weights file>` is the path to the file containing the weights of the points (one value per line).
-
-**Allowed options**
-
-* `-h [ --help ]` Produce help message
-* `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output.
-* `-p [ --field-charac ]` (default=11) Characteristic p of the coefficient field Z/pZ for computing homology.
-* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals.
-
-**Example**
-
-```
- weighted_alpha_complex_3d_persistence ../../data/points/tore3D_300.off ../../data/points/tore3D_300.weights -p 2 -m 0.45
-```
-
-
-N.B.:
-
-* Weights values are explained on CGAL [Alpha shape](https://doc.cgal.org/latest/Alpha_shapes_3/index.html#title0)
-and [Regular triangulation](https://doc.cgal.org/latest/Triangulation_3/index.html#Triangulation3secclassRegulartriangulation) documentation.
-* Filtration values are alpha square values.
-
-
-## periodic_alpha_complex_3d_persistence ##
-Same as `alpha_complex_3d_persistence`, but using periodic alpha shape 3d.
-Refer to the [CGAL's 3D Periodic Triangulations User Manual](https://doc.cgal.org/latest/Periodic_3_triangulation_3/index.html) for more details.
-
-**Usage**
-
-```
- periodic_alpha_complex_3d_persistence [options] <input OFF file> <cuboid file>
-```
-
-where
-
-* `<input OFF file>` is the path to the input point cloud in [nOFF ASCII format](http://www.geomview.org/docs/html/OFF.html).
-* `<cuboid file>` is the path to the file describing the periodic domain. It must be in the format described
-[here](/doc/latest/fileformats.html#FileFormatsIsoCuboid).
-
-**Allowed options**
-
-* `-h [ --help ]` Produce help message
-* `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output.
-* `-p [ --field-charac ]` (default=11) Characteristic p of the coefficient field Z/pZ for computing homology.
-* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals
-
-
-**Example**
-
-```
-periodic_alpha_complex_3d_persistence ../../data/points/grid_10_10_10_in_0_1.off ../../data/points/iso_cuboid_3_in_0_1.txt -p 3 -m 1.0
-```
-
-N.B.:
-
-* Cuboid file must be in the format described [here](/doc/latest/fileformats.html#FileFormatsIsoCuboid).
-* Filtration values are alpha square values.
+* Weights values are explained on CGAL
+[Alpha shape](https://doc.cgal.org/latest/Alpha_shapes_3/index.html#title0)
+and
+[Regular triangulation](https://doc.cgal.org/latest/Triangulation_3/index.html#Triangulation3secclassRegulartriangulation) documentation.
diff --git a/src/Alpha_complex/utilities/exact_alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/exact_alpha_complex_3d_persistence.cpp
deleted file mode 100644
index 61f49bb1..00000000
--- a/src/Alpha_complex/utilities/exact_alpha_complex_3d_persistence.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/* This file is part of the Gudhi Library. The Gudhi library
- * (Geometric Understanding in Higher Dimensions) is a generic C++
- * library for computational topology.
- *
- * Author(s): Vincent Rouvreau
- *
- * Copyright (C) 2014 Inria
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/program_options.hpp>
-#include <boost/variant.hpp>
-
-#include <gudhi/Simplex_tree.h>
-#include <gudhi/Persistent_cohomology.h>
-#include <gudhi/Points_3D_off_io.h>
-
-#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
-#include <CGAL/Delaunay_triangulation_3.h>
-#include <CGAL/Alpha_shape_3.h>
-#include <CGAL/Alpha_shape_cell_base_3.h>
-#include <CGAL/Alpha_shape_vertex_base_3.h>
-#include <CGAL/iterator.h>
-
-#include <fstream>
-#include <cmath>
-#include <string>
-#include <tuple>
-#include <map>
-#include <utility>
-#include <vector>
-#include <cstdlib>
-
-#include "alpha_complex_3d_helper.h"
-
-// Alpha_shape_3 templates type definitions
-using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
-using Exact_tag = CGAL::Tag_true;
-using Vb = CGAL::Alpha_shape_vertex_base_3<Kernel, CGAL::Default, Exact_tag>;
-using Fb = CGAL::Alpha_shape_cell_base_3<Kernel, CGAL::Default, Exact_tag>;
-using Tds = CGAL::Triangulation_data_structure_3<Vb, Fb>;
-using Triangulation_3 = CGAL::Delaunay_triangulation_3<Kernel, Tds>;
-using Alpha_shape_3 = CGAL::Alpha_shape_3<Triangulation_3, Exact_tag>;
-
-// From file type definition
-using Point_3 = Kernel::Point_3;
-
-// filtration with alpha values needed type definition
-using Alpha_value_type = Alpha_shape_3::FT;
-using Object = CGAL::Object;
-using Dispatch =
- CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<Object, Alpha_value_type>,
- CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<Object> >,
- std::back_insert_iterator<std::vector<Alpha_value_type> > > >;
-using Cell_handle = Alpha_shape_3::Cell_handle;
-using Facet = Alpha_shape_3::Facet;
-using Edge_3 = Alpha_shape_3::Edge;
-using Vertex_handle = Alpha_shape_3::Vertex_handle;
-using Vertex_list = std::vector<Vertex_handle>;
-
-// gudhi type definition
-using ST = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
-using Filtration_value = ST::Filtration_value;
-using Simplex_tree_vertex = ST::Vertex_handle;
-using Alpha_shape_simplex_tree_map = std::map<Alpha_shape_3::Vertex_handle, Simplex_tree_vertex>;
-using Simplex_tree_vector_vertex = std::vector<Simplex_tree_vertex>;
-using Persistent_cohomology =
- Gudhi::persistent_cohomology::Persistent_cohomology<ST, Gudhi::persistent_cohomology::Field_Zp>;
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag,
- int &coeff_field_characteristic, Filtration_value &min_persistence);
-
-int main(int argc, char **argv) {
- std::string off_file_points;
- std::string output_file_diag;
- int coeff_field_characteristic;
- Filtration_value min_persistence;
-
- program_options(argc, argv, off_file_points, output_file_diag, coeff_field_characteristic, min_persistence);
-
- // Read the OFF file (input file name given as parameter) and triangulate points
- Gudhi::Points_3D_off_reader<Point_3> off_reader(off_file_points);
- // Check the read operation was correct
- if (!off_reader.is_valid()) {
- std::cerr << "Unable to read file " << off_file_points << std::endl;
- exit(-1);
- }
-
- // Retrieve the points
- std::vector<Point_3> lp = off_reader.get_point_cloud();
-
- // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode.
- Alpha_shape_3 as(lp.begin(), lp.end(), 0, Alpha_shape_3::GENERAL);
-#ifdef DEBUG_TRACES
- std::cout << "Alpha shape computed in GENERAL mode" << std::endl;
-#endif // DEBUG_TRACES
-
- // filtration with alpha values from alpha shape
- std::vector<Object> the_objects;
- std::vector<Alpha_value_type> the_alpha_values;
-
- Dispatch disp = CGAL::dispatch_output<Object, Alpha_value_type>(std::back_inserter(the_objects),
- std::back_inserter(the_alpha_values));
-
- as.filtration_with_alpha_values(disp);
-#ifdef DEBUG_TRACES
- std::cout << "filtration_with_alpha_values returns : " << the_objects.size() << " objects" << std::endl;
-#endif // DEBUG_TRACES
-
- Alpha_shape_3::size_type count_vertices = 0;
- Alpha_shape_3::size_type count_edges = 0;
- Alpha_shape_3::size_type count_facets = 0;
- Alpha_shape_3::size_type count_cells = 0;
-
- // Loop on objects vector
- Vertex_list vertex_list;
- ST simplex_tree;
- Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
- std::vector<Alpha_value_type>::iterator the_alpha_value_iterator = the_alpha_values.begin();
- for (auto object_iterator : the_objects) {
- // Retrieve Alpha shape vertex list from object
- if (const Cell_handle *cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
- vertex_list = from_cell<Vertex_list, Cell_handle>(*cell);
- count_cells++;
- } else if (const Facet *facet = CGAL::object_cast<Facet>(&object_iterator)) {
- vertex_list = from_facet<Vertex_list, Facet>(*facet);
- count_facets++;
- } else if (const Edge_3 *edge = CGAL::object_cast<Edge_3>(&object_iterator)) {
- vertex_list = from_edge<Vertex_list, Edge_3>(*edge);
- count_edges++;
- } else if (const Vertex_handle *vertex = CGAL::object_cast<Vertex_handle>(&object_iterator)) {
- count_vertices++;
- vertex_list = from_vertex<Vertex_list, Vertex_handle>(*vertex);
- }
- // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
- Simplex_tree_vector_vertex the_simplex;
- for (auto the_alpha_shape_vertex : vertex_list) {
- Alpha_shape_simplex_tree_map::iterator the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
- if (the_map_iterator == map_cgal_simplex_tree.end()) {
- // alpha shape not found
- Simplex_tree_vertex vertex = map_cgal_simplex_tree.size();
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
- } else {
- // alpha shape found
- Simplex_tree_vertex vertex = the_map_iterator->second;
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- }
- }
- // Construction of the simplex_tree
- // you can also use the_alpha_value_iterator->exact()
- Filtration_value filtr = /*std::sqrt*/ CGAL::to_double(the_alpha_value_iterator->exact());
-#ifdef DEBUG_TRACES
- std::cout << "filtration = " << filtr << std::endl;
-#endif // DEBUG_TRACES
- simplex_tree.insert_simplex(the_simplex, filtr);
- if (the_alpha_value_iterator != the_alpha_values.end())
- ++the_alpha_value_iterator;
- else
- std::cout << "This shall not happen" << std::endl;
- }
-
-#ifdef DEBUG_TRACES
- std::cout << "vertices \t\t" << count_vertices << std::endl;
- std::cout << "edges \t\t" << count_edges << std::endl;
- std::cout << "facets \t\t" << count_facets << std::endl;
- std::cout << "cells \t\t" << count_cells << std::endl;
-
- std::cout << "Information of the Simplex Tree: " << std::endl;
- std::cout << " Number of vertices = " << simplex_tree.num_vertices() << " ";
- std::cout << " Number of simplices = " << simplex_tree.num_simplices() << std::endl << std::endl;
- std::cout << " Dimension = " << simplex_tree.dimension() << " ";
-#endif // DEBUG_TRACES
-
-#ifdef DEBUG_TRACES
- std::cout << "Iterator on vertices: " << std::endl;
- for (auto vertex : simplex_tree.complex_vertex_range()) {
- std::cout << vertex << " ";
- }
-#endif // DEBUG_TRACES
-
- // Sort the simplices in the order of the filtration
- simplex_tree.initialize_filtration();
-
- std::cout << "Simplex_tree dim: " << simplex_tree.dimension() << std::endl;
- // Compute the persistence diagram of the complex
- Persistent_cohomology pcoh(simplex_tree, true);
- // initializes the coefficient field for homology
- pcoh.init_coefficients(coeff_field_characteristic);
-
- pcoh.compute_persistent_cohomology(min_persistence);
-
- // Output the diagram in filediag
- if (output_file_diag.empty()) {
- pcoh.output_diagram();
- } else {
- std::cout << "Result in file: " << output_file_diag << std::endl;
- std::ofstream out(output_file_diag);
- pcoh.output_diagram(out);
- out.close();
- }
-
- return 0;
-}
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &output_file_diag,
- int &coeff_field_characteristic, Filtration_value &min_persistence) {
- namespace po = boost::program_options;
- po::options_description hidden("Hidden options");
- hidden.add_options()("input-file", po::value<std::string>(&off_file_points),
- "Name of file containing a point set. Format is one point per line: X1 ... Xd ");
-
- po::options_description visible("Allowed options", 100);
- visible.add_options()("help,h", "produce help message")(
- "output-file,o", po::value<std::string>(&output_file_diag)->default_value(std::string()),
- "Name of file in which the persistence diagram is written. Default print in std::cout")(
- "field-charac,p", po::value<int>(&coeff_field_characteristic)->default_value(11),
- "Characteristic p of the coefficient field Z/pZ for computing homology.")(
- "min-persistence,m", po::value<Filtration_value>(&min_persistence),
- "Minimal lifetime of homology feature to be recorded. Default is 0. Enter a negative value to see zero length "
- "intervals");
-
- po::positional_options_description pos;
- pos.add("input-file", 1);
-
- po::options_description all;
- all.add(visible).add(hidden);
-
- po::variables_map vm;
- po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm);
- po::notify(vm);
-
- if (vm.count("help") || !vm.count("input-file")) {
- 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 \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";
- std::cout << "b and d are respectively the birth and death of the feature and \n";
- std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;
-
- std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl;
- std::cout << visible << std::endl;
- exit(-1);
- }
-}
diff --git a/src/Alpha_complex/utilities/periodic_alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/periodic_alpha_complex_3d_persistence.cpp
deleted file mode 100644
index a261c5a3..00000000
--- a/src/Alpha_complex/utilities/periodic_alpha_complex_3d_persistence.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/* This file is part of the Gudhi Library. The Gudhi library
- * (Geometric Understanding in Higher Dimensions) is a generic C++
- * library for computational topology.
- *
- * Author(s): Vincent Rouvreau
- * Pawel Dlotko - 2017 - Swansea University, UK
- *
- * Copyright (C) 2014 Inria
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/program_options.hpp>
-#include <boost/variant.hpp>
-
-#include <gudhi/Simplex_tree.h>
-#include <gudhi/Persistent_cohomology.h>
-#include <gudhi/Points_3D_off_io.h>
-
-#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
-#include <CGAL/Periodic_3_Delaunay_triangulation_traits_3.h>
-#include <CGAL/Periodic_3_Delaunay_triangulation_3.h>
-#include <CGAL/Alpha_shape_3.h>
-#include <CGAL/Alpha_shape_cell_base_3.h>
-#include <CGAL/Alpha_shape_vertex_base_3.h>
-#include <CGAL/iterator.h>
-
-#include <fstream>
-#include <cmath>
-#include <string>
-#include <tuple>
-#include <map>
-#include <utility>
-#include <vector>
-#include <cstdlib>
-
-#include "alpha_complex_3d_helper.h"
-
-// Traits
-using K = CGAL::Exact_predicates_inexact_constructions_kernel;
-using PK = CGAL::Periodic_3_Delaunay_triangulation_traits_3<K>;
-// Vertex type
-using DsVb = CGAL::Periodic_3_triangulation_ds_vertex_base_3<>;
-using Vb = CGAL::Triangulation_vertex_base_3<PK, DsVb>;
-using AsVb = CGAL::Alpha_shape_vertex_base_3<PK, Vb>;
-// Cell type
-using DsCb = CGAL::Periodic_3_triangulation_ds_cell_base_3<>;
-using Cb = CGAL::Triangulation_cell_base_3<PK, DsCb>;
-using AsCb = CGAL::Alpha_shape_cell_base_3<PK, Cb>;
-using Tds = CGAL::Triangulation_data_structure_3<AsVb, AsCb>;
-using P3DT3 = CGAL::Periodic_3_Delaunay_triangulation_3<PK, Tds>;
-using Alpha_shape_3 = CGAL::Alpha_shape_3<P3DT3>;
-using Point_3 = PK::Point_3;
-
-// filtration with alpha values needed type definition
-using Alpha_value_type = Alpha_shape_3::FT;
-using Object = CGAL::Object;
-using Dispatch =
- CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<Object, Alpha_value_type>,
- CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<Object> >,
- std::back_insert_iterator<std::vector<Alpha_value_type> > > >;
-using Cell_handle = Alpha_shape_3::Cell_handle;
-using Facet = Alpha_shape_3::Facet;
-using Edge_3 = Alpha_shape_3::Edge;
-using Vertex_handle = Alpha_shape_3::Vertex_handle;
-using Vertex_list = std::vector<Alpha_shape_3::Vertex_handle>;
-
-// gudhi type definition
-using ST = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
-using Filtration_value = ST::Filtration_value;
-using Simplex_tree_vertex = ST::Vertex_handle;
-using Alpha_shape_simplex_tree_map = std::map<Alpha_shape_3::Vertex_handle, Simplex_tree_vertex>;
-using Simplex_tree_vector_vertex = std::vector<Simplex_tree_vertex>;
-using Persistent_cohomology =
- Gudhi::persistent_cohomology::Persistent_cohomology<ST, Gudhi::persistent_cohomology::Field_Zp>;
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &cuboid_file,
- std::string &output_file_diag, int &coeff_field_characteristic, Filtration_value &min_persistence);
-
-int main(int argc, char **argv) {
- std::string off_file_points;
- std::string cuboid_file;
- std::string output_file_diag;
- int coeff_field_characteristic;
- Filtration_value min_persistence;
-
- program_options(argc, argv, off_file_points, cuboid_file, output_file_diag, coeff_field_characteristic,
- min_persistence);
-
- // Read the OFF file (input file name given as parameter) and triangulate points
- Gudhi::Points_3D_off_reader<Point_3> off_reader(off_file_points);
- // Check the read operation was correct
- if (!off_reader.is_valid()) {
- std::cerr << "Unable to read OFF file " << off_file_points << std::endl;
- exit(-1);
- }
-
- // Read iso_cuboid_3 information from file
- std::ifstream iso_cuboid_str(cuboid_file);
- double x_min, y_min, z_min, x_max, y_max, z_max;
- if (iso_cuboid_str.good()) {
- iso_cuboid_str >> x_min >> y_min >> z_min >> x_max >> y_max >> z_max;
- } else {
- std::cerr << "Unable to read file " << cuboid_file << std::endl;
- exit(-1);
- }
- // Checking if the cuboid is the same in x,y and z direction. If not, CGAL will not process it.
- if ((x_max - x_min != y_max - y_min) || (x_max - x_min != z_max - z_min) || (z_max - z_min != y_max - y_min)) {
- std::cerr << "The size of the cuboid in every directions is not the same." << std::endl;
- exit(-1);
- }
-
- // Retrieve the points
- std::vector<Point_3> lp = off_reader.get_point_cloud();
-
- // Define the periodic cube
- P3DT3 pdt(PK::Iso_cuboid_3(x_min, y_min, z_min, x_max, y_max, z_max));
- // Heuristic for inserting large point sets (if pts is reasonably large)
- pdt.insert(lp.begin(), lp.end(), true);
- // As pdt won't be modified anymore switch to 1-sheeted cover if possible
- if (pdt.is_triangulation_in_1_sheet()) {
- pdt.convert_to_1_sheeted_covering();
- } else {
- std::cerr << "ERROR: we were not able to construct a triangulation within a single periodic domain." << std::endl;
- exit(-1);
- }
- std::cout << "Periodic Delaunay computed." << std::endl;
-
- // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode
- // Maybe need to set it to GENERAL mode
- Alpha_shape_3 as(pdt, 0, Alpha_shape_3::GENERAL);
-
- // filtration with alpha values from alpha shape
- std::vector<Object> the_objects;
- std::vector<Alpha_value_type> the_alpha_values;
-
- Dispatch disp = CGAL::dispatch_output<Object, Alpha_value_type>(std::back_inserter(the_objects),
- std::back_inserter(the_alpha_values));
-
- as.filtration_with_alpha_values(disp);
-#ifdef DEBUG_TRACES
- std::cout << "filtration_with_alpha_values returns : " << the_objects.size() << " objects" << std::endl;
-#endif // DEBUG_TRACES
-
- Alpha_shape_3::size_type count_vertices = 0;
- Alpha_shape_3::size_type count_edges = 0;
- Alpha_shape_3::size_type count_facets = 0;
- Alpha_shape_3::size_type count_cells = 0;
-
- // Loop on objects vector
- Vertex_list vertex_list;
- ST simplex_tree;
- Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
- std::vector<Alpha_value_type>::iterator the_alpha_value_iterator = the_alpha_values.begin();
- for (auto object_iterator : the_objects) {
- // Retrieve Alpha shape vertex list from object
- if (const Cell_handle *cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
- vertex_list = from_cell<Vertex_list, Cell_handle>(*cell);
- count_cells++;
- } else if (const Facet *facet = CGAL::object_cast<Facet>(&object_iterator)) {
- vertex_list = from_facet<Vertex_list, Facet>(*facet);
- count_facets++;
- } else if (const Edge_3 *edge = CGAL::object_cast<Edge_3>(&object_iterator)) {
- vertex_list = from_edge<Vertex_list, Edge_3>(*edge);
- count_edges++;
- } else if (const Vertex_handle *vertex = CGAL::object_cast<Vertex_handle>(&object_iterator)) {
- count_vertices++;
- vertex_list = from_vertex<Vertex_list, Vertex_handle>(*vertex);
- }
- // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
- Simplex_tree_vector_vertex the_simplex;
- for (auto the_alpha_shape_vertex : vertex_list) {
- Alpha_shape_simplex_tree_map::iterator the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
- if (the_map_iterator == map_cgal_simplex_tree.end()) {
- // alpha shape not found
- Simplex_tree_vertex vertex = map_cgal_simplex_tree.size();
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
- } else {
- // alpha shape found
- Simplex_tree_vertex vertex = the_map_iterator->second;
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- }
- }
- // Construction of the simplex_tree
- Filtration_value filtr = /*std::sqrt*/ (*the_alpha_value_iterator);
-#ifdef DEBUG_TRACES
- std::cout << "filtration = " << filtr << std::endl;
-#endif // DEBUG_TRACES
- simplex_tree.insert_simplex(the_simplex, filtr);
- if (the_alpha_value_iterator != the_alpha_values.end())
- ++the_alpha_value_iterator;
- else
- std::cout << "This shall not happen" << std::endl;
- }
-
-#ifdef DEBUG_TRACES
- std::cout << "vertices \t\t" << count_vertices << std::endl;
- std::cout << "edges \t\t" << count_edges << std::endl;
- std::cout << "facets \t\t" << count_facets << std::endl;
- std::cout << "cells \t\t" << count_cells << std::endl;
-
- std::cout << "Information of the Simplex Tree: " << std::endl;
- std::cout << " Number of vertices = " << simplex_tree.num_vertices() << " ";
- std::cout << " Number of simplices = " << simplex_tree.num_simplices() << std::endl << std::endl;
- std::cout << " Dimension = " << simplex_tree.dimension() << " ";
-#endif // DEBUG_TRACES
-
-#ifdef DEBUG_TRACES
- std::cout << "Iterator on vertices: " << std::endl;
- for (auto vertex : simplex_tree.complex_vertex_range()) {
- std::cout << vertex << " ";
- }
-#endif // DEBUG_TRACES
-
- // Sort the simplices in the order of the filtration
- simplex_tree.initialize_filtration();
-
- std::cout << "Simplex_tree dim: " << simplex_tree.dimension() << std::endl;
- // Compute the persistence diagram of the complex
- Persistent_cohomology pcoh(simplex_tree, true);
- // initializes the coefficient field for homology
- pcoh.init_coefficients(coeff_field_characteristic);
-
- pcoh.compute_persistent_cohomology(min_persistence);
-
- // Output the diagram in filediag
- if (output_file_diag.empty()) {
- pcoh.output_diagram();
- } else {
- std::cout << "Result in file: " << output_file_diag << std::endl;
- std::ofstream out(output_file_diag);
- pcoh.output_diagram(out);
- out.close();
- }
-
- return 0;
-}
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &cuboid_file,
- std::string &output_file_diag, int &coeff_field_characteristic,
- Filtration_value &min_persistence) {
- namespace po = boost::program_options;
- po::options_description hidden("Hidden options");
- hidden.add_options()("input-file", po::value<std::string>(&off_file_points),
- "Name of file containing a point set. Format is one point per line: X1 ... Xd ")(
- "cuboid-file", po::value<std::string>(&cuboid_file),
- "Name of file describing the periodic domain. Format is: min_hx min_hy min_hz\nmax_hx max_hy max_hz");
-
- po::options_description visible("Allowed options", 100);
- visible.add_options()("help,h", "produce help message")(
- "output-file,o", po::value<std::string>(&output_file_diag)->default_value(std::string()),
- "Name of file in which the persistence diagram is written. Default print in std::cout")(
- "field-charac,p", po::value<int>(&coeff_field_characteristic)->default_value(11),
- "Characteristic p of the coefficient field Z/pZ for computing homology.")(
- "min-persistence,m", po::value<Filtration_value>(&min_persistence),
- "Minimal lifetime of homology feature to be recorded. Default is 0. Enter a negative value to see zero length "
- "intervals");
-
- po::positional_options_description pos;
- pos.add("input-file", 1);
- pos.add("cuboid-file", 2);
-
- po::options_description all;
- all.add(visible).add(hidden);
-
- po::variables_map vm;
- po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm);
- po::notify(vm);
-
- if (vm.count("help") || !vm.count("input-file") || !vm.count("cuboid-file")) {
- std::cout << std::endl;
- std::cout << "Compute the persistent homology with coefficient field Z/pZ \n";
- std::cout << "of a periodic 3D Alpha complex defined on a set of input points.\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";
- std::cout << "b and d are respectively the birth and death of the feature and \n";
- std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;
-
- std::cout << "Usage: " << argv[0] << " [options] input-file cuboid-file" << std::endl << std::endl;
- std::cout << visible << std::endl;
- exit(-1);
- }
-}
diff --git a/src/Alpha_complex/utilities/weighted_alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/weighted_alpha_complex_3d_persistence.cpp
deleted file mode 100644
index aa7ddee2..00000000
--- a/src/Alpha_complex/utilities/weighted_alpha_complex_3d_persistence.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/* This file is part of the Gudhi Library. The Gudhi library
- * (Geometric Understanding in Higher Dimensions) is a generic C++
- * library for computational topology.
- *
- * Author(s): Vincent Rouvreau
- *
- * Copyright (C) 2014 Inria
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/program_options.hpp>
-#include <boost/variant.hpp>
-
-#include <gudhi/Simplex_tree.h>
-#include <gudhi/Persistent_cohomology.h>
-#include <gudhi/Points_3D_off_io.h>
-
-#include <CGAL/config.h>
-#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
-#include <CGAL/Regular_triangulation_3.h>
-#include <CGAL/Alpha_shape_3.h>
-#include <CGAL/Alpha_shape_cell_base_3.h>
-#include <CGAL/Alpha_shape_vertex_base_3.h>
-#include <CGAL/iterator.h>
-
-// For CGAL < 4.11
-#if CGAL_VERSION_NR < 1041100000
-#include <CGAL/Regular_triangulation_euclidean_traits_3.h>
-#endif // CGAL_VERSION_NR < 1041100000
-
-#include <fstream>
-#include <cmath>
-#include <string>
-#include <tuple>
-#include <map>
-#include <utility>
-#include <vector>
-#include <cstdlib>
-
-#include "alpha_complex_3d_helper.h"
-
-// Alpha_shape_3 templates type definitions
-using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
-
-// For CGAL < 4.11
-#if CGAL_VERSION_NR < 1041100000
-using Gt = CGAL::Regular_triangulation_euclidean_traits_3<Kernel>;
-using Vb = CGAL::Alpha_shape_vertex_base_3<Gt>;
-using Fb = CGAL::Alpha_shape_cell_base_3<Gt>;
-using Tds = CGAL::Triangulation_data_structure_3<Vb, Fb>;
-using Triangulation_3 = CGAL::Regular_triangulation_3<Gt, Tds>;
-
-// From file type definition
-using Point_3 = Gt::Bare_point;
-using Weighted_point_3 = Gt::Weighted_point;
-
-// For CGAL >= 4.11
-#else // CGAL_VERSION_NR < 1041100000
-using Rvb = CGAL::Regular_triangulation_vertex_base_3<Kernel>;
-using Vb = CGAL::Alpha_shape_vertex_base_3<Kernel, Rvb>;
-using Rcb = CGAL::Regular_triangulation_cell_base_3<Kernel>;
-using Cb = CGAL::Alpha_shape_cell_base_3<Kernel, Rcb>;
-using Tds = CGAL::Triangulation_data_structure_3<Vb, Cb>;
-using Triangulation_3 = CGAL::Regular_triangulation_3<Kernel, Tds>;
-
-// From file type definition
-using Point_3 = Triangulation_3::Bare_point;
-using Weighted_point_3 = Triangulation_3::Weighted_point;
-#endif // CGAL_VERSION_NR < 1041100000
-
-using Alpha_shape_3 = CGAL::Alpha_shape_3<Triangulation_3>;
-
-// filtration with alpha values needed type definition
-using Alpha_value_type = Alpha_shape_3::FT;
-using Object = CGAL::Object;
-using Dispatch =
- CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<Object, Alpha_value_type>,
- CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<Object> >,
- std::back_insert_iterator<std::vector<Alpha_value_type> > > >;
-using Cell_handle = Alpha_shape_3::Cell_handle;
-using Facet = Alpha_shape_3::Facet;
-using Edge_3 = Alpha_shape_3::Edge;
-using Vertex_handle = Alpha_shape_3::Vertex_handle;
-using Vertex_list = std::vector<Alpha_shape_3::Vertex_handle>;
-
-// gudhi type definition
-using ST = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
-using Filtration_value = ST::Filtration_value;
-using Simplex_tree_vertex = ST::Vertex_handle;
-using Alpha_shape_simplex_tree_map = std::map<Alpha_shape_3::Vertex_handle, Simplex_tree_vertex>;
-using Simplex_tree_vector_vertex = std::vector<Simplex_tree_vertex>;
-using Persistent_cohomology =
- Gudhi::persistent_cohomology::Persistent_cohomology<ST, Gudhi::persistent_cohomology::Field_Zp>;
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &weight_file,
- std::string &output_file_diag, int &coeff_field_characteristic, Filtration_value &min_persistence);
-
-int main(int argc, char **argv) {
- std::string off_file_points;
- std::string weight_file;
- std::string output_file_diag;
- int coeff_field_characteristic;
- Filtration_value min_persistence;
-
- program_options(argc, argv, off_file_points, weight_file, output_file_diag, coeff_field_characteristic,
- min_persistence);
-
- // Read the OFF file (input file name given as parameter) and triangulate points
- Gudhi::Points_3D_off_reader<Point_3> off_reader(off_file_points);
- // Check the read operation was correct
- if (!off_reader.is_valid()) {
- std::cerr << "Unable to read OFF file " << off_file_points << std::endl;
- exit(-1);
- }
-
- // Retrieve the points
- std::vector<Point_3> lp = off_reader.get_point_cloud();
-
- // Read weights information from file
- std::ifstream weights_ifstr(weight_file);
- std::vector<Weighted_point_3> wp;
- if (weights_ifstr.good()) {
- double weight = 0.0;
- std::size_t index = 0;
- wp.reserve(lp.size());
- // Attempt read the weight in a double format, return false if it fails
- while ((weights_ifstr >> weight) && (index < lp.size())) {
- wp.push_back(Weighted_point_3(lp[index], weight));
- index++;
- }
- if (index != lp.size()) {
- std::cerr << "Bad number of weights in file " << weight_file << std::endl;
- exit(-1);
- }
- } else {
- std::cerr << "Unable to read weights file " << weight_file << std::endl;
- exit(-1);
- }
-
- // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode.
- Alpha_shape_3 as(wp.begin(), wp.end(), 0, Alpha_shape_3::GENERAL);
-#ifdef DEBUG_TRACES
- std::cout << "Alpha shape computed in GENERAL mode" << std::endl;
-#endif // DEBUG_TRACES
-
- // filtration with alpha values from alpha shape
- std::vector<Object> the_objects;
- std::vector<Alpha_value_type> the_alpha_values;
-
- Dispatch disp = CGAL::dispatch_output<Object, Alpha_value_type>(std::back_inserter(the_objects),
- std::back_inserter(the_alpha_values));
-
- as.filtration_with_alpha_values(disp);
-#ifdef DEBUG_TRACES
- std::cout << "filtration_with_alpha_values returns : " << the_objects.size() << " objects" << std::endl;
-#endif // DEBUG_TRACES
-
- Alpha_shape_3::size_type count_vertices = 0;
- Alpha_shape_3::size_type count_edges = 0;
- Alpha_shape_3::size_type count_facets = 0;
- Alpha_shape_3::size_type count_cells = 0;
-
- // Loop on objects vector
- Vertex_list vertex_list;
- ST simplex_tree;
- Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
- std::vector<Alpha_value_type>::iterator the_alpha_value_iterator = the_alpha_values.begin();
- for (auto object_iterator : the_objects) {
- // Retrieve Alpha shape vertex list from object
- if (const Cell_handle *cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
- vertex_list = from_cell<Vertex_list, Cell_handle>(*cell);
- count_cells++;
- } else if (const Facet *facet = CGAL::object_cast<Facet>(&object_iterator)) {
- vertex_list = from_facet<Vertex_list, Facet>(*facet);
- count_facets++;
- } else if (const Edge_3 *edge = CGAL::object_cast<Edge_3>(&object_iterator)) {
- vertex_list = from_edge<Vertex_list, Edge_3>(*edge);
- count_edges++;
- } else if (const Vertex_handle *vertex = CGAL::object_cast<Vertex_handle>(&object_iterator)) {
- count_vertices++;
- vertex_list = from_vertex<Vertex_list, Vertex_handle>(*vertex);
- }
- // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
- Simplex_tree_vector_vertex the_simplex;
- for (auto the_alpha_shape_vertex : vertex_list) {
- Alpha_shape_simplex_tree_map::iterator the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
- if (the_map_iterator == map_cgal_simplex_tree.end()) {
- // alpha shape not found
- Simplex_tree_vertex vertex = map_cgal_simplex_tree.size();
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
- } else {
- // alpha shape found
- Simplex_tree_vertex vertex = the_map_iterator->second;
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- }
- }
- // Construction of the simplex_tree
- Filtration_value filtr = /*std::sqrt*/ (*the_alpha_value_iterator);
-#ifdef DEBUG_TRACES
- std::cout << "filtration = " << filtr << std::endl;
-#endif // DEBUG_TRACES
- simplex_tree.insert_simplex(the_simplex, filtr);
- if (the_alpha_value_iterator != the_alpha_values.end())
- ++the_alpha_value_iterator;
- else
- std::cout << "This shall not happen" << std::endl;
- }
-
-#ifdef DEBUG_TRACES
- std::cout << "vertices \t\t" << count_vertices << std::endl;
- std::cout << "edges \t\t" << count_edges << std::endl;
- std::cout << "facets \t\t" << count_facets << std::endl;
- std::cout << "cells \t\t" << count_cells << std::endl;
-
- std::cout << "Information of the Simplex Tree: " << std::endl;
- std::cout << " Number of vertices = " << simplex_tree.num_vertices() << " ";
- std::cout << " Number of simplices = " << simplex_tree.num_simplices() << std::endl << std::endl;
- std::cout << " Dimension = " << simplex_tree.dimension() << " ";
-#endif // DEBUG_TRACES
-
-#ifdef DEBUG_TRACES
- std::cout << "Iterator on vertices: " << std::endl;
- for (auto vertex : simplex_tree.complex_vertex_range()) {
- std::cout << vertex << " ";
- }
-#endif // DEBUG_TRACES
-
- // Sort the simplices in the order of the filtration
- simplex_tree.initialize_filtration();
-
- std::cout << "Simplex_tree dim: " << simplex_tree.dimension() << std::endl;
- // Compute the persistence diagram of the complex
- Persistent_cohomology pcoh(simplex_tree, true);
- // initializes the coefficient field for homology
- pcoh.init_coefficients(coeff_field_characteristic);
-
- pcoh.compute_persistent_cohomology(min_persistence);
-
- // Output the diagram in filediag
- if (output_file_diag.empty()) {
- pcoh.output_diagram();
- } else {
- std::cout << "Result in file: " << output_file_diag << std::endl;
- std::ofstream out(output_file_diag);
- pcoh.output_diagram(out);
- out.close();
- }
-
- return 0;
-}
-
-void program_options(int argc, char *argv[], std::string &off_file_points, std::string &weight_file,
- std::string &output_file_diag, int &coeff_field_characteristic,
- Filtration_value &min_persistence) {
- namespace po = boost::program_options;
- po::options_description hidden("Hidden options");
- hidden.add_options()("input-file", po::value<std::string>(&off_file_points),
- "Name of file containing a point set. Format is one point per line: X1 ... Xd ")(
- "weight-file", po::value<std::string>(&weight_file),
- "Name of file containing a point weights. Format is one weigt per line: W1\n...\nWn ");
-
- po::options_description visible("Allowed options", 100);
- visible.add_options()("help,h", "produce help message")(
- "output-file,o", po::value<std::string>(&output_file_diag)->default_value(std::string()),
- "Name of file in which the persistence diagram is written. Default print in std::cout")(
- "field-charac,p", po::value<int>(&coeff_field_characteristic)->default_value(11),
- "Characteristic p of the coefficient field Z/pZ for computing homology.")(
- "min-persistence,m", po::value<Filtration_value>(&min_persistence),
- "Minimal lifetime of homology feature to be recorded. Default is 0. Enter a negative value to see zero length "
- "intervals");
-
- po::positional_options_description pos;
- pos.add("input-file", 1);
- pos.add("weight-file", 2);
-
- po::options_description all;
- all.add(visible).add(hidden);
-
- po::variables_map vm;
- po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm);
- po::notify(vm);
-
- if (vm.count("help") || !vm.count("input-file") || !vm.count("weight-file")) {
- std::cout << std::endl;
- std::cout << "Compute the persistent homology with coefficient field Z/pZ \n";
- std::cout << "of a weighted 3D Alpha complex defined on a set of input points.\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";
- std::cout << "b and d are respectively the birth and death of the feature and \n";
- std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;
-
- std::cout << "Usage: " << argv[0] << " [options] input-file weight-file" << std::endl << std::endl;
- std::cout << visible << std::endl;
- exit(-1);
- }
-}
diff --git a/src/Alpha_complex/utilities/weighted_periodic_alpha_complex_3d_persistence.cpp b/src/Alpha_complex/utilities/weighted_periodic_alpha_complex_3d_persistence.cpp
deleted file mode 100644
index d030c88c..00000000
--- a/src/Alpha_complex/utilities/weighted_periodic_alpha_complex_3d_persistence.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-/* This file is part of the Gudhi Library. The Gudhi library
- * (Geometric Understanding in Higher Dimensions) is a generic C++
- * library for computational topology.
- *
- * Author(s): Vincent Rouvreau
- * Pawel Dlotko - 2017 - Swansea University, UK
- *
- * Copyright (C) 2014 Inria
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/variant.hpp>
-
-#include <gudhi/Simplex_tree.h>
-#include <gudhi/Persistent_cohomology.h>
-#include <gudhi/Points_3D_off_io.h>
-
-#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
-#include <CGAL/Periodic_3_regular_triangulation_traits_3.h>
-#include <CGAL/Periodic_3_regular_triangulation_3.h>
-#include <CGAL/Alpha_shape_3.h>
-#include <CGAL/Alpha_shape_cell_base_3.h>
-#include <CGAL/Alpha_shape_vertex_base_3.h>
-#include <CGAL/iterator.h>
-
-#include <fstream>
-#include <cmath>
-#include <string>
-#include <tuple>
-#include <map>
-#include <utility>
-#include <vector>
-#include <cstdlib>
-
-#include "alpha_complex_3d_helper.h"
-
-// Traits
-using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
-using PK = CGAL::Periodic_3_regular_triangulation_traits_3<Kernel>;
-
-// Vertex type
-using DsVb = CGAL::Periodic_3_triangulation_ds_vertex_base_3<>;
-using Vb = CGAL::Regular_triangulation_vertex_base_3<PK, DsVb>;
-using AsVb = CGAL::Alpha_shape_vertex_base_3<PK, Vb>;
-// Cell type
-using DsCb = CGAL::Periodic_3_triangulation_ds_cell_base_3<>;
-using Cb = CGAL::Regular_triangulation_cell_base_3<PK, DsCb>;
-using AsCb = CGAL::Alpha_shape_cell_base_3<PK, Cb>;
-using Tds = CGAL::Triangulation_data_structure_3<AsVb, AsCb>;
-using P3RT3 = CGAL::Periodic_3_regular_triangulation_3<PK, Tds>;
-using Alpha_shape_3 = CGAL::Alpha_shape_3<P3RT3>;
-
-using Point_3 = P3RT3::Bare_point;
-using Weighted_point_3 = P3RT3::Weighted_point;
-
-// filtration with alpha values needed type definition
-using Alpha_value_type = Alpha_shape_3::FT;
-using Object = CGAL::Object;
-using Dispatch =
- CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<Object, Alpha_value_type>,
- CGAL::cpp11::tuple<std::back_insert_iterator<std::vector<Object> >,
- std::back_insert_iterator<std::vector<Alpha_value_type> > > >;
-using Cell_handle = Alpha_shape_3::Cell_handle;
-using Facet = Alpha_shape_3::Facet;
-using Edge_3 = Alpha_shape_3::Edge;
-using Vertex_handle = Alpha_shape_3::Vertex_handle;
-using Vertex_list = std::vector<Alpha_shape_3::Vertex_handle>;
-
-// gudhi type definition
-using ST = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
-using Filtration_value = ST::Filtration_value;
-using Simplex_tree_vertex = ST::Vertex_handle;
-using Alpha_shape_simplex_tree_map = std::map<Alpha_shape_3::Vertex_handle, Simplex_tree_vertex>;
-using Simplex_tree_vector_vertex = std::vector<Simplex_tree_vertex>;
-using Persistent_cohomology =
- Gudhi::persistent_cohomology::Persistent_cohomology<ST, Gudhi::persistent_cohomology::Field_Zp>;
-
-void usage(const std::string& progName) {
- std::cerr << "Usage: " << progName << " path_to_the_OFF_file path_to_weight_file path_to_the_cuboid_file "
- "coeff_field_characteristic[integer > 0] min_persistence[float >= -1.0]\n";
- exit(-1);
-}
-
-int main(int argc, char* const argv[]) {
- // program args management
- if (argc != 6) {
- std::cerr << "Error: Number of arguments (" << argc << ") is not correct\n";
- usage(argv[0]);
- }
-
- int coeff_field_characteristic = atoi(argv[4]);
- Filtration_value min_persistence = strtof(argv[5], nullptr);
-
- // Read points from file
- std::string offInputFile(argv[1]);
- // Read the OFF file (input file name given as parameter) and triangulate points
- Gudhi::Points_3D_off_reader<Point_3> off_reader(offInputFile);
- // Check the read operation was correct
- if (!off_reader.is_valid()) {
- std::cerr << "Unable to read file " << offInputFile << std::endl;
- usage(argv[0]);
- }
-
- // Retrieve the points
- std::vector<Point_3> lp = off_reader.get_point_cloud();
-
- // Read iso_cuboid_3 information from file
- std::ifstream iso_cuboid_str(argv[3]);
- double x_min, y_min, z_min, x_max, y_max, z_max;
- if (iso_cuboid_str.is_open()) {
- if (!(iso_cuboid_str >> x_min >> y_min >> z_min >> x_max >> y_max >> z_max)) {
- std::cerr << argv[3] << " - Bad file format." << std::endl;
- usage(argv[0]);
- }
-
- } else {
- std::cerr << "Unable to read file " << argv[3] << std::endl;
- usage(argv[0]);
- }
- // Checking if the cuboid is the same in x,y and z direction. If not, CGAL will not process it.
- if ((x_max - x_min != y_max - y_min) || (x_max - x_min != z_max - z_min) || (z_max - z_min != y_max - y_min)) {
- std::cerr << "The size of the cuboid in every directions is not the same." << std::endl;
- exit(-1);
- }
-
- double maximal_possible_weight = 0.015625 * (x_max - x_min) * (x_max - x_min);
-
- // Read weights information from file
- std::ifstream weights_ifstr(argv[2]);
- std::vector<Weighted_point_3> wp;
- if (weights_ifstr.is_open()) {
- double weight = 0.0;
- std::size_t index = 0;
- wp.reserve(lp.size());
- // Attempt read the weight in a double format, return false if it fails
- while ((weights_ifstr >> weight) && (index < lp.size())) {
- if ((weight >= maximal_possible_weight) || (weight < 0)) {
- std::cerr << "At line " << (index + 1) << ", the weight (" << weight
- << ") is negative or more than or equal to maximal possible weight (" << maximal_possible_weight
- << ") = 1/64*cuboid length squared, which is not an acceptable input." << std::endl;
- exit(-1);
- }
-
- wp.push_back(Weighted_point_3(lp[index], weight));
- index++;
- }
- if (index != lp.size()) {
- std::cerr << "Bad number of weights in file " << argv[2] << std::endl;
- usage(argv[0]);
- }
- } else {
- std::cerr << "Unable to read file " << argv[2] << std::endl;
- usage(argv[0]);
- }
-
- // Define the periodic cube
- P3RT3 prt(PK::Iso_cuboid_3(x_min, y_min, z_min, x_max, y_max, z_max));
- // Heuristic for inserting large point sets (if pts is reasonably large)
- prt.insert(wp.begin(), wp.end(), true);
- // As prt won't be modified anymore switch to 1-sheeted cover if possible
- if (prt.is_triangulation_in_1_sheet()) {
- prt.convert_to_1_sheeted_covering();
- } else {
- std::cerr << "ERROR: we were not able to construct a triangulation within a single periodic domain." << std::endl;
- exit(-1);
- }
- std::cout << "Weighted Periodic Delaunay computed." << std::endl;
-
- // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode
- // Maybe need to set it to GENERAL mode
- Alpha_shape_3 as(prt, 0, Alpha_shape_3::GENERAL);
-
- // filtration with alpha values from alpha shape
- std::vector<Object> the_objects;
- std::vector<Alpha_value_type> the_alpha_values;
-
- Dispatch disp = CGAL::dispatch_output<Object, Alpha_value_type>(std::back_inserter(the_objects),
- std::back_inserter(the_alpha_values));
-
- as.filtration_with_alpha_values(disp);
-#ifdef DEBUG_TRACES
- std::cout << "filtration_with_alpha_values returns : " << the_objects.size() << " objects" << std::endl;
-#endif // DEBUG_TRACES
-
- Alpha_shape_3::size_type count_vertices = 0;
- Alpha_shape_3::size_type count_edges = 0;
- Alpha_shape_3::size_type count_facets = 0;
- Alpha_shape_3::size_type count_cells = 0;
-
- // Loop on objects vector
- Vertex_list vertex_list;
- ST simplex_tree;
- Alpha_shape_simplex_tree_map map_cgal_simplex_tree;
- std::vector<Alpha_value_type>::iterator the_alpha_value_iterator = the_alpha_values.begin();
- for (auto object_iterator : the_objects) {
- // Retrieve Alpha shape vertex list from object
- if (const Cell_handle* cell = CGAL::object_cast<Cell_handle>(&object_iterator)) {
- vertex_list = from_cell<Vertex_list, Cell_handle>(*cell);
- count_cells++;
- } else if (const Facet* facet = CGAL::object_cast<Facet>(&object_iterator)) {
- vertex_list = from_facet<Vertex_list, Facet>(*facet);
- count_facets++;
- } else if (const Edge_3* edge = CGAL::object_cast<Edge_3>(&object_iterator)) {
- vertex_list = from_edge<Vertex_list, Edge_3>(*edge);
- count_edges++;
- } else if (const Vertex_handle* vertex = CGAL::object_cast<Vertex_handle>(&object_iterator)) {
- count_vertices++;
- vertex_list = from_vertex<Vertex_list, Vertex_handle>(*vertex);
- }
- // Construction of the vector of simplex_tree vertex from list of alpha_shapes vertex
- Simplex_tree_vector_vertex the_simplex;
- for (auto the_alpha_shape_vertex : vertex_list) {
- Alpha_shape_simplex_tree_map::iterator the_map_iterator = map_cgal_simplex_tree.find(the_alpha_shape_vertex);
- if (the_map_iterator == map_cgal_simplex_tree.end()) {
- // alpha shape not found
- Simplex_tree_vertex vertex = map_cgal_simplex_tree.size();
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex);
- } else {
- // alpha shape found
- Simplex_tree_vertex vertex = the_map_iterator->second;
-#ifdef DEBUG_TRACES
- std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl;
-#endif // DEBUG_TRACES
- the_simplex.push_back(vertex);
- }
- }
- // Construction of the simplex_tree
- Filtration_value filtr = /*std::sqrt*/ (*the_alpha_value_iterator);
-#ifdef DEBUG_TRACES
- std::cout << "filtration = " << filtr << std::endl;
-#endif // DEBUG_TRACES
- simplex_tree.insert_simplex(the_simplex, filtr);
- if (the_alpha_value_iterator != the_alpha_values.end())
- ++the_alpha_value_iterator;
- else
- std::cout << "This shall not happen" << std::endl;
- }
-
-#ifdef DEBUG_TRACES
- std::cout << "vertices \t\t" << count_vertices << std::endl;
- std::cout << "edges \t\t" << count_edges << std::endl;
- std::cout << "facets \t\t" << count_facets << std::endl;
- std::cout << "cells \t\t" << count_cells << std::endl;
-
- std::cout << "Information of the Simplex Tree: " << std::endl;
- std::cout << " Number of vertices = " << simplex_tree.num_vertices() << " ";
- std::cout << " Number of simplices = " << simplex_tree.num_simplices() << std::endl << std::endl;
- std::cout << " Dimension = " << simplex_tree.dimension() << " ";
-#endif // DEBUG_TRACES
-
-#ifdef DEBUG_TRACES
- std::cout << "Iterator on vertices: " << std::endl;
- for (auto vertex : simplex_tree.complex_vertex_range()) {
- std::cout << vertex << " ";
- }
-#endif // DEBUG_TRACES
-
- // Sort the simplices in the order of the filtration
- simplex_tree.initialize_filtration();
-
- std::cout << "Simplex_tree dim: " << simplex_tree.dimension() << std::endl;
- // Compute the persistence diagram of the complex
- Persistent_cohomology pcoh(simplex_tree, true);
- // initializes the coefficient field for homology
- pcoh.init_coefficients(coeff_field_characteristic);
-
- pcoh.compute_persistent_cohomology(min_persistence);
-
- pcoh.output_diagram();
-
- return 0;
-}
diff --git a/src/Bitmap_cubical_complex/doc/Gudhi_Cubical_Complex_doc.h b/src/Bitmap_cubical_complex/doc/Gudhi_Cubical_Complex_doc.h
index d1836ef0..5fa02a5e 100644
--- a/src/Bitmap_cubical_complex/doc/Gudhi_Cubical_Complex_doc.h
+++ b/src/Bitmap_cubical_complex/doc/Gudhi_Cubical_Complex_doc.h
@@ -87,11 +87,16 @@ namespace cubical_complex {
*
* \section inputformat Input Format
*
- * In the current implantation, filtration is given at the maximal cubes, and it is then extended by the lower star
+ * In the current implementation, filtration is given at the maximal cubes, and it is then extended by the lower star
* filtration to all cubes. There are a number of constructors that can be used to construct cubical complex by users
* who want to use the code directly. They can be found in the \a Bitmap_cubical_complex class.
- * Currently one input from a text file is used. It uses a format used already in Perseus software
- * (http://www.sas.upenn.edu/~vnanda/perseus/) by Vidit Nanda. The file format is described here: \ref FileFormatsPerseus.
+ * Currently one input from a text file is used. It uses a format inspired from the Perseus software
+ * (http://www.sas.upenn.edu/~vnanda/perseus/) by Vidit Nanda.
+ * \note While Perseus assume the filtration of all maximal cubes to be non-negative, over here we do not enforce this
+ * and we allow any filtration values. As a consequence one cannot use `-1`'s to indicate missing cubes. If you have
+ * missing cubes in your complex, please set their filtration to \f$+\infty\f$ (aka. `inf` in the file).
+ *
+ * The file format is described in details in \ref FileFormatsPerseus file format section.
*
* \section PeriodicBoundaryConditions Periodic boundary conditions
* Often one would like to impose periodic boundary conditions to the cubical complex. Let \f$ I_1\times ... \times
diff --git a/src/Bitmap_cubical_complex/include/gudhi/Bitmap_cubical_complex_base.h b/src/Bitmap_cubical_complex/include/gudhi/Bitmap_cubical_complex_base.h
index 9b74e267..f5e005b2 100644
--- a/src/Bitmap_cubical_complex/include/gudhi/Bitmap_cubical_complex_base.h
+++ b/src/Bitmap_cubical_complex/include/gudhi/Bitmap_cubical_complex_base.h
@@ -650,18 +650,33 @@ void Bitmap_cubical_complex_base<T>::read_perseus_style_file(const char* perseus
Bitmap_cubical_complex_base<T>::Top_dimensional_cells_iterator it(*this);
it = this->top_dimensional_cells_iterator_begin();
- T filtrationLevel;
- for (std::size_t i = 0; i < dimensions; ++i) {
- if (!(inFiltration >> filtrationLevel) || (inFiltration.eof())) {
- throw std::ios_base::failure("Bad Perseus file format.");
- }
- if (dbg) {
- std::cerr << "Cell of an index : " << it.compute_index_in_bitmap()
- << " and dimension: " << this->get_dimension_of_a_cell(it.compute_index_in_bitmap())
- << " get the value : " << filtrationLevel << std::endl;
+ T filtrationLevel = 0.;
+ std::size_t filtration_counter = 0;
+ while (!inFiltration.eof()) {
+ std::string line;
+ getline(inFiltration, line);
+ if (line.length() != 0) {
+ int n = sscanf(line.c_str(), "%lf", &filtrationLevel);
+ if (n != 1) {
+ std::string perseus_error("Bad Perseus file format. This line is incorrect : " + line);
+ throw std::ios_base::failure(perseus_error.c_str());
+ }
+
+ if (dbg) {
+ std::cerr << "Cell of an index : " << it.compute_index_in_bitmap()
+ << " and dimension: " << this->get_dimension_of_a_cell(it.compute_index_in_bitmap())
+ << " get the value : " << filtrationLevel << std::endl;
+ }
+ this->get_cell_data(*it) = filtrationLevel;
+ ++it;
+ ++filtration_counter;
}
- this->get_cell_data(*it) = filtrationLevel;
- ++it;
+ }
+
+ if (filtration_counter != dimensions) {
+ std::string perseus_error("Bad Perseus file format. Read " + std::to_string(filtration_counter) + " expected " + \
+ std::to_string(dimensions) + " values");
+ throw std::ios_base::failure(perseus_error.c_str());
}
inFiltration.close();
diff --git a/src/Bitmap_cubical_complex/test/Bitmap_test.cpp b/src/Bitmap_cubical_complex/test/Bitmap_test.cpp
index ca7bd986..6a917c25 100644
--- a/src/Bitmap_cubical_complex/test/Bitmap_test.cpp
+++ b/src/Bitmap_cubical_complex/test/Bitmap_test.cpp
@@ -32,6 +32,7 @@
#include <iostream>
#include <sstream>
#include <vector>
+#include <limits>
typedef Gudhi::cubical_complex::Bitmap_cubical_complex_base<double> Bitmap_cubical_complex_base;
typedef Gudhi::cubical_complex::Bitmap_cubical_complex<Bitmap_cubical_complex_base> Bitmap_cubical_complex;
@@ -1576,3 +1577,17 @@ BOOST_AUTO_TEST_CASE(compute_incidence_between_cells_test_periodic_boundary_cond
}
}
}
+
+BOOST_AUTO_TEST_CASE(perseus_file_read) {
+ Bitmap_cubical_complex increasing("sinusoid.txt");
+
+ auto it = increasing.top_dimensional_cells_iterator_begin();
+ double value = increasing.get_cell_data(*it);
+ std::cout << "First value of sinusoid.txt is " << value << std::endl;
+ BOOST_CHECK(value == 10.);
+ // Next value
+ ++it;
+ value = increasing.get_cell_data(*it);
+ std::cout << "Second value of sinusoid.txt is " << value << std::endl;
+ BOOST_CHECK(value == std::numeric_limits<double>::infinity());
+}
diff --git a/src/Bitmap_cubical_complex/test/CMakeLists.txt b/src/Bitmap_cubical_complex/test/CMakeLists.txt
index 8b43632a..d2f002a6 100644
--- a/src/Bitmap_cubical_complex/test/CMakeLists.txt
+++ b/src/Bitmap_cubical_complex/test/CMakeLists.txt
@@ -2,6 +2,9 @@ project(Bitmap_cubical_complex_tests)
include(GUDHI_test_coverage)
+# Do not forget to copy test files in current binary dir
+file(COPY "${CMAKE_SOURCE_DIR}/data/bitmap/sinusoid.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
+
add_executable ( Bitmap_cubical_complex_test_unit Bitmap_test.cpp )
target_link_libraries(Bitmap_cubical_complex_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
if (TBB_FOUND)
diff --git a/src/Bottleneck_distance/doc/Intro_bottleneck_distance.h b/src/Bottleneck_distance/doc/Intro_bottleneck_distance.h
index f8fce96c..6fd058a8 100644
--- a/src/Bottleneck_distance/doc/Intro_bottleneck_distance.h
+++ b/src/Bottleneck_distance/doc/Intro_bottleneck_distance.h
@@ -41,6 +41,9 @@ namespace persistence_diagram {
*
* \image html perturb_pd.png On this picture, the red edges represent the matching. The bottleneck distance is the length of the longest edge.
*
+ * This implementation is based on ideas from "Geometry Helps in Bottleneck Matching and Related Problems"
+ * \cite DBLP:journals/algorithmica/EfratIK01. Another relevant publication, although it was not used is
+ * "Geometry Helps to Compare Persistence Diagrams" \cite Kerber:2017:GHC:3047249.3064175.
*/
/** @} */ // end defgroup bottleneck_distance
diff --git a/src/Bottleneck_distance/include/gudhi/Neighbors_finder.h b/src/Bottleneck_distance/include/gudhi/Neighbors_finder.h
index 36a63ea0..8c12d353 100644
--- a/src/Bottleneck_distance/include/gudhi/Neighbors_finder.h
+++ b/src/Bottleneck_distance/include/gudhi/Neighbors_finder.h
@@ -33,6 +33,7 @@
#include <unordered_set>
#include <vector>
#include <algorithm> // for std::max
+#include <cmath> // for std::abs
namespace Gudhi {
@@ -45,7 +46,7 @@ struct Square_query {
typedef Internal_point Point_d;
typedef double FT;
bool contains(Point_d p) const {
- return std::max(std::abs(p.x()-c.x()), std::abs(p.y()-c.y())) <= size;
+ return (std::max)(std::abs(p.x()-c.x()), std::abs(p.y()-c.y())) <= size;
}
bool inner_range_intersects(CGAL::Kd_tree_rectangle<FT, D> const&r) const {
return
diff --git a/src/Bottleneck_distance/utilities/bottleneckdistance.md b/src/Bottleneck_distance/utilities/bottleneckdistance.md
index 939eb911..a81426cf 100644
--- a/src/Bottleneck_distance/utilities/bottleneckdistance.md
+++ b/src/Bottleneck_distance/utilities/bottleneckdistance.md
@@ -22,5 +22,5 @@ This program computes the Bottleneck distance between two persistence diagram fi
where
-* `<file_1.pers>` and `<file_2.pers>` must be in the format described [here](/doc/latest/fileformats.html#FileFormatsPers).
+* `<file_1.pers>` and `<file_2.pers>` must be in the format described [here]({{ site.officialurl }}/doc/latest/fileformats.html#FileFormatsPers).
* `<tolerance>` is an error bound on the bottleneck distance (set by default to the smallest positive double value).
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6c446104..40fdcf2b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,6 +6,9 @@ include(CMakeGUDHIVersion.txt)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
+set(GUDHI_MODULES "" CACHE INTERNAL "GUDHI_MODULES")
+set(GUDHI_MISSING_MODULES "" CACHE INTERNAL "GUDHI_MISSING_MODULES")
+
# This variable is used by Cython CMakeLists.txt and by GUDHI_third_party_libraries to know its path
set(GUDHI_CYTHON_PATH "cython")
@@ -32,11 +35,10 @@ add_gudhi_module(Skeleton_blocker)
add_gudhi_module(Spatial_searching)
add_gudhi_module(Subsampling)
add_gudhi_module(Tangential_complex)
+add_gudhi_module(Toplex_map)
add_gudhi_module(Witness_complex)
add_gudhi_module(Nerve_GIC)
-message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"")
-
# For "make doxygen" - Requires GUDHI_USER_VERSION_DIR to be set
set(GUDHI_USER_VERSION_DIR ${CMAKE_SOURCE_DIR})
include(GUDHI_doxygen_target)
@@ -60,7 +62,14 @@ add_subdirectory(GudhUI)
if (WITH_GUDHI_PYTHON)
# specific for cython module
add_subdirectory(${GUDHI_CYTHON_PATH})
+else()
+ message("++ Python module will not be compiled because WITH_GUDHI_PYTHON is set to OFF")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "python")
endif()
+
+message("++ GUDHI_MODULES list is:\"${GUDHI_MODULES}\"")
+message("++ GUDHI_MISSING_MODULES list is:\"${GUDHI_MISSING_MODULES}\"")
+
#---------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------
diff --git a/src/Doxyfile.in b/src/Doxyfile.in
index 858a9299..1c293d1c 100644
--- a/src/Doxyfile.in
+++ b/src/Doxyfile.in
@@ -785,7 +785,8 @@ EXCLUDE = data/ \
GudhUI/ \
cmake/ \
src/cython/ \
- include/gudhi_patches/
+ include/gudhi_patches/ \
+ README.md
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
diff --git a/src/GudhUI/CMakeLists.txt b/src/GudhUI/CMakeLists.txt
index b357b8f7..0945e758 100644
--- a/src/GudhUI/CMakeLists.txt
+++ b/src/GudhUI/CMakeLists.txt
@@ -1,40 +1,55 @@
project(GudhUI)
# Need to find OpenGL first as find_package(Qt5) tries to #include"GL/gl.h" on some platforms
-find_package(OpenGL)
+find_package(OpenGL QUIET)
if (OPENGL_FOUND)
- find_package(Qt5 COMPONENTS Widgets Xml OpenGL)
- find_package(QGLViewer)
-
- if ( CGAL_FOUND AND Qt5_FOUND AND QGLVIEWER_FOUND AND NOT CGAL_VERSION VERSION_EQUAL 4.8.0)
-
- set(CMAKE_AUTOMOC ON)
- set(CMAKE_AUTOUIC ON)
- set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
- SET(Boost_USE_STATIC_LIBS ON)
- SET(Boost_USE_MULTITHREAD OFF)
- include_directories (${QGLVIEWER_INCLUDE_DIR})
-
- add_executable ( GudhUI
- gui/gudhui.cpp
- gui/MainWindow.cpp
- gui/Menu_k_nearest_neighbors.cpp
- gui/Menu_uniform_neighbors.cpp
- gui/Menu_edge_contraction.cpp
- gui/Menu_persistence.cpp
- view/Viewer_instructor.cpp
- view/Viewer.cpp
- )
- target_link_libraries( GudhUI Qt5::Widgets Qt5::Xml Qt5::OpenGL )
- target_link_libraries( GudhUI ${QGLVIEWER_LIBRARIES} )
- target_link_libraries( GudhUI ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} )
- if (TBB_FOUND)
- target_link_libraries( GudhUI ${TBB_LIBRARIES})
+ find_package(Qt5 COMPONENTS Widgets Xml OpenGL QUIET)
+ if (Qt5_FOUND)
+ find_package(QGLViewer QUIET)
+ if ( QGLVIEWER_FOUND)
+
+ if ( CGAL_FOUND AND NOT CGAL_VERSION VERSION_EQUAL 4.8.0)
+ set(CMAKE_AUTOMOC ON)
+ set(CMAKE_AUTOUIC ON)
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+ SET(Boost_USE_STATIC_LIBS ON)
+ SET(Boost_USE_MULTITHREAD OFF)
+ include_directories (${QGLVIEWER_INCLUDE_DIR})
+
+ add_executable ( GudhUI
+ gui/gudhui.cpp
+ gui/MainWindow.cpp
+ gui/Menu_k_nearest_neighbors.cpp
+ gui/Menu_uniform_neighbors.cpp
+ gui/Menu_edge_contraction.cpp
+ gui/Menu_persistence.cpp
+ view/Viewer_instructor.cpp
+ view/Viewer.cpp
+ )
+ target_link_libraries( GudhUI Qt5::Widgets Qt5::Xml Qt5::OpenGL )
+ target_link_libraries( GudhUI ${QGLVIEWER_LIBRARIES} )
+ target_link_libraries( GudhUI ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} )
+ if (TBB_FOUND)
+ target_link_libraries( GudhUI ${TBB_LIBRARIES})
+ endif()
+
+ install(TARGETS GudhUI DESTINATION bin)
+ set(GUDHI_MODULES ${GUDHI_MODULES} "GudhUI" CACHE INTERNAL "GUDHI_MODULES")
+ else()
+ message("++ GudhUI will not be compiled because CGAL < 4.8.0 or not found")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "GudhUI" CACHE INTERNAL "GUDHI_MISSING_MODULES")
+ endif()
+ else()
+ message("++ GudhUI will not be compiled because QGLViewer is not found")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "GudhUI" CACHE INTERNAL "GUDHI_MISSING_MODULES")
endif()
-
- install(TARGETS GudhUI DESTINATION bin)
-
+ else()
+ message("++ GudhUI will not be compiled because Qt5 COMPONENTS Widgets Xml OpenGL are not found")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "GudhUI" CACHE INTERNAL "GUDHI_MISSING_MODULES")
endif()
-endif(OPENGL_FOUND)
+else()
+ message("++ GudhUI will not be compiled because OpenGL is not found")
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "GudhUI" CACHE INTERNAL "GUDHI_MISSING_MODULES")
+endif()
diff --git a/src/Nerve_GIC/include/gudhi/GIC.h b/src/Nerve_GIC/include/gudhi/GIC.h
index fea0b861..11bb4e85 100644
--- a/src/Nerve_GIC/include/gudhi/GIC.h
+++ b/src/Nerve_GIC/include/gudhi/GIC.h
@@ -52,7 +52,7 @@
#include <string>
#include <limits> // for numeric_limits
#include <utility> // for std::pair<>
-#include <algorithm> // for std::max
+#include <algorithm> // for (std::max)
#include <random>
#include <cassert>
#include <cmath>
@@ -226,7 +226,24 @@ class Cover_complex {
void set_mask(int nodemask) { mask = nodemask; }
public:
- /** \brief Reads and stores the input point cloud.
+
+
+ /** \brief Reads and stores the input point cloud from vector stored in memory.
+ *
+ * @param[in] point_cloud input vector representing the point cloud. Each row is a point and each coordinate is a vector.
+ *
+ */
+ void set_point_cloud_from_range(const std::vector<std::vector<double> > & point_cloud) {
+ n = point_cloud.size(); data_dimension = point_cloud[0].size();
+ point_cloud_name = "matrix"; cover.resize(n);
+ for(int i = 0; i < n; i++){
+ boost::add_vertex(one_skeleton_OFF);
+ vertices.push_back(boost::add_vertex(one_skeleton));
+ }
+ this->point_cloud = point_cloud;
+ }
+
+ /** \brief Reads and stores the input point cloud from .(n)OFF file.
*
* @param[in] off_file_name name of the input .OFF or .nOFF file.
*
@@ -371,6 +388,22 @@ class Cover_complex {
distances[index[boost::source(*ei, one_skeleton)]][index[boost::target(*ei, one_skeleton)]]);
}
+ public:
+ /** \brief Reads and stores the distance matrices from vector stored in memory.
+ *
+ * @param[in] distance_matrix input vector representing the distance matrix.
+ *
+ */
+ void set_distances_from_range(const std::vector<std::vector<double> > & distance_matrix) {
+ n = distance_matrix.size(); data_dimension = 0; point_cloud_name = "matrix";
+ cover.resize(n); point_cloud.resize(n);
+ for(int i = 0; i < n; i++){
+ boost::add_vertex(one_skeleton_OFF);
+ vertices.push_back(boost::add_vertex(one_skeleton));
+ }
+ distances = distance_matrix;
+ }
+
public: // Pairwise distances.
/** \private \brief Computes all pairwise distances.
*/
@@ -424,7 +457,7 @@ class Cover_complex {
template <typename Distance>
double set_graph_from_automatic_rips(Distance distance, int N = 100) {
int m = floor(n / std::exp((1 + rate_power) * std::log(std::log(n) / std::log(rate_constant))));
- m = std::min(m, n - 1);
+ m = (std::min)(m, n - 1);
double delta = 0;
if (verbose) std::cout << n << " points in R^" << data_dimension << std::endl;
@@ -442,8 +475,8 @@ class Cover_complex {
double hausdorff_dist = 0;
for (int j = 0; j < n; j++) {
double mj = distances[j][samples[0]];
- for (int k = 1; k < m; k++) mj = std::min(mj, distances[j][samples[k]]);
- hausdorff_dist = std::max(hausdorff_dist, mj);
+ for (int k = 1; k < m; k++) mj = (std::min)(mj, distances[j][samples[k]]);
+ hausdorff_dist = (std::max)(hausdorff_dist, mj);
}
deltamutex.lock();
delta += hausdorff_dist / N;
@@ -456,8 +489,8 @@ class Cover_complex {
double hausdorff_dist = 0;
for (int j = 0; j < n; j++) {
double mj = distances[j][samples[0]];
- for (int k = 1; k < m; k++) mj = std::min(mj, distances[j][samples[k]]);
- hausdorff_dist = std::max(hausdorff_dist, mj);
+ for (int k = 1; k < m; k++) mj = (std::min)(mj, distances[j][samples[k]]);
+ hausdorff_dist = (std::max)(hausdorff_dist, mj);
}
delta += hausdorff_dist / N;
}
@@ -500,9 +533,17 @@ class Cover_complex {
*
*/
void set_function_from_coordinate(int k) {
- for (int i = 0; i < n; i++) func.push_back(point_cloud[i][k]);
- functional_cover = true;
- cover_name = "coordinate " + std::to_string(k);
+ if(point_cloud[0].size() > 0){
+ for (int i = 0; i < n; i++) func.push_back(point_cloud[i][k]);
+ functional_cover = true;
+ cover_name = "coordinate " + std::to_string(k);
+ }
+ else{
+ std::cout << "Only pairwise distances provided---cannot access " << k << "th coordinate; returning null vector instead" << std::endl;
+ for (int i = 0; i < n; i++) func.push_back(0.0);
+ functional_cover = true;
+ cover_name = "null";
+ }
}
public: // Set function from vector.
@@ -545,7 +586,7 @@ class Cover_complex {
if (type == "GIC") {
boost::graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
- reso = std::max(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
+ reso = (std::max)(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
func[index[boost::target(*ei, one_skeleton)]]));
if (verbose) std::cout << "resolution = " << reso << std::endl;
resolution_double = reso;
@@ -554,7 +595,7 @@ class Cover_complex {
if (type == "Nerve") {
boost::graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
- reso = std::max(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
+ reso = (std::max)(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
func[index[boost::target(*ei, one_skeleton)]]) /
gain);
if (verbose) std::cout << "resolution = " << reso << std::endl;
@@ -599,11 +640,11 @@ class Cover_complex {
}
// Read function values and compute min and max
- double minf = std::numeric_limits<float>::max();
+ double minf = (std::numeric_limits<float>::max)();
double maxf = std::numeric_limits<float>::lowest();
for (int i = 0; i < n; i++) {
- minf = std::min(minf, func[i]);
- maxf = std::max(maxf, func[i]);
+ minf = (std::min)(minf, func[i]);
+ maxf = (std::max)(maxf, func[i]);
}
if (verbose) std::cout << "Min function value = " << minf << " and Max function value = " << maxf << std::endl;
@@ -858,7 +899,7 @@ class Cover_complex {
Weight_map weight = boost::get(boost::edge_weight, one_skeleton);
Index_map index = boost::get(boost::vertex_index, one_skeleton);
std::vector<double> mindist(n);
- for (int j = 0; j < n; j++) mindist[j] = std::numeric_limits<double>::max();
+ for (int j = 0; j < n; j++) mindist[j] = (std::numeric_limits<double>::max)();
// Compute the geodesic distances to subsamples with Dijkstra
#ifdef GUDHI_USE_TBB
@@ -953,9 +994,17 @@ class Cover_complex {
*
*/
void set_color_from_coordinate(int k = 0) {
- for (int i = 0; i < n; i++) func_color.push_back(point_cloud[i][k]);
- color_name = "coordinate ";
- color_name.append(std::to_string(k));
+ if(point_cloud[0].size() > 0){
+ for (int i = 0; i < n; i++) func_color.push_back(point_cloud[i][k]);
+ color_name = "coordinate ";
+ color_name.append(std::to_string(k));
+ }
+ else{
+ std::cout << "Only pairwise distances provided---cannot access " << k << "th coordinate; returning null vector instead" << std::endl;
+ for (int i = 0; i < n; i++) func.push_back(0.0);
+ functional_cover = true;
+ cover_name = "null";
+ }
}
public: // Set color from vector.
@@ -964,7 +1013,7 @@ class Cover_complex {
* @param[in] color input vector of values.
*
*/
- void set_color_from_vector(std::vector<double> color) {
+ void set_color_from_range(std::vector<double> color) {
for (unsigned int i = 0; i < color.size(); i++) func_color.push_back(color[i]);
}
@@ -978,10 +1027,10 @@ class Cover_complex {
std::ofstream graphic(mapp);
double maxv = std::numeric_limits<double>::lowest();
- double minv = std::numeric_limits<double>::max();
+ double minv = (std::numeric_limits<double>::max)();
for (std::map<int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
- maxv = std::max(maxv, iit->second.second);
- minv = std::min(minv, iit->second.second);
+ maxv = (std::max)(maxv, iit->second.second);
+ minv = (std::min)(minv, iit->second.second);
}
int k = 0;
@@ -1108,28 +1157,31 @@ class Cover_complex {
/** \brief Computes the extended persistence diagram of the complex.
*
*/
- void compute_PD() {
+ Persistence_diagram compute_PD() {
Simplex_tree st;
// Compute max and min
double maxf = std::numeric_limits<double>::lowest();
- double minf = std::numeric_limits<double>::max();
+ double minf = (std::numeric_limits<double>::max)();
for (std::map<int, double>::iterator it = cover_std.begin(); it != cover_std.end(); it++) {
- maxf = std::max(maxf, it->second);
- minf = std::min(minf, it->second);
+ maxf = (std::max)(maxf, it->second);
+ minf = (std::min)(minf, it->second);
}
// Build filtration
for (auto const& simplex : simplices) {
- std::vector<int> splx = simplex; splx.push_back(-2);
+ std::vector<int> splx = simplex;
+ splx.push_back(-2);
st.insert_simplex_and_subfaces(splx, -3);
}
for (std::map<int, double>::iterator it = cover_std.begin(); it != cover_std.end(); it++) {
int vertex = it->first; float val = it->second;
int vert[] = {vertex}; int edge[] = {vertex, -2};
- st.assign_filtration(st.find(vert), -2 + (val - minf)/(maxf - minf));
- st.assign_filtration(st.find(edge), 2 - (val - minf)/(maxf - minf));
+ if(st.find(vert) != st.null_simplex()){
+ st.assign_filtration(st.find(vert), -2 + (val - minf)/(maxf - minf));
+ st.assign_filtration(st.find(edge), 2 - (val - minf)/(maxf - minf));
+ }
}
st.make_filtration_non_decreasing();
@@ -1159,6 +1211,7 @@ class Cover_complex {
if (verbose) std::cout << " [" << birth << ", " << death << "]" << std::endl;
}
}
+ return PD;
}
public:
@@ -1184,7 +1237,7 @@ class Cover_complex {
Cboot.point_cloud.push_back(this->point_cloud[id]); Cboot.cover.emplace_back(); Cboot.func.push_back(this->func[id]);
boost::add_vertex(Cboot.one_skeleton_OFF); Cboot.vertices.push_back(boost::add_vertex(Cboot.one_skeleton));
}
- Cboot.set_color_from_vector(Cboot.func);
+ Cboot.set_color_from_range(Cboot.func);
for (int j = 0; j < n; j++) {
std::vector<double> dist(n);
@@ -1230,7 +1283,7 @@ class Cover_complex {
unsigned int N = distribution.size();
double level = 1;
for (unsigned int i = 0; i < N; i++)
- if (distribution[i] > d){ level = i * 1.0 / N; break; }
+ if (distribution[i] >= d){ level = i * 1.0 / N; break; }
if (verbose) std::cout << "Confidence level of distance " << d << " is " << level << std::endl;
return level;
}
@@ -1241,8 +1294,8 @@ class Cover_complex {
*
*/
double compute_p_value() {
- double distancemin = std::numeric_limits<double>::max(); int N = PD.size();
- for (int i = 0; i < N; i++) distancemin = std::min(distancemin, 0.5 * std::abs(PD[i].second - PD[i].first));
+ double distancemin = (std::numeric_limits<double>::max)(); int N = PD.size();
+ for (int i = 0; i < N; i++) distancemin = (std::min)(distancemin, 0.5 * std::abs(PD[i].second - PD[i].first));
double p_value = 1 - compute_confidence_level_from_distance(distancemin);
if (verbose) std::cout << "p value = " << p_value << std::endl;
return p_value;
@@ -1264,7 +1317,7 @@ class Cover_complex {
for (auto const& simplex : simplices) {
int numvert = simplex.size();
double filt = std::numeric_limits<double>::lowest();
- for (int i = 0; i < numvert; i++) filt = std::max(cover_color[simplex[i]].second, filt);
+ for (int i = 0; i < numvert; i++) filt = (std::max)(cover_color[simplex[i]].second, filt);
complex.insert_simplex_and_subfaces(simplex, filt);
if (dimension < simplex.size() - 1) dimension = simplex.size() - 1;
}
@@ -1308,8 +1361,8 @@ class Cover_complex {
int vt = cover[index[boost::target(*ei, one_skeleton)]][j];
if (cover_fct[vs] == cover_fct[vt] + 1 || cover_fct[vt] == cover_fct[vs] + 1) {
std::vector<int> edge(2);
- edge[0] = std::min(vs, vt);
- edge[1] = std::max(vs, vt);
+ edge[0] = (std::min)(vs, vt);
+ edge[1] = (std::max)(vs, vt);
simplices.push_back(edge);
goto afterLoop;
}
diff --git a/src/Persistent_cohomology/example/persistence_from_file.cpp b/src/Persistent_cohomology/example/persistence_from_file.cpp
index 53456919..0a05c193 100644
--- a/src/Persistent_cohomology/example/persistence_from_file.cpp
+++ b/src/Persistent_cohomology/example/persistence_from_file.cpp
@@ -33,7 +33,6 @@
using namespace Gudhi;
using namespace Gudhi::persistent_cohomology;
-typedef int Vertex_handle;
typedef double Filtration_value;
void program_options(int argc, char * argv[]
diff --git a/src/Rips_complex/concept/SimplicialComplexForRips.h b/src/Rips_complex/concept/SimplicialComplexForRips.h
index 3c5acecf..36ab1b0c 100644
--- a/src/Rips_complex/concept/SimplicialComplexForRips.h
+++ b/src/Rips_complex/concept/SimplicialComplexForRips.h
@@ -34,6 +34,9 @@ struct SimplicialComplexForRips {
/** \brief Type used to store the filtration values of the simplicial complex. */
typedef unspecified Filtration_value;
+ /** \brief Handle type to a simplex contained in the simplicial complex. */
+ typedef unspecified Simplex_handle;
+
/** \brief Inserts a given `Gudhi::rips_complex::Rips_complex::OneSkeletonGraph` in the simplicial complex. */
template<class OneSkeletonGraph>
void insert_graph(const OneSkeletonGraph& skel_graph);
@@ -42,6 +45,24 @@ struct SimplicialComplexForRips {
* explained in \ref ripsdefinition. */
void expansion(int max_dim);
+ /** \brief Expands a simplicial complex containing only a graph. Simplices corresponding to cliques in the graph are added
+ * incrementally, faces before cofaces, unless the simplex has dimension larger than `max_dim` or `block_simplex`
+ * returns true for this simplex.
+ *
+ * @param[in] max_dim Expansion maximal dimension value.
+ * @param[in] block_simplex Blocker oracle. Its concept is <CODE>bool block_simplex(Simplex_handle sh)</CODE>
+ *
+ * The function identifies a candidate simplex whose faces are all already in the complex, inserts
+ * it with a filtration value corresponding to the maximum of the filtration values of the faces, then calls
+ * `block_simplex` on a `Simplex_handle` for this new simplex. If `block_simplex` returns true, the simplex is
+ * removed, otherwise it is kept.
+ */
+ template< typename Blocker >
+ void expansion_with_blockers(int max_dim, Blocker block_simplex);
+
+ /** \brief Returns a range over the vertices of a simplex. */
+ unspecified simplex_vertex_range(Simplex_handle sh);
+
/** \brief Returns the number of vertices in the simplicial complex. */
std::size_t num_vertices();
diff --git a/src/Rips_complex/doc/Intro_rips_complex.h b/src/Rips_complex/doc/Intro_rips_complex.h
index 712d3b6e..97d66fbd 100644
--- a/src/Rips_complex/doc/Intro_rips_complex.h
+++ b/src/Rips_complex/doc/Intro_rips_complex.h
@@ -39,7 +39,7 @@ namespace rips_complex {
* <a target="_blank" href="https://en.wikipedia.org/wiki/Vietoris%E2%80%93Rips_complex">(Wikipedia)</a>
* is an abstract simplicial complex
* defined on a finite metric space, where each simplex corresponds to a subset
- * of point whose diameter is smaller that some given threshold.
+ * of points whose diameter is smaller that some given threshold.
* Varying the threshold, we can also see the Rips complex as a filtration of
* the \f$(n-1)-\f$dimensional simplex, where the filtration value of each
* simplex is the diameter of the corresponding subset of points.
@@ -53,7 +53,10 @@ namespace rips_complex {
* The number of simplices in the full Rips complex is exponential in the
* number of vertices, it is thus usually restricted, by excluding all the
* simplices with filtration value larger than some threshold, and keeping only
- * the dim_max-skeleton.
+ * the dim_max-skeleton. It may also be a good idea to start by making the
+ * point set sparser, for instance with
+ * `Gudhi::subsampling::sparsify_point_set()`, since small clusters of points
+ * have a disproportionate cost without affecting the persistence diagram much.
*
* In order to build this complex, the algorithm first builds the graph.
* The filtration value of each edge is computed from a user-given distance
@@ -89,21 +92,27 @@ namespace rips_complex {
* The sparse Rips filtration was introduced by Don Sheehy
* \cite sheehy13linear. We are using the version described in
* \cite buchet16efficient (except that we multiply all filtration values
- * by 2, to match the usual Rips complex), which proves a
- * \f$\frac{1+\epsilon}{1-\epsilon}\f$-interleaving, although in practice the
+ * by 2, to match the usual Rips complex), for which \cite cavanna15geometric proves a
+ * \f$(1,\frac{1}{1-\epsilon})\f$-interleaving, although in practice the
* error is usually smaller.
* A more intuitive presentation of the idea is available in
* \cite cavanna15geometric, and in a video \cite cavanna15visualizing.
*
* The interface of `Sparse_rips_complex` is similar to the one for the usual
- * `Rips_complex`, except that one has to specify the approximation factor, and
- * there is no option to limit the maximum filtration value (the way the
- * approximation is done means that larger filtration values are much cheaper
- * to handle than low filtration values, so the gain would be too small).
+ * `Rips_complex`, except that one has to specify the approximation factor.
+ * There is an option to limit the minimum and maximum filtration values, but
+ * they are not recommended: the way the approximation is done means that
+ * larger filtration values are much cheaper to handle than low filtration
+ * values, so the gain in ignoring the large ones is small, and
+ * `Gudhi::subsampling::sparsify_point_set()` is a more efficient way of
+ * ignoring small filtration values.
*
* Theoretical guarantees are only available for \f$\epsilon<1\f$. The
* construction accepts larger values of &epsilon;, and the size of the complex
* keeps decreasing, but there is no guarantee on the quality of the result.
+ * Note that while the number of edges decreases when &epsilon; increases, the
+ * number of higher-dimensional simplices may not be monotonous when
+ * \f$\frac12\leq\epsilon\leq 1\f$.
*
* \section ripspointsdistance Point cloud and distance function
*
@@ -116,8 +125,8 @@ namespace rips_complex {
*
* \include Rips_complex/example_one_skeleton_rips_from_points.cpp
*
- * When launching (Rips maximal distance between 2 points is 12.0, is expanded until dimension 1 - one skeleton graph
- * in other words):
+ * When launching (Rips maximal distance between 2 points is 12.0, is expanded
+ * until dimension 1 - one skeleton graph in other words):
*
* \code $> ./Rips_complex_example_one_skeleton_from_points
* \endcode
diff --git a/src/Rips_complex/include/gudhi/Rips_complex.h b/src/Rips_complex/include/gudhi/Rips_complex.h
index f0fe57f4..ee100867 100644
--- a/src/Rips_complex/include/gudhi/Rips_complex.h
+++ b/src/Rips_complex/include/gudhi/Rips_complex.h
@@ -59,7 +59,7 @@ class Rips_complex {
/**
* \brief Type of the one skeleton graph stored inside the Rips complex structure.
*/
- typedef typename boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS
+ typedef typename boost::adjacency_list < boost::vecS, boost::vecS, boost::directedS
, boost::property < vertex_filtration_t, Filtration_value >
, boost::property < edge_filtration_t, Filtration_value >> OneSkeletonGraph;
@@ -90,7 +90,7 @@ class Rips_complex {
* @param[in] threshold Rips value.
*
* \tparam DistanceMatrix must have a `size()` method and on which `distance_matrix[i][j]` returns
- * the distance between points \f$i\f$ and \f$j\f$ as long as \f$ 0 \leqslant i < j \leqslant
+ * the distance between points \f$i\f$ and \f$j\f$ as long as \f$ 0 \leqslant j < i \leqslant
* distance\_matrix.size().\f$
*/
template<typename DistanceMatrix>
diff --git a/src/Rips_complex/include/gudhi/Sparse_rips_complex.h b/src/Rips_complex/include/gudhi/Sparse_rips_complex.h
index 4dcc08ed..8df6e387 100644
--- a/src/Rips_complex/include/gudhi/Sparse_rips_complex.h
+++ b/src/Rips_complex/include/gudhi/Sparse_rips_complex.h
@@ -47,7 +47,9 @@ namespace rips_complex {
*
* \details
* This class is used to construct a sparse \f$(1+O(\epsilon))\f$-approximation of `Rips_complex`, i.e. a filtered
- * simplicial complex that is multiplicatively \f$(1+O(\epsilon))\f$-interleaved with the Rips filtration.
+ * simplicial complex that is multiplicatively
+ * \f$(1+O(\epsilon))\f$-interleaved with the Rips filtration. More precisely,
+ * this is a \f$(1,\frac{1}{1-\epsilon}\f$-interleaving.
*
* \tparam Filtration_value is the type used to store the filtration values of the simplicial complex.
*/
@@ -55,7 +57,7 @@ template <typename Filtration_value>
class Sparse_rips_complex {
private:
// TODO(MG): use a different graph where we know we can safely insert in parallel.
- typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
+ typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::property<vertex_filtration_t, Filtration_value>,
boost::property<edge_filtration_t, Filtration_value>>
Graph;
@@ -68,32 +70,36 @@ class Sparse_rips_complex {
* @param[in] points Range of points.
* @param[in] distance Distance function that returns a `Filtration_value` from 2 given points.
* @param[in] epsilon Approximation parameter. epsilon must be positive.
+ * @param[in] mini Minimal filtration value. Ignore anything below this scale. This is a less efficient version of `Gudhi::subsampling::sparsify_point_set()`.
+ * @param[in] maxi Maximal filtration value. Ignore anything above this scale.
*
*/
template <typename RandomAccessPointRange, typename Distance>
- Sparse_rips_complex(const RandomAccessPointRange& points, Distance distance, double epsilon) {
+ Sparse_rips_complex(const RandomAccessPointRange& points, Distance distance, double epsilon, Filtration_value mini=-std::numeric_limits<Filtration_value>::infinity(), Filtration_value maxi=std::numeric_limits<Filtration_value>::infinity())
+ : epsilon_(epsilon) {
GUDHI_CHECK(epsilon > 0, "epsilon must be positive");
- std::vector<Vertex_handle> sorted_points;
- std::vector<Filtration_value> params;
auto dist_fun = [&](Vertex_handle i, Vertex_handle j) { return distance(points[i], points[j]); };
Ker<decltype(dist_fun)> kernel(dist_fun);
subsampling::choose_n_farthest_points(kernel, boost::irange<Vertex_handle>(0, boost::size(points)), -1, -1,
std::back_inserter(sorted_points), std::back_inserter(params));
- compute_sparse_graph(sorted_points, params, dist_fun, epsilon);
+ compute_sparse_graph(dist_fun, epsilon, mini, maxi);
}
/** \brief Sparse_rips_complex constructor from a distance matrix.
*
* @param[in] distance_matrix Range of range of distances.
* `distance_matrix[i][j]` returns the distance between points \f$i\f$ and
- * \f$j\f$ as long as \f$ 0 \leqslant i < j \leqslant
+ * \f$j\f$ as long as \f$ 0 \leqslant j < i \leqslant
* distance\_matrix.size().\f$
* @param[in] epsilon Approximation parameter. epsilon must be positive.
+ * @param[in] mini Minimal filtration value. Ignore anything below this scale. This is a less efficient version of `Gudhi::subsampling::sparsify_point_set()`.
+ * @param[in] maxi Maximal filtration value. Ignore anything above this scale.
*/
template <typename DistanceMatrix>
- Sparse_rips_complex(const DistanceMatrix& distance_matrix, double epsilon)
+ Sparse_rips_complex(const DistanceMatrix& distance_matrix, double epsilon, Filtration_value mini=-std::numeric_limits<Filtration_value>::infinity(), Filtration_value maxi=std::numeric_limits<Filtration_value>::infinity())
: Sparse_rips_complex(boost::irange<Vertex_handle>(0, boost::size(distance_matrix)),
- [&](Vertex_handle i, Vertex_handle j) { return distance_matrix[j][i]; }, epsilon) {}
+ [&](Vertex_handle i, Vertex_handle j) { return (i==j) ? 0 : (i<j) ? distance_matrix[j][i] : distance_matrix[i][j]; },
+ epsilon, mini, maxi) {}
/** \brief Fills the simplicial complex with the sparse Rips graph and
* expands it with all the cliques, stopping at a given maximal dimension.
@@ -111,7 +117,26 @@ class Sparse_rips_complex {
std::invalid_argument("Sparse_rips_complex::create_complex - simplicial complex is not empty"));
complex.insert_graph(graph_);
- complex.expansion(dim_max);
+ if(epsilon_ >= 1) {
+ complex.expansion(dim_max);
+ return;
+ }
+ const int n = boost::size(params);
+ std::vector<Filtration_value> lambda(n);
+ // lambda[original_order]=params[sorted_order]
+ for(int i=0;i<n;++i)
+ lambda[sorted_points[i]] = params[i];
+ double cst = epsilon_ * (1 - epsilon_) / 2;
+ auto block = [cst,&complex,&lambda](typename SimplicialComplexForRips::Simplex_handle sh){
+ auto filt = complex.filtration(sh);
+ auto mini = filt * cst;
+ for(auto v : complex.simplex_vertex_range(sh)){
+ if(lambda[v] < mini)
+ return true; // v died before this simplex could be born
+ }
+ return false;
+ };
+ complex.expansion_with_blockers(dim_max, block);
}
private:
@@ -127,9 +152,11 @@ class Sparse_rips_complex {
};
// PointRange must be random access.
- template <typename PointRange, typename ParamRange, typename Distance>
- void compute_sparse_graph(const PointRange& points, const ParamRange& params, Distance& dist, double epsilon) {
+ template <typename Distance>
+ void compute_sparse_graph(Distance& dist, double epsilon, Filtration_value mini, Filtration_value maxi) {
+ const auto& points = sorted_points; // convenience alias
const int n = boost::size(points);
+ double cst = epsilon * (1 - epsilon) / 2;
graph_.~Graph();
new (&graph_) Graph(n);
// for(auto v : vertices(g)) // doesn't work :-(
@@ -143,29 +170,43 @@ class Sparse_rips_complex {
// TODO(MG):
// - make it parallel
// - only test near-enough neighbors
- for (int i = 0; i < n; ++i)
+ for (int i = 0; i < n; ++i) {
+ auto&& pi = points[i];
+ auto li = params[i];
+ if (li < mini) break;
for (int j = i + 1; j < n; ++j) {
- auto&& pi = points[i];
auto&& pj = points[j];
auto d = dist(pi, pj);
- auto li = params[i];
auto lj = params[j];
+ if (lj < mini) break;
GUDHI_CHECK(lj <= li, "Bad furthest point sorting");
Filtration_value alpha;
// The paper has d/2 and d-lj/e to match the Cech, but we use doubles to match the Rips
if (d * epsilon <= 2 * lj)
alpha = d;
- else if (d * epsilon <= li + lj && (epsilon >= 1 || d * epsilon <= lj * (1 + 1 / (1 - epsilon))))
- alpha = (d - lj / epsilon) * 2;
- else
+ else if (d * epsilon > li + lj)
continue;
+ else {
+ alpha = (d - lj / epsilon) * 2;
+ // Keep the test exactly the same as in block to avoid inconsistencies
+ if (epsilon < 1 && alpha * cst > lj)
+ continue;
+ }
- add_edge(pi, pj, alpha, graph_);
+ if (alpha <= maxi)
+ add_edge(pi, pj, alpha, graph_);
}
+ }
}
Graph graph_;
+ double epsilon_;
+ // Because of the arbitrary split between constructor and create_complex
+ // sorted_points[sorted_order]=original_order
+ std::vector<Vertex_handle> sorted_points;
+ // params[sorted_order]=distance to previous points
+ std::vector<Filtration_value> params;
};
} // namespace rips_complex
diff --git a/src/Rips_complex/utilities/ripscomplex.md b/src/Rips_complex/utilities/ripscomplex.md
index 6df49310..03838085 100644
--- a/src/Rips_complex/utilities/ripscomplex.md
+++ b/src/Rips_complex/utilities/ripscomplex.md
@@ -85,7 +85,7 @@ properly, this is a known issue.
## sparse_rips_persistence ##
This program computes the persistent homology with coefficient field *Z/pZ*
-of a sparse (1+epsilon)-approximation of the Rips complex defined on a set of input Euclidean points. The output diagram contains one bar per line, written with the convention:
+of a sparse 1/(1-epsilon)-approximation of the Rips complex defined on a set of input Euclidean points. The output diagram contains one bar per line, written with the convention:
`p dim birth death`
@@ -99,8 +99,8 @@ where `dim` is the dimension of the homological feature, `birth` and `death` are
* `-h [ --help ]` Produce help message
* `-o [ --output-file ]` Name of file in which the persistence diagram is written. Default print in standard output.
-* `-e [ --approximation ]` (default = .5) Epsilon, where the sparse Rips complex is a (1+epsilon)-approximation of the Rips complex.
-* `-d [ --cpx-dimension ]` (default = 1) Maximal dimension of the Rips complex we want to compute.
+* `-e [ --approximation ]` (default = .5) Epsilon, where the sparse Rips complex is a (1+epsilon)/(1-epsilon)-approximation of the Rips complex.
+* `-d [ --cpx-dimension ]` (default = INT_MAX) Maximal dimension of the Rips complex we want to compute.
* `-p [ --field-charac ]` (default = 11) Characteristic p of the coefficient field Z/pZ for computing homology.
* `-m [ --min-persistence ]` (default = 0) Minimal lifetime of homology feature to be recorded. Enter a negative value to see zero length intervals.
diff --git a/src/Rips_complex/utilities/sparse_rips_persistence.cpp b/src/Rips_complex/utilities/sparse_rips_persistence.cpp
index 6d4d86fd..3840d9f7 100644
--- a/src/Rips_complex/utilities/sparse_rips_persistence.cpp
+++ b/src/Rips_complex/utilities/sparse_rips_persistence.cpp
@@ -98,7 +98,7 @@ void program_options(int argc, char* argv[], std::string& off_file_points, std::
"Name of file in which the persistence diagram is written. Default print in std::cout")(
"approximation,e", po::value<double>(&epsilon)->default_value(.5),
"Epsilon, where the sparse Rips complex is a (1+epsilon)-approximation of the Rips complex.")(
- "cpx-dimension,d", po::value<int>(&dim_max)->default_value(1),
+ "cpx-dimension,d", po::value<int>(&dim_max)->default_value(std::numeric_limits<int>::max()),
"Maximal dimension of the Rips complex we want to compute.")(
"field-charac,p", po::value<int>(&p)->default_value(11),
"Characteristic p of the coefficient field Z/pZ for computing homology.")(
@@ -119,7 +119,7 @@ void program_options(int argc, char* argv[], std::string& off_file_points, std::
if (vm.count("help") || !vm.count("input-file")) {
std::cout << std::endl;
std::cout << "Compute the persistent homology with coefficient field Z/pZ \n";
- std::cout << "of a sparse (1+epsilon)-approximation of the Rips complex \ndefined on a set of input points.\n \n";
+ std::cout << "of a sparse 1/(1-epsilon)-approximation of the Rips complex \ndefined on a set of input points.\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/Simplex_tree/example/cech_complex_cgal_mini_sphere_3d.cpp b/src/Simplex_tree/example/cech_complex_cgal_mini_sphere_3d.cpp
index 34092ef6..6bab8adb 100644
--- a/src/Simplex_tree/example/cech_complex_cgal_mini_sphere_3d.cpp
+++ b/src/Simplex_tree/example/cech_complex_cgal_mini_sphere_3d.cpp
@@ -50,7 +50,7 @@ using Vertex_handle = Simplex_tree::Vertex_handle;
using Simplex_handle = Simplex_tree::Simplex_handle;
using Filtration_value = Simplex_tree::Filtration_value;
using Siblings = Simplex_tree::Siblings;
-using Graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
+using Graph_t = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
boost::property<Gudhi::vertex_filtration_t, Filtration_value>,
boost::property<Gudhi::edge_filtration_t, Filtration_value> >;
using Edge_t = std::pair<Vertex_handle, Vertex_handle>;
diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h
index ee96d5a2..4b18651c 100644
--- a/src/Simplex_tree/include/gudhi/Simplex_tree.h
+++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h
@@ -296,12 +296,79 @@ class Simplex_tree {
dimension_(-1) { }
/** \brief User-defined copy constructor reproduces the whole tree structure. */
- Simplex_tree(const Simplex_tree& simplex_source)
- : null_vertex_(simplex_source.null_vertex_),
- root_(nullptr, null_vertex_ , simplex_source.root_.members_),
- filtration_vect_(),
- dimension_(simplex_source.dimension_) {
- auto root_source = simplex_source.root_;
+ Simplex_tree(const Simplex_tree& complex_source) {
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree copy constructor" << std::endl;
+#endif // DEBUG_TRACES
+ copy_from(complex_source);
+ }
+
+ /** \brief User-defined move constructor relocates the whole tree structure.
+ * \exception std::invalid_argument In debug mode, if the complex_source is invalid.
+ */
+ Simplex_tree(Simplex_tree && complex_source) {
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree move constructor" << std::endl;
+#endif // DEBUG_TRACES
+ move_from(complex_source);
+
+ // just need to set dimension_ on source to make it available again
+ // (filtration_vect_ and members are already set from the move)
+ complex_source.dimension_ = -1;
+ }
+
+ /** \brief Destructor; deallocates the whole tree structure. */
+ ~Simplex_tree() {
+ root_members_recursive_deletion();
+ }
+
+ /** \brief User-defined copy assignment reproduces the whole tree structure. */
+ Simplex_tree& operator= (const Simplex_tree& complex_source) {
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree copy assignment" << std::endl;
+#endif // DEBUG_TRACES
+ // Self-assignment detection
+ if (&complex_source != this) {
+ // We start by deleting root_ if not empty
+ root_members_recursive_deletion();
+
+ copy_from(complex_source);
+ }
+ return *this;
+ }
+
+ /** \brief User-defined move assignment relocates the whole tree structure.
+ * \exception std::invalid_argument In debug mode, if the complex_source is invalid.
+ */
+ Simplex_tree& operator=(Simplex_tree&& complex_source) {
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree move assignment" << std::endl;
+#endif // DEBUG_TRACES
+ // Self-assignment detection
+ if (&complex_source != this) {
+ // root_ deletion in case it was not empty
+ root_members_recursive_deletion();
+
+ move_from(complex_source);
+ }
+ return *this;
+ }
+ /** @} */ // end constructor/destructor
+
+ private:
+ // Copy from complex_source to "this"
+ void copy_from(const Simplex_tree& complex_source) {
+ null_vertex_ = complex_source.null_vertex_;
+ filtration_vect_.clear();
+ dimension_ = complex_source.dimension_;
+ auto root_source = complex_source.root_;
+
+ // root members copy
+ root_.members() = Dictionary(boost::container::ordered_unique_range, root_source.members().begin(), root_source.members().end());
+ // Needs to reassign children
+ for (auto& map_el : root_.members()) {
+ map_el.second.assign_children(&root_);
+ }
rec_copy(&root_, &root_source);
}
@@ -320,26 +387,38 @@ class Simplex_tree {
}
}
- /** \brief User-defined move constructor moves the whole tree structure. */
- Simplex_tree(Simplex_tree && old)
- : null_vertex_(std::move(old.null_vertex_)),
- root_(std::move(old.root_)),
- filtration_vect_(std::move(old.filtration_vect_)),
- dimension_(std::move(old.dimension_)) {
- old.dimension_ = -1;
- old.root_ = Siblings(nullptr, null_vertex_);
+ // Move from complex_source to "this"
+ void move_from(Simplex_tree& complex_source) {
+ null_vertex_ = std::move(complex_source.null_vertex_);
+ root_ = std::move(complex_source.root_);
+ filtration_vect_ = std::move(complex_source.filtration_vect_);
+ dimension_ = std::move(complex_source.dimension_);
+
+ // Need to update root members (children->oncles and children need to point on the new root pointer)
+ for (auto& map_el : root_.members()) {
+ if (map_el.second.children() != &(complex_source.root_)) {
+ // reset children->oncles with the moved root_ pointer value
+ map_el.second.children()->oncles_ = &root_;
+ } else {
+ // if simplex is of dimension 0, oncles_ shall be nullptr
+ GUDHI_CHECK(map_el.second.children()->oncles_ == nullptr,
+ std::invalid_argument("Simplex_tree move constructor from an invalid Simplex_tree"));
+ // and children points on root_ - to be moved
+ map_el.second.assign_children(&root_);
+ }
+ }
}
- /** \brief Destructor; deallocates the whole tree structure. */
- ~Simplex_tree() {
+ // delete all root_.members() recursively
+ void root_members_recursive_deletion() {
for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
if (has_children(sh)) {
rec_delete(sh->second.children());
}
}
+ root_.members().clear();
}
- /** @} */ // end constructor/destructor
- private:
+
// Recursive deletion
void rec_delete(Siblings * sib) {
for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
@@ -970,7 +1049,8 @@ class Simplex_tree {
* boost::graph_traits<OneSkeletonGraph>::vertex_descriptor
* must be Vertex_handle.
* boost::graph_traits<OneSkeletonGraph>::directed_category
- * must be undirected_tag.
+ * can be directed_tag (the fastest, the least RAM use), undirected_tag or even
+ * bidirected_tag.
*
* If an edge appears with multiplicity, the function will arbitrarily pick
* one representative to read the filtration value. */
@@ -998,12 +1078,14 @@ class Simplex_tree {
root_.members_.end(), *v_it,
Node(&root_, boost::get(vertex_filtration_t(), skel_graph, *v_it)));
}
- typename boost::graph_traits<OneSkeletonGraph>::edge_iterator e_it,
- e_it_end;
- for (std::tie(e_it, e_it_end) = boost::edges(skel_graph); e_it != e_it_end;
- ++e_it) {
- auto u = source(*e_it, skel_graph);
- auto v = target(*e_it, skel_graph);
+ std::pair<typename boost::graph_traits<OneSkeletonGraph>::edge_iterator,
+ typename boost::graph_traits<OneSkeletonGraph>::edge_iterator> boost_edges = boost::edges(skel_graph);
+ // boost_edges.first is the equivalent to boost_edges.begin()
+ // boost_edges.second is the equivalent to boost_edges.end()
+ for (; boost_edges.first != boost_edges.second; boost_edges.first++) {
+ auto edge = *(boost_edges.first);
+ auto u = source(edge, skel_graph);
+ auto v = target(edge, skel_graph);
if (u == v) throw "Self-loops are not simplicial";
// We cannot skip edges with the wrong orientation and expect them to
// come a second time with the right orientation, that does not always
@@ -1019,7 +1101,7 @@ class Simplex_tree {
}
sh->second.children()->members().emplace(v,
- Node(sh->second.children(), boost::get(edge_filtration_t(), skel_graph, *e_it)));
+ Node(sh->second.children(), boost::get(edge_filtration_t(), skel_graph, edge)));
}
}
@@ -1035,6 +1117,7 @@ class Simplex_tree {
* The Simplex_tree must contain no simplex of dimension bigger than
* 1 when calling the method. */
void expansion(int max_dim) {
+ if (max_dim <= 1) return;
dimension_ = max_dim;
for (Dictionary_it root_it = root_.members_.begin();
root_it != root_.members_.end(); ++root_it) {
@@ -1057,7 +1140,10 @@ class Simplex_tree {
Dictionary_it next = siblings->members().begin();
++next;
- thread_local std::vector<std::pair<Vertex_handle, Node> > inter;
+#ifdef GUDHI_CAN_USE_CXX11_THREAD_LOCAL
+ thread_local
+#endif // GUDHI_CAN_USE_CXX11_THREAD_LOCAL
+ std::vector<std::pair<Vertex_handle, Node> > inter;
for (Dictionary_it s_h = siblings->members().begin();
s_h != siblings->members().end(); ++s_h, ++next) {
Simplex_handle root_sh = find_vertex(s_h->first);
diff --git a/src/Simplex_tree/test/CMakeLists.txt b/src/Simplex_tree/test/CMakeLists.txt
index c63d8532..5bea3938 100644
--- a/src/Simplex_tree/test/CMakeLists.txt
+++ b/src/Simplex_tree/test/CMakeLists.txt
@@ -28,3 +28,11 @@ if (TBB_FOUND)
endif()
gudhi_add_coverage_test(Simplex_tree_iostream_operator_test_unit)
+
+add_executable ( Simplex_tree_ctor_and_move_test_unit simplex_tree_ctor_and_move_unit_test.cpp )
+target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+if (TBB_FOUND)
+ target_link_libraries(Simplex_tree_ctor_and_move_test_unit ${TBB_LIBRARIES})
+endif()
+
+gudhi_add_coverage_test(Simplex_tree_ctor_and_move_test_unit)
diff --git a/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp
new file mode 100644
index 00000000..e729cf00
--- /dev/null
+++ b/src/Simplex_tree/test/simplex_tree_ctor_and_move_unit_test.cpp
@@ -0,0 +1,137 @@
+#include <iostream>
+#include <vector>
+#include <string>
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "simplex_tree_constructor_and_move"
+#include <boost/test/unit_test.hpp>
+#include <boost/mpl/list.hpp>
+
+// ^
+// /!\ Nothing else from Simplex_tree shall be included to test includes are well defined.
+#include "gudhi/Simplex_tree.h"
+
+using namespace Gudhi;
+
+typedef boost::mpl::list<Simplex_tree<>, Simplex_tree<Simplex_tree_options_fast_persistence>> list_of_tested_variants;
+
+template<typename Simplex_tree>
+void print_simplex_filtration(Simplex_tree& st, const std::string& msg) {
+ // Required before browsing through filtration values
+ st.initialize_filtration();
+
+ std::cout << "********************************************************************\n";
+ std::cout << "* " << msg << "\n";
+ std::cout << "* The complex contains " << st.num_simplices() << " simplices";
+ std::cout << " - dimension " << st.dimension() << "\n";
+ std::cout << "* Iterator on Simplices in the filtration, with [filtration value]:\n";
+ for (auto f_simplex : st.filtration_simplex_range()) {
+ std::cout << " "
+ << "[" << st.filtration(f_simplex) << "] ";
+ for (auto vertex : st.simplex_vertex_range(f_simplex)) std::cout << "(" << vertex << ")";
+ std::cout << std::endl;
+ }
+
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_copy_constructor, Simplex_tree, list_of_tested_variants) {
+ Simplex_tree st;
+
+ st.insert_simplex_and_subfaces({2, 1, 0}, 3.0);
+ st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.0);
+ st.insert_simplex_and_subfaces({3, 0}, 2.0);
+ st.insert_simplex_and_subfaces({3, 4, 5}, 3.0);
+ st.insert_simplex_and_subfaces({8}, 1.0);
+ /* Inserted simplex: */
+ /* 1 6 */
+ /* o---o */
+ /* /X\7/ */
+ /* o---o---o---o o */
+ /* 2 0 3\X/4 8 */
+ /* o */
+ /* 5 */
+ /* */
+ /* In other words: */
+ /* A facet [2,1,0] */
+ /* An edge [0,3] */
+ /* A facet [3,4,5] */
+ /* A cell [0,1,6,7] */
+ /* A vertex [8] */
+
+ print_simplex_filtration(st, "Default Simplex_tree is initialized");
+
+ std::cout << "********************************************************************" << std::endl;
+ std::cout << "TEST OF COPY CONSTRUCTOR" << std::endl;
+
+ Simplex_tree st1(st);
+ Simplex_tree st2(st);
+ print_simplex_filtration(st1, "First copy constructor from the default Simplex_tree");
+ print_simplex_filtration(st2, "Second copy constructor from the default Simplex_tree");
+ // Cross check
+ BOOST_CHECK(st1 == st2);
+ BOOST_CHECK(st == st2);
+ BOOST_CHECK(st1 == st);
+
+ std::cout << "********************************************************************" << std::endl;
+ std::cout << "TEST OF COPY ASSIGNMENT" << std::endl;
+ Simplex_tree st3;
+ // To check there is no memory leak
+ st3.insert_simplex_and_subfaces({9, 10, 11}, 200.0);
+ st3 = st;
+ print_simplex_filtration(st3, "First copy assignment from the default Simplex_tree");
+ Simplex_tree st4;
+ st4 = st;
+ print_simplex_filtration(st4, "Second copy assignment from the default Simplex_tree");
+
+ // Cross check
+ BOOST_CHECK(st3 == st4);
+ BOOST_CHECK(st == st4);
+ BOOST_CHECK(st3 == st);
+
+ st = st;
+ print_simplex_filtration(st4, "Third self copy assignment from the default Simplex_tree");
+
+ BOOST_CHECK(st3 == st);
+
+ std::cout << "********************************************************************" << std::endl;
+ std::cout << "TEST OF MOVE CONSTRUCTOR" << std::endl;
+ Simplex_tree st5(std::move(st1));
+ print_simplex_filtration(st5, "First move constructor from the default Simplex_tree");
+ print_simplex_filtration(st1, "First moved Simplex_tree shall be empty");
+ Simplex_tree st6(std::move(st2));
+ print_simplex_filtration(st6, "Second move constructor from the default Simplex_tree");
+ print_simplex_filtration(st2, "Second moved Simplex_tree shall be empty");
+
+ // Cross check
+ BOOST_CHECK(st5 == st6);
+ BOOST_CHECK(st == st6);
+ BOOST_CHECK(st5 == st);
+
+ Simplex_tree empty_st;
+ BOOST_CHECK(st1 == st2);
+ BOOST_CHECK(empty_st == st2);
+ BOOST_CHECK(st1 == empty_st);
+
+ std::cout << "********************************************************************" << std::endl;
+ std::cout << "TEST OF MOVE ASSIGNMENT" << std::endl;
+
+ Simplex_tree st7;
+ // To check there is no memory leak
+ st7.insert_simplex_and_subfaces({9, 10, 11}, 200.0);
+ st7 = std::move(st3);
+ print_simplex_filtration(st7, "First move assignment from the default Simplex_tree");
+ Simplex_tree st8;
+ st8 = std::move(st4);
+ print_simplex_filtration(st8, "Second move assignment from the default Simplex_tree");
+
+ // Cross check
+ BOOST_CHECK(st7 == st8);
+ BOOST_CHECK(st == st8);
+ BOOST_CHECK(st7 == st);
+
+ st = std::move(st);
+ print_simplex_filtration(st, "Third self move assignment from the default Simplex_tree");
+
+ BOOST_CHECK(st7 == st);
+
+}
diff --git a/src/Simplex_tree/test/simplex_tree_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_unit_test.cpp
index f63ea080..b27bbce8 100644
--- a/src/Simplex_tree/test/simplex_tree_unit_test.cpp
+++ b/src/Simplex_tree/test/simplex_tree_unit_test.cpp
@@ -864,34 +864,60 @@ BOOST_AUTO_TEST_CASE(make_filtration_non_decreasing) {
}
-BOOST_AUTO_TEST_CASE(insert_graph) {
+
+typedef boost::mpl::list<boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>,
+ boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>,
+ boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>,
+ boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>,
+ boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>,
+ boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
+ boost::property<vertex_filtration_t, double>,
+ boost::property<edge_filtration_t, double>>> list_of_graph_variants;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_insert_graph, Graph, list_of_graph_variants) {
std::cout << "********************************************************************" << std::endl;
std::cout << "INSERT GRAPH" << std::endl;
- typedef typename boost::adjacency_list<boost::vecS, boost::vecS,
- boost::undirectedS,
- boost::property<vertex_filtration_t, double>,
- boost::property<edge_filtration_t, double>> Graph;
+
Graph g(3);
// filtration value 0 everywhere
put(Gudhi::vertex_filtration_t(), g, 0, 0);
put(Gudhi::vertex_filtration_t(), g, 1, 0);
put(Gudhi::vertex_filtration_t(), g, 2, 0);
// vertices don't always occur in sorted order
- add_edge(0, 1, 0, g);
- add_edge(2, 1, 0, g);
- add_edge(2, 0, 0, g);
+ add_edge(0, 1, 1.1, g);
+ add_edge(2, 0, 2.2, g);
+ add_edge(2, 1, 3.3, g);
- typedef Simplex_tree<> typeST;
- typeST st1;
+ Simplex_tree<> st1;
st1.insert_graph(g);
BOOST_CHECK(st1.num_simplices() == 6);
// edges can have multiplicity in the graph unless we replace the first vecS with (hash_)setS
- add_edge(1, 0, 0, g);
- add_edge(1, 2, 0, g);
- add_edge(0, 2, 0, g);
- add_edge(0, 2, 0, g);
- typeST st2;
+ add_edge(1, 0, 1.1, g);
+ add_edge(1, 2, 3.3, g);
+ add_edge(0, 2, 2.2, g);
+ add_edge(0, 1, 1.1, g);
+ add_edge(2, 1, 3.3, g);
+ add_edge(2, 0, 2.2, g);
+ Simplex_tree<> st2;
st2.insert_graph(g);
BOOST_CHECK(st2.num_simplices() == 6);
+
+ std::cout << "st1 is" << std::endl;
+ std::cout << st1 << std::endl;
+
+ std::cout << "st2 is" << std::endl;
+ std::cout << st2 << std::endl;
+
+ BOOST_CHECK(st1 == st2);
}
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/example/example_basic.cpp b/src/Tangential_complex/example/example_basic.cpp
index 4f2b859e..ab35edf0 100644
--- a/src/Tangential_complex/example/example_basic.cpp
+++ b/src/Tangential_complex/example/example_basic.cpp
@@ -1,5 +1,7 @@
#include <gudhi/Tangential_complex.h>
#include <gudhi/sparsify_point_set.h>
+//#include <gudhi/Fake_simplex_tree.h>
+
#include <CGAL/Epick_d.h>
#include <CGAL/Random.h>
@@ -20,7 +22,7 @@ CGAL::Parallel_tag> TC;
int main(void) {
const int INTRINSIC_DIM = 2;
const int AMBIENT_DIM = 3;
- const int NUM_POINTS = 1000;
+ const int NUM_POINTS = 100;
Kernel k;
@@ -37,6 +39,7 @@ int main(void) {
// Export the TC into a Simplex_tree
Gudhi::Simplex_tree<> stree;
+ //Gudhi::Fake_simplex_tree stree;
tc.create_complex(stree);
// Display stats about inconsistencies
diff --git a/src/Tangential_complex/include/gudhi/Tangential_complex.h b/src/Tangential_complex/include/gudhi/Tangential_complex.h
index d1c846cf..4a78127c 100644
--- a/src/Tangential_complex/include/gudhi/Tangential_complex.h
+++ b/src/Tangential_complex/include/gudhi/Tangential_complex.h
@@ -30,6 +30,7 @@
#include <gudhi/console_color.h>
#include <gudhi/Clock.h>
#include <gudhi/Simplex_tree.h>
+#include <gudhi/Debug_utils.h>
#include <CGAL/Default.h>
#include <CGAL/Dimension.h>
@@ -321,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. "
@@ -356,7 +361,7 @@ class Tangential_complex {
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_points.size()), Compute_tangent_triangulation(*this));
} else {
#endif // GUDHI_USE_TBB
- // Sequential
+ // Sequential
for (std::size_t i = 0; i < m_points.size(); ++i) compute_tangent_triangulation(i);
#ifdef GUDHI_USE_TBB
}
@@ -629,12 +634,12 @@ class Tangential_complex {
// Don't export infinite cells
if (!export_infinite_simplices && is_infinite(c)) continue;
- if (!export_inconsistent_simplices && !is_simplex_consistent(c)) continue;
-
if (static_cast<int>(c.size()) > max_dim) max_dim = static_cast<int>(c.size());
// Add the missing center vertex
c.insert(idx);
+ if (!export_inconsistent_simplices && !is_simplex_consistent(c)) continue;
+
// Try to insert the simplex
bool inserted = tree.insert_simplex_and_subfaces(c).second;
@@ -689,6 +694,10 @@ class Tangential_complex {
// Don't export infinite cells
if (!export_infinite_simplices && is_infinite(c)) continue;
+ if (static_cast<int>(c.size()) > max_dim) max_dim = static_cast<int>(c.size());
+ // Add the missing center vertex
+ c.insert(idx);
+
if (!export_inconsistent_simplices && !is_simplex_consistent(c)) continue;
// Unusual simplex dim?
@@ -701,10 +710,6 @@ class Tangential_complex {
check_lower_and_higher_dim_simplices = 1;
}
- if (static_cast<int>(c.size()) > max_dim) max_dim = static_cast<int>(c.size());
- // Add the missing center vertex
- c.insert(idx);
-
// Try to insert the simplex
bool added = complex.add_simplex(c, check_lower_and_higher_dim_simplices == 1);
@@ -800,7 +805,7 @@ class Tangential_complex {
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_points.size()), Compute_tangent_triangulation(*this));
} else {
#endif // GUDHI_USE_TBB
- // Sequential
+ // Sequential
for (std::size_t i = 0; i < m_points.size(); ++i) compute_tangent_triangulation(i);
#ifdef GUDHI_USE_TBB
}
@@ -835,7 +840,7 @@ class Tangential_complex {
Refresh_tangent_triangulation(*this, updated_pts_ds));
} else {
#endif // GUDHI_USE_TBB
- // Sequential
+ // Sequential
for (std::size_t i = 0; i < m_points.size(); ++i) refresh_tangent_triangulation(i, updated_pts_ds);
#ifdef GUDHI_USE_TBB
}
@@ -983,10 +988,9 @@ class Tangential_complex {
// of the sphere "star sphere" centered at "center_vertex"
// and which contains all the
// circumspheres of the star of "center_vertex"
- boost::optional<FT> squared_star_sphere_radius_plus_margin = boost::make_optional(false, FT());
- // This is the strange way boost is recommending to get rid of "may be used uninitialized in this function".
- // Former code was :
- // boost::optional<FT> squared_star_sphere_radius_plus_margin;
+ // If th the m_max_squared_edge_length is set the maximal radius of the "star sphere"
+ // is at most square root of m_max_squared_edge_length
+ boost::optional<FT> squared_star_sphere_radius_plus_margin = m_max_squared_edge_length;
// Insert points until we find a point which is outside "star sphere"
for (auto nn_it = ins_range.begin(); nn_it != ins_range.end(); ++nn_it) {
@@ -999,10 +1003,16 @@ class Tangential_complex {
Point neighbor_pt;
FT neighbor_weight;
compute_perturbed_weighted_point(neighbor_point_idx, neighbor_pt, neighbor_weight);
-
+ GUDHI_CHECK(!m_max_squared_edge_length ||
+ squared_star_sphere_radius_plus_margin.value() <= m_max_squared_edge_length.value(),
+ std::invalid_argument("Tangential_complex::compute_star - set a bigger value with set_max_squared_edge_length."));
if (squared_star_sphere_radius_plus_margin &&
- k_sqdist(center_pt, neighbor_pt) > *squared_star_sphere_radius_plus_margin)
+ k_sqdist(center_pt, neighbor_pt) > squared_star_sphere_radius_plus_margin.value()) {
+ GUDHI_CHECK(triangulation.current_dimension() >= tangent_space_dim,
+ std::invalid_argument("Tangential_complex::compute_star - Dimension of the star is only " + \
+ std::to_string(triangulation.current_dimension())));
break;
+ }
Tr_point proj_pt = project_point_and_compute_weight(neighbor_pt, neighbor_weight, tsb, local_tr_traits);
@@ -1044,7 +1054,7 @@ class Tangential_complex {
FT sq_power_sphere_diam = 4 * point_weight(c);
if (!squared_star_sphere_radius_plus_margin ||
- sq_power_sphere_diam > *squared_star_sphere_radius_plus_margin) {
+ sq_power_sphere_diam > squared_star_sphere_radius_plus_margin.value()) {
squared_star_sphere_radius_plus_margin = sq_power_sphere_diam;
}
}
@@ -1055,12 +1065,22 @@ class Tangential_complex {
if (squared_star_sphere_radius_plus_margin) {
// "2*m_last_max_perturb" because both points can be perturbed
squared_star_sphere_radius_plus_margin =
- CGAL::square(std::sqrt(*squared_star_sphere_radius_plus_margin) + 2 * m_last_max_perturb);
+ CGAL::square(std::sqrt(squared_star_sphere_radius_plus_margin.value()) + 2 * m_last_max_perturb);
+
+ // Reduce the square radius to m_max_squared_edge_length if necessary
+ if (m_max_squared_edge_length && squared_star_sphere_radius_plus_margin.value() > m_max_squared_edge_length.value()) {
+ squared_star_sphere_radius_plus_margin = m_max_squared_edge_length.value();
+ }
// Save it in `m_squared_star_spheres_radii_incl_margin`
- m_squared_star_spheres_radii_incl_margin[i] = *squared_star_sphere_radius_plus_margin;
+ m_squared_star_spheres_radii_incl_margin[i] = squared_star_sphere_radius_plus_margin.value();
} else {
- m_squared_star_spheres_radii_incl_margin[i] = FT(-1);
+ if (m_max_squared_edge_length) {
+ squared_star_sphere_radius_plus_margin = m_max_squared_edge_length.value();
+ m_squared_star_spheres_radii_incl_margin[i] = m_max_squared_edge_length.value();
+ } else {
+ m_squared_star_spheres_radii_incl_margin[i] = FT(-1);
+ }
}
}
}
@@ -1968,6 +1988,15 @@ 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:
const K m_k;
const int m_intrinsic_dim;
@@ -1993,6 +2022,7 @@ class Tangential_complex {
// and their center vertex
Stars_container m_stars;
std::vector<FT> m_squared_star_spheres_radii_incl_margin;
+ boost::optional<FT> m_max_squared_edge_length;
#ifdef GUDHI_TC_USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM
Points m_points_for_tse;
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/Toplex_map/benchmark/CMakeLists.txt b/src/Toplex_map/benchmark/CMakeLists.txt
new file mode 100644
index 00000000..2d58a156
--- /dev/null
+++ b/src/Toplex_map/benchmark/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(Toplex_map_benchmark)
+
+add_executable(Toplex_map_benchmark benchmark_tm.cpp)
diff --git a/src/Toplex_map/benchmark/benchmark_tm.cpp b/src/Toplex_map/benchmark/benchmark_tm.cpp
new file mode 100644
index 00000000..eedb442b
--- /dev/null
+++ b/src/Toplex_map/benchmark/benchmark_tm.cpp
@@ -0,0 +1,137 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <random>
+#include <chrono>
+
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Toplex_map.h>
+#include <gudhi/Lazy_toplex_map.h>
+
+using namespace Gudhi;
+
+typedef Toplex_map::Simplex Simplex;
+typedef Toplex_map::Vertex Vertex;
+typedef std::pair<Simplex_tree<>::Simplex_handle, bool> typePairSimplexBool;
+
+class ST_wrapper {
+ public:
+ void insert_simplex(const Simplex& tau) {
+ /*std::cout << "insert_simplex - " << simplexTree.num_simplices() << " - ";
+ for (auto v : tau)
+ std::cout << v << ", ";
+ std::cout << std::endl;
+ */
+ simplexTree.insert_simplex_and_subfaces(tau);
+ }
+
+ bool membership(const Simplex& tau) { return simplexTree.find(tau) != simplexTree.null_simplex(); }
+
+ Vertex contraction(const Vertex x, const Vertex y) {
+ // TODO (VR): edge contraction is not yet available for Simplex_tree
+ return y;
+ }
+
+ std::size_t num_maximal_simplices() { return simplexTree.num_simplices(); }
+
+ private:
+ Simplex_tree<> simplexTree;
+ void erase_max(const Simplex& sigma) {
+ if (membership(sigma)) simplexTree.remove_maximal_simplex(simplexTree.find(sigma));
+ }
+};
+
+int n = 300;
+
+int nb_insert_simplex1 = 3000;
+int nb_membership1 = 4000;
+int nb_contraction = 300;
+int nb_insert_simplex2 = 3000;
+int nb_membership2 = 400000;
+
+Simplex random_simplex(int n, std::size_t d) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<std::size_t> dis(1, n);
+ Simplex s;
+ while (s.size() < d) s.insert(dis(gen));
+ return s;
+}
+
+std::vector<Simplex> r_vector_simplices(int n, int max_d, int m) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<std::size_t> dis(1, max_d);
+ std::vector<Simplex> v;
+ for (int i = 0; i < m; i++) v.push_back(random_simplex(n, dis(gen)));
+ return v;
+}
+
+template <typename complex_type>
+void chrono(int n, int d) {
+ complex_type K;
+ std::vector<Simplex> simplices_insert_simplex1 = r_vector_simplices(n, d, nb_insert_simplex1);
+ std::vector<Simplex> simplices_membership1 = r_vector_simplices(n, d, nb_membership1);
+ std::vector<Simplex> simplices_insert_simplex2 = r_vector_simplices(n - 2 * nb_contraction, d, nb_insert_simplex2);
+ std::vector<Simplex> simplices_membership2 = r_vector_simplices(n - 2 * nb_contraction, d, nb_membership2);
+ std::chrono::time_point<std::chrono::system_clock> start, end;
+
+ for (const Simplex& s : simplices_insert_simplex1) K.insert_simplex(s);
+
+ for (const Simplex& s : simplices_membership1) K.membership(s);
+
+ start = std::chrono::system_clock::now();
+ for (int i = 1; i <= nb_contraction; i++) K.contraction(n - 2 * i, n - 2 * i - 1);
+ end = std::chrono::system_clock::now();
+ auto c3 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+
+ start = std::chrono::system_clock::now();
+ for (const Simplex& s : simplices_insert_simplex2) K.insert_simplex(s);
+ end = std::chrono::system_clock::now();
+ auto c1 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+
+ start = std::chrono::system_clock::now();
+ for (const Simplex& s : simplices_membership2) K.membership(s);
+ end = std::chrono::system_clock::now();
+ auto c2 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+
+ if (c3 > 0)
+ std::cout << c1 << "\t \t" << c2 << "\t \t" << c3 << "\t \t" << K.num_maximal_simplices() << std::endl;
+ else
+ std::cout << c1 << "\t \t" << c2 << "\t \tN/A\t \t" << K.num_maximal_simplices() << std::endl;
+}
+
+int main() {
+ for (int d = 5; d <= 40; d += 5) {
+ std::cout << "d=" << d << " \t Insertions \t Membership \t Contractions \t Size" << std::endl;
+ std::cout << "T Map \t \t";
+ chrono<Toplex_map>(n, d);
+ std::cout << "Lazy \t \t";
+ chrono<Lazy_toplex_map>(n, d);
+ if (d <= 15) {
+ std::cout << "ST \t \t";
+ chrono<ST_wrapper>(n, d);
+ }
+ std::cout << std::endl;
+ }
+}
diff --git a/src/Toplex_map/doc/Intro_Toplex_map.h b/src/Toplex_map/doc/Intro_Toplex_map.h
new file mode 100644
index 00000000..a925dc2b
--- /dev/null
+++ b/src/Toplex_map/doc/Intro_Toplex_map.h
@@ -0,0 +1,61 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2017 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DOC_TOPLEX_MAP_INTRO_TOPLEX_MAP_H_
+#define DOC_TOPLEX_MAP_INTRO_TOPLEX_MAP_H_
+
+// needs namespace for Doxygen to link on classes
+namespace Gudhi {
+
+/** \defgroup toplex_map Toplex Map
+ *
+ * \author Fran&ccedil;ois Godi
+ * @{
+ *
+ * \section toplexmapdefinition Definition
+ *
+ * A Toplex_map is a data structure to represent and store a simplicial complex. A "toplex" is the contraction of
+ * "top-simplex", also known as a maximal simplex. The plural form of "toplex" will be called "toplices".
+ *
+ * Let's consider a simplicial complex, denote by \f$d\f$ its dimension and by \f$k\f$ its number of maximal simplices.
+ * Furthermore, denote by \f$\Gamma_0\f$ the maximal number of toplices, i.e. maximal simplices, that contain a same
+ * vertex.
+ *
+ * The goal of the Toplex Map is both to represent the complex in optimal O(kd) space and to provide fast standard
+ * operations such as : insertion, removal, contraction of an edge, collapses and membership of a simplex. The time
+ * needed for these operation is linear or quadratic in \f$\Gamma_0\f$ and \f$d\f$.
+ *
+ * Toplex map is composed firstly of a raw storage of toplices and secondly of a map which associate any vertex to a
+ * set of pointers toward all toplices containing this vertex. The data structure is described in
+ * \cite boissonnat_et_al:LIPIcs:2015:5098 (aka. Simplex Array List or SAL).
+ *
+ * \image html map.png
+ *
+ * The performances are a lot better than the `Simplex_tree` as soon you use maximal simplices and not simplices
+ * (cf. \cite DBLP:journals/corr/BoissonnatS16 ).
+ *
+ */
+/** @} */ // end defgroup toplex_map
+
+} // namespace Gudhi
+
+#endif // DOC_TOPLEX_MAP_INTRO_TOPLEX_MAP_H_
diff --git a/src/Toplex_map/doc/map.png b/src/Toplex_map/doc/map.png
new file mode 100644
index 00000000..0f9cde2b
--- /dev/null
+++ b/src/Toplex_map/doc/map.png
Binary files differ
diff --git a/src/Toplex_map/example/CMakeLists.txt b/src/Toplex_map/example/CMakeLists.txt
new file mode 100644
index 00000000..1d54fe87
--- /dev/null
+++ b/src/Toplex_map/example/CMakeLists.txt
@@ -0,0 +1,4 @@
+project(Toplex_map_examples)
+
+add_executable(Toplex_map_example_simple_toplex_map simple_toplex_map.cpp)
+add_test(NAME Toplex_map_example_simple_toplex_map COMMAND $<TARGET_FILE:Toplex_map_example_simple_toplex_map>)
diff --git a/src/Toplex_map/example/simple_toplex_map.cpp b/src/Toplex_map/example/simple_toplex_map.cpp
new file mode 100644
index 00000000..e1c12ed6
--- /dev/null
+++ b/src/Toplex_map/example/simple_toplex_map.cpp
@@ -0,0 +1,165 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2018
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gudhi/Toplex_map.h>
+
+#include <iostream>
+#include <utility> // for pair
+#include <vector>
+#include <cassert>
+
+int main(int argc, char* const argv[]) {
+ using Simplex = Gudhi::Toplex_map::Simplex;
+ Simplex sigma1 = {1, 2, 3};
+ Simplex sigma2 = {2, 3, 4, 5};
+
+ Gudhi::Toplex_map tm;
+ tm.insert_simplex(sigma1);
+ tm.insert_simplex(sigma2);
+
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* /X\5/ */
+ /* o---o */
+ /* 1 3 */
+
+ std::cout << "num max simplices = " << tm.num_maximal_simplices() << " - num vertices = " << tm.num_vertices()
+ << std::endl;
+
+ // Browse maximal cofaces
+ Simplex sigma3 = {2, 3};
+ std::cout << "Maximal cofaces of {2, 3} are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_cofaces(sigma3, 2)) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ Simplex sigma4 = {1, 3};
+ assert(tm.membership(sigma4));
+
+ Gudhi::Toplex_map::Vertex v = tm.contraction(1, 3);
+ std::cout << "After contraction(1, 3) - " << v << std::endl;
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* \5/ */
+ /* o */
+ /* 3 */
+ std::cout << "num max simplices = " << tm.num_maximal_simplices() << " - num vertices = " << tm.num_vertices()
+ << std::endl;
+
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ Simplex sigma5 = {3, 4};
+ assert(tm.membership(sigma5));
+
+ v = tm.contraction(3, 4);
+ std::cout << "After contraction(3, 4) - " << v << std::endl;
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* \X/ */
+ /* o */
+ /* 5 */
+ std::cout << "num max simplices = " << tm.num_maximal_simplices() << " - num vertices = " << tm.num_vertices()
+ << std::endl;
+
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ tm.insert_simplex(sigma1);
+ tm.insert_simplex(sigma2);
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* /X\5/ */
+ /* o---o */
+ /* 1 3 */
+ tm.remove_simplex(sigma1);
+
+ std::cout << "After remove_simplex(1, 2, 3)" << std::endl;
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* / \5/ */
+ /* o---o */
+ /* 1 3 */
+ std::cout << "num max simplices = " << tm.num_maximal_simplices() << " - num vertices = " << tm.num_vertices()
+ << std::endl;
+
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ tm.remove_vertex(1);
+
+ std::cout << "After remove_vertex(1)" << std::endl;
+ /* Simplex is: */
+ /* 2 4 */
+ /* o---o */
+ /* \5/ */
+ /* o */
+ /* 3 */
+ std::cout << "num max simplices = " << tm.num_maximal_simplices() << " - num vertices = " << tm.num_vertices()
+ << std::endl;
+
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ }
+
+ return 0;
+}
diff --git a/src/Toplex_map/include/gudhi/Lazy_toplex_map.h b/src/Toplex_map/include/gudhi/Lazy_toplex_map.h
new file mode 100644
index 00000000..d7bccdff
--- /dev/null
+++ b/src/Toplex_map/include/gudhi/Lazy_toplex_map.h
@@ -0,0 +1,271 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LAZY_TOPLEX_MAP_H
+#define LAZY_TOPLEX_MAP_H
+
+#include <gudhi/Toplex_map.h>
+#include <boost/heap/fibonacci_heap.hpp>
+
+namespace Gudhi {
+
+/**
+ * \brief Lazy toplex map data structure for representing unfiltered simplicial complexes.
+ *
+ * \details A Toplex_map is an unordered map from vertices to maximal simplices (aka. toplices).
+ * The lazy version is not always up to date as it requires clean operation in order to be.
+ *
+ * \ingroup toplex_map */
+class Lazy_toplex_map {
+ public:
+ /** Vertex is the type of vertices. */
+ using Vertex = Toplex_map::Vertex;
+
+ /** Simplex is the type of simplices. */
+ using Simplex = Toplex_map::Simplex;
+
+ /** The type of the pointers to maximal simplices. */
+ using Simplex_ptr = Toplex_map::Simplex_ptr;
+
+ /** The type of the sets of Simplex_ptr. */
+ using Simplex_ptr_set = Toplex_map::Simplex_ptr_set;
+
+ /** Adds the given simplex to the complex.
+ * The simplex must not be in the complex already, and it must not contain one of the current toplices. */
+ template <typename Input_vertex_range>
+ void insert_independent_simplex(const Input_vertex_range &vertex_range);
+
+ /** \brief Adds the given simplex to the complex.
+ * Nothing happens if the simplex is already in the complex (i.e. it is a face of one of the toplices). */
+ template <typename Input_vertex_range>
+ bool insert_simplex(const Input_vertex_range &vertex_range);
+
+ /** \brief Removes the given simplex and its cofaces from the complex.
+ * Its faces are kept inside. */
+ template <typename Input_vertex_range>
+ void remove_simplex(const Input_vertex_range &vertex_range);
+
+ /** Does a simplex belong to the complex ? */
+ template <typename Input_vertex_range>
+ bool membership(const Input_vertex_range &vertex_range);
+
+ /** Do all the facets of a simplex belong to the complex ? */
+ template <typename Input_vertex_range>
+ bool all_facets_inside(const Input_vertex_range &vertex_range);
+
+ /** Contracts one edge in the complex.
+ * The edge has to verify the link condition if you want to preserve topology.
+ * Returns the remaining vertex. */
+ Vertex contraction(const Vertex x, const Vertex y);
+
+ /** \brief Number of maximal simplices. */
+ std::size_t num_maximal_simplices() const { return size; }
+
+ /** \brief Number of vertices. */
+ std::size_t num_vertices() const { return t0.size(); }
+
+ private:
+ template <typename Input_vertex_range>
+ void erase_max(const Input_vertex_range &vertex_range);
+ template <typename Input_vertex_range>
+ Vertex best_index(const Input_vertex_range &vertex_range);
+ void clean(const Vertex v);
+
+ std::unordered_map<Vertex, std::size_t> gamma0_lbounds;
+
+ std::unordered_map<Vertex, Simplex_ptr_set> t0;
+ bool empty_toplex = true; // Is the empty simplex a toplex ?
+
+ typedef boost::heap::fibonacci_heap<std::pair<std::size_t, Vertex>> PriorityQueue;
+ PriorityQueue cleaning_priority;
+ std::unordered_map<Vertex, PriorityQueue::handle_type> cp_handles;
+
+ std::size_t get_gamma0_lbound(const Vertex v) const;
+
+ std::size_t size_lbound = 0;
+ std::size_t size = 0;
+
+ const double ALPHA = 4; // time
+ const double BETTA = 8; // memory
+};
+
+template <typename Input_vertex_range>
+void Lazy_toplex_map::insert_independent_simplex(const Input_vertex_range &vertex_range) {
+ for (const Vertex &v : vertex_range)
+ if (!gamma0_lbounds.count(v))
+ gamma0_lbounds.emplace(v, 1);
+ else
+ gamma0_lbounds[v]++;
+ size_lbound++;
+ insert_simplex(vertex_range);
+}
+
+template <typename Input_vertex_range>
+bool Lazy_toplex_map::insert_simplex(const Input_vertex_range &vertex_range) {
+ Simplex sigma(vertex_range.begin(), vertex_range.end());
+ // Check empty face management
+ empty_toplex = (sigma.size() == 0);
+ Simplex_ptr sptr = std::make_shared<Simplex>(sigma);
+ bool inserted = false;
+ for (const Vertex &v : sigma) {
+ if (!t0.count(v)) {
+ t0.emplace(v, Simplex_ptr_set());
+ auto v_handle = cleaning_priority.push(std::make_pair(0, v));
+ cp_handles.emplace(v, v_handle);
+ }
+ inserted = t0.at(v).emplace(sptr).second;
+ cleaning_priority.update(cp_handles.at(v), std::make_pair(t0.at(v).size() - get_gamma0_lbound(v), v));
+ }
+ if (inserted) size++;
+ if (size > (size_lbound + 1) * BETTA) clean(cleaning_priority.top().second);
+ return inserted;
+}
+
+template <typename Input_vertex_range>
+void Lazy_toplex_map::remove_simplex(const Input_vertex_range &vertex_range) {
+ if (vertex_range.begin() == vertex_range.end()) {
+ t0.clear();
+ gamma0_lbounds.clear();
+ cleaning_priority.clear();
+ size_lbound = 0;
+ size = 0;
+ empty_toplex = false;
+ } else {
+ const Vertex &v = best_index(vertex_range);
+ // Copy constructor needed because the set is modified
+ if (t0.count(v))
+ for (const Simplex_ptr &sptr : Simplex_ptr_set(t0.at(v)))
+ if (included(vertex_range, *sptr)) {
+ erase_max(*sptr);
+ for (const Simplex &f : facets(vertex_range)) insert_independent_simplex(f);
+ }
+ }
+}
+
+template <typename Input_vertex_range>
+bool Lazy_toplex_map::membership(const Input_vertex_range &vertex_range) {
+ if (t0.size() == 0 && !empty_toplex) return false; // empty complex
+ if (vertex_range.begin() == vertex_range.end()) return true; // empty query simplex
+ Vertex v = best_index(vertex_range);
+ if (!t0.count(v)) return false;
+ for (const Simplex_ptr &sptr : t0.at(v))
+ if (included(vertex_range, *sptr)) return true;
+ return false;
+}
+
+template <typename Input_vertex_range>
+bool Lazy_toplex_map::all_facets_inside(const Input_vertex_range &vertex_range) {
+ Simplex sigma(vertex_range.begin(), vertex_range.end());
+ Vertex v = best_index(sigma);
+ if (!t0.count(v)) return false;
+ Simplex f = sigma;
+ f.erase(v);
+ if (!membership(f)) return false;
+ std::unordered_set<Vertex> facets_inside;
+ for (const Simplex_ptr &sptr : t0.at(v))
+ for (const Vertex &w : sigma) {
+ f = sigma;
+ f.erase(w);
+ if (included(f, *sptr)) facets_inside.insert(w);
+ }
+ return facets_inside.size() == sigma.size() - 1;
+}
+
+/* Returns the remaining vertex */
+Lazy_toplex_map::Vertex Lazy_toplex_map::contraction(const Vertex x, const Vertex y) {
+ if (!t0.count(x)) return y;
+ if (!t0.count(y)) return x;
+ Vertex k, d;
+ if (t0.at(x).size() > t0.at(y).size())
+ k = x, d = y;
+ else
+ k = y, d = x;
+ // Copy constructor needed because the set is modified
+ for (const Simplex_ptr &sptr : Simplex_ptr_set(t0.at(d))) {
+ Simplex sigma(*sptr);
+ erase_max(sigma);
+ sigma.erase(d);
+ sigma.insert(k);
+ insert_simplex(sigma);
+ }
+ t0.erase(d);
+ return k;
+}
+
+/* No facets insert_simplexed */
+template <typename Input_vertex_range>
+inline void Lazy_toplex_map::erase_max(const Input_vertex_range &vertex_range) {
+ Simplex sigma(vertex_range.begin(), vertex_range.end());
+ empty_toplex = false;
+ Simplex_ptr sptr = std::make_shared<Simplex>(sigma);
+ bool erased = false;
+ for (const Vertex &v : sigma) {
+ erased = t0.at(v).erase(sptr) > 0;
+ if (t0.at(v).size() == 0) t0.erase(v);
+ }
+ if (erased) size--;
+}
+
+template <typename Input_vertex_range>
+Lazy_toplex_map::Vertex Lazy_toplex_map::best_index(const Input_vertex_range &vertex_range) {
+ Simplex tau(vertex_range.begin(), vertex_range.end());
+ std::size_t min = std::numeric_limits<size_t>::max();
+ Vertex arg_min = -1;
+ for (const Vertex &v : tau)
+ if (!t0.count(v))
+ return v;
+ else if (t0.at(v).size() < min)
+ min = t0.at(v).size(), arg_min = v;
+ if (min > ALPHA * get_gamma0_lbound(arg_min)) clean(arg_min);
+ return arg_min;
+}
+
+std::size_t Lazy_toplex_map::get_gamma0_lbound(const Vertex v) const {
+ return gamma0_lbounds.count(v) ? gamma0_lbounds.at(v) : 0;
+}
+
+void Lazy_toplex_map::clean(const Vertex v) {
+ Toplex_map toplices;
+ std::unordered_map<int, std::vector<Simplex>> dsorted_simplices;
+ std::size_t max_dim = 0;
+ for (const Simplex_ptr &sptr : Simplex_ptr_set(t0.at(v))) {
+ if (sptr->size() > max_dim) {
+ for (std::size_t d = max_dim + 1; d <= sptr->size(); d++) dsorted_simplices.emplace(d, std::vector<Simplex>());
+ max_dim = sptr->size();
+ }
+ dsorted_simplices[sptr->size()].emplace_back(*sptr);
+ erase_max(*sptr);
+ }
+ for (std::size_t d = max_dim; d >= 1; d--)
+ for (const Simplex &s : dsorted_simplices.at(d))
+ if (!toplices.membership(s)) toplices.insert_independent_simplex(s);
+ Simplex sv;
+ sv.insert(v);
+ auto clean_cofaces = toplices.maximal_cofaces(sv);
+ size_lbound = size_lbound - get_gamma0_lbound(v) + clean_cofaces.size();
+ gamma0_lbounds[v] = clean_cofaces.size();
+ for (const Simplex_ptr &sptr : clean_cofaces) insert_simplex(*sptr);
+}
+
+} // namespace Gudhi
+
+#endif /* LAZY_TOPLEX_MAP_H */
diff --git a/src/Toplex_map/include/gudhi/Toplex_map.h b/src/Toplex_map/include/gudhi/Toplex_map.h
new file mode 100644
index 00000000..4dc2331f
--- /dev/null
+++ b/src/Toplex_map/include/gudhi/Toplex_map.h
@@ -0,0 +1,338 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TOPLEX_MAP_H
+#define TOPLEX_MAP_H
+
+#include <vector>
+#include <set>
+#include <unordered_set>
+#include <unordered_map>
+#include <memory>
+#include <limits>
+
+namespace Gudhi {
+
+/**
+ * \brief Toplex map data structure for representing unfiltered simplicial complexes.
+ *
+ * \details A Toplex_map is an unordered map from vertices to maximal simplices (aka. toplices).
+ *
+ * \ingroup toplex_map */
+class Toplex_map {
+ public:
+ /** Vertex is the type of vertices. */
+ using Vertex = std::size_t;
+
+ /** Simplex is the type of simplices. */
+ using Simplex = std::set<Vertex>;
+
+ /** The type of the pointers to maximal simplices. */
+ using Simplex_ptr = std::shared_ptr<Toplex_map::Simplex>;
+
+ struct Sptr_hash {
+ std::size_t operator()(const Toplex_map::Simplex_ptr& s) const;
+ };
+
+ struct Sptr_equal {
+ std::size_t operator()(const Toplex_map::Simplex_ptr& a, const Toplex_map::Simplex_ptr& b) const;
+ };
+
+ /** The type of the sets of Toplex_map::Simplex_ptr. */
+ using Simplex_ptr_set = std::unordered_set<Toplex_map::Simplex_ptr, Sptr_hash, Sptr_equal>;
+
+ /** \brief Adds the given simplex to the complex.
+ * Nothing happens if the simplex is already in the complex (i.e. it is a face of one of the toplices). */
+ template <typename Input_vertex_range>
+ void insert_simplex(const Input_vertex_range& vertex_range);
+
+ /** \brief Removes the given simplex and its cofaces from the complex.
+ * Its faces are kept inside. */
+ template <typename Input_vertex_range>
+ void remove_simplex(const Input_vertex_range& vertex_range);
+
+ /** Does a simplex belong to the complex ? */
+ template <typename Input_vertex_range>
+ bool membership(const Input_vertex_range& vertex_range) const;
+
+ /** Does a simplex is a toplex ? */
+ template <typename Input_vertex_range>
+ bool maximality(const Input_vertex_range& vertex_range) const;
+
+ /** Gives a set of pointers to the maximal cofaces of a simplex.
+ * Gives all the toplices if given the empty simplex.
+ * Gives not more than max_number maximal cofaces if max_number is strictly positive. */
+ template <typename Input_vertex_range>
+ Toplex_map::Simplex_ptr_set maximal_cofaces(const Input_vertex_range& vertex_range,
+ const std::size_t max_number = 0) const;
+
+ /** Gives a set of pointers to the maximal simplices.
+ * Gives not more than max_number maximal cofaces if max_number is strictly positive. */
+ Toplex_map::Simplex_ptr_set maximal_simplices(const std::size_t max_number = 0) const {
+ return maximal_cofaces(Simplex(), max_number);
+ }
+
+ /** Contracts one edge in the complex.
+ * The edge has to verify the link condition if you want to preserve topology.
+ * Returns the remaining vertex. */
+ Vertex contraction(const Vertex x, const Vertex y);
+
+ /** Remove the vertex and all its cofaces from the complex. */
+ void remove_vertex(const Vertex x);
+
+ /** \brief Number of maximal simplices. */
+ std::size_t num_maximal_simplices() const { return maximal_simplices().size(); }
+
+ /** \brief Number of vertices. */
+ std::size_t num_vertices() const { return t0.size(); }
+
+ std::set<Vertex> unitary_collapse(const Vertex k, const Vertex d);
+
+ /** Adds the given simplex to the complex.
+ * The simplex must not be in the complex already, and it must not contain one of the current toplices. */
+ template <typename Input_vertex_range>
+ void insert_independent_simplex(const Input_vertex_range& vertex_range);
+
+ protected:
+ /** \internal Gives an index in order to look for a simplex quickly. */
+ template <typename Input_vertex_range>
+ Vertex best_index(const Input_vertex_range& vertex_range) const;
+
+ /** \internal The map from vertices to toplices */
+ std::unordered_map<Vertex, Toplex_map::Simplex_ptr_set> t0;
+
+ const Vertex VERTEX_UPPER_BOUND = std::numeric_limits<Vertex>::max();
+
+ /** \internal Removes a toplex without adding facets after. */
+ void erase_maximal(const Toplex_map::Simplex_ptr& sptr);
+};
+
+// Pointers are also used as key in the hash sets.
+template <typename Input_vertex_range>
+Toplex_map::Simplex_ptr get_key(const Input_vertex_range& vertex_range);
+
+// Is the first simplex a face of the second ?
+template <typename Input_vertex_range1, typename Input_vertex_range2>
+bool included(const Input_vertex_range1& vertex_range1, const Input_vertex_range2& vertex_range2);
+
+// All the facets of the given simplex.
+template <typename Input_vertex_range>
+std::vector<Toplex_map::Simplex> facets(const Input_vertex_range& vertex_range);
+
+template <typename Input_vertex_range>
+void Toplex_map::insert_simplex(const Input_vertex_range& vertex_range) {
+ if (membership(vertex_range)) return;
+ bool replace_facets = true;
+ for (const Toplex_map::Simplex& facet : facets(vertex_range))
+ if (!maximality(facet)) {
+ replace_facets = false;
+ break;
+ }
+ if (replace_facets)
+ for (const Toplex_map::Simplex& facet : facets(vertex_range)) erase_maximal(get_key(facet));
+ else
+ for (const Vertex& v : vertex_range)
+ if (t0.count(v))
+ for (const Toplex_map::Simplex_ptr& fptr : Simplex_ptr_set(t0.at(v)))
+ // Copy constructor needed because the set is modified
+ if (included(*fptr, vertex_range)) erase_maximal(fptr);
+ // We erase all the maximal faces of the simplex
+ insert_independent_simplex(vertex_range);
+}
+
+template <typename Input_vertex_range>
+void Toplex_map::remove_simplex(const Input_vertex_range& vertex_range) {
+ if (vertex_range.begin() == vertex_range.end()) t0.clear();
+ // Removal of the empty simplex means cleaning everything
+ else {
+ const Vertex& v = best_index(vertex_range);
+ if (t0.count(v))
+ for (const Toplex_map::Simplex_ptr& sptr : Simplex_ptr_set(t0.at(v)))
+ // Copy constructor needed because the set is modified
+ if (included(vertex_range, *sptr)) {
+ erase_maximal(sptr);
+ for (const Toplex_map::Simplex& f : facets(vertex_range))
+ if (!membership(f)) insert_independent_simplex(f);
+ // We add the facets which are new maximal simplices
+ }
+ }
+}
+
+template <typename Input_vertex_range>
+bool Toplex_map::membership(const Input_vertex_range& vertex_range) const {
+ if (t0.size() == 0) return false;
+ const Vertex& v = best_index(vertex_range);
+ if (!t0.count(v)) return false;
+ if (maximality(vertex_range)) return true;
+ for (const Toplex_map::Simplex_ptr& sptr : t0.at(v))
+ if (included(vertex_range, *sptr)) return true;
+ return false;
+}
+
+template <typename Input_vertex_range>
+bool Toplex_map::maximality(const Input_vertex_range& vertex_range) const {
+ const Vertex& v = best_index(vertex_range);
+ if (!t0.count(v)) return false;
+ return t0.at(v).count(get_key(vertex_range));
+}
+
+template <typename Input_vertex_range>
+Toplex_map::Simplex_ptr_set Toplex_map::maximal_cofaces(const Input_vertex_range& vertex_range,
+ const std::size_t max_number) const {
+ Simplex_ptr_set cofaces;
+ if (maximality(vertex_range))
+ cofaces.emplace(get_key(vertex_range));
+ else if (vertex_range.begin() == vertex_range.end())
+ for (const auto& kv : t0)
+ for (const Toplex_map::Simplex_ptr& sptr : kv.second) {
+ // kv.second is a Simplex_ptr_set
+ cofaces.emplace(sptr);
+ if (cofaces.size() == max_number) return cofaces;
+ }
+ else {
+ const Vertex& v = best_index(vertex_range);
+ if (t0.count(v))
+ for (const Toplex_map::Simplex_ptr& sptr : t0.at(v))
+ if (included(vertex_range, *sptr)) {
+ cofaces.emplace(sptr);
+ if (cofaces.size() == max_number) return cofaces;
+ }
+ }
+ return cofaces;
+}
+
+Toplex_map::Vertex Toplex_map::contraction(const Toplex_map::Vertex x, const Toplex_map::Vertex y) {
+ if (!t0.count(x)) return y;
+ if (!t0.count(y)) return x;
+ int k, d;
+ if (t0.at(x).size() > t0.at(y).size())
+ k = x, d = y;
+ else
+ k = y, d = x;
+ for (const Toplex_map::Simplex_ptr& sptr : Simplex_ptr_set(t0.at(d))) {
+ // Copy constructor needed because the set is modified
+ Simplex sigma(*sptr);
+ erase_maximal(sptr);
+ sigma.erase(d);
+ sigma.insert(k);
+ insert_simplex(sigma);
+ }
+ return k;
+}
+
+std::set<Toplex_map::Vertex> Toplex_map::unitary_collapse(const Toplex_map::Vertex k, const Toplex_map::Vertex d) {
+ Toplex_map::Simplex r;
+ for (const Toplex_map::Simplex_ptr& sptr : Simplex_ptr_set(t0.at(d))) {
+ // Copy constructor needed because the set is modified
+ Simplex sigma(*sptr);
+ erase_maximal(sptr);
+ sigma.erase(d);
+ for (const Vertex v : sigma) r.insert(v);
+ sigma.insert(k);
+ insert_simplex(sigma);
+ }
+ return r;
+}
+
+template <typename Input_vertex_range>
+void Toplex_map::insert_independent_simplex(const Input_vertex_range& vertex_range) {
+ auto key = get_key(vertex_range);
+ for (const Vertex& v : vertex_range) {
+ if (!t0.count(v)) t0.emplace(v, Simplex_ptr_set());
+ t0.at(v).emplace(key);
+ }
+}
+
+void Toplex_map::remove_vertex(const Toplex_map::Vertex x) {
+ for (const Toplex_map::Simplex_ptr& sptr : Simplex_ptr_set(t0.at(x))) {
+ Simplex sigma(*sptr);
+ erase_maximal(sptr);
+ sigma.erase(x);
+ insert_simplex(sigma);
+ }
+}
+
+inline void Toplex_map::erase_maximal(const Toplex_map::Simplex_ptr& sptr) {
+ Simplex sigma(*sptr);
+ if (sptr->size() == 0) sigma.insert(VERTEX_UPPER_BOUND);
+ for (const Vertex& v : sigma) {
+ t0.at(v).erase(sptr);
+ if (t0.at(v).size() == 0) t0.erase(v);
+ }
+}
+
+template <typename Input_vertex_range>
+Toplex_map::Vertex Toplex_map::best_index(const Input_vertex_range& vertex_range) const {
+ std::size_t min = std::numeric_limits<size_t>::max();
+ Vertex arg_min = VERTEX_UPPER_BOUND;
+ for (const Vertex& v : vertex_range)
+ if (!t0.count(v))
+ return v;
+ else if (t0.at(v).size() < min)
+ min = t0.at(v).size(), arg_min = v;
+ return arg_min;
+}
+
+std::size_t Toplex_map::Sptr_equal::operator()(const Toplex_map::Simplex_ptr& s1,
+ const Toplex_map::Simplex_ptr& s2) const {
+ if (s1->size() != s2->size()) return false;
+ return included(*s1, *s2);
+ // inclusion tests equality for same size simplices
+}
+
+std::size_t Toplex_map::Sptr_hash::operator()(const Toplex_map::Simplex_ptr& s) const {
+ std::hash<double> h_f;
+ // double hash works better than int hash
+ size_t h = 0;
+ for (const Vertex& v : *s) h += h_f(static_cast<double>(v));
+ return h;
+}
+
+template <typename Input_vertex_range>
+Toplex_map::Simplex_ptr get_key(const Input_vertex_range& vertex_range) {
+ Toplex_map::Simplex s(vertex_range.begin(), vertex_range.end());
+ return std::make_shared<Toplex_map::Simplex>(s);
+}
+
+template <typename Input_vertex_range1, typename Input_vertex_range2>
+bool included(const Input_vertex_range1& vertex_range1, const Input_vertex_range2& vertex_range2) {
+ Toplex_map::Simplex s2(vertex_range2.begin(), vertex_range2.end());
+ for (const Toplex_map::Vertex& v : vertex_range1)
+ if (!s2.count(v)) return false;
+ return true;
+}
+
+template <typename Input_vertex_range>
+std::vector<Toplex_map::Simplex> facets(const Input_vertex_range& vertex_range) {
+ std::vector<Toplex_map::Simplex> facets;
+ Toplex_map::Simplex f(vertex_range.begin(), vertex_range.end());
+ for (const Toplex_map::Vertex& v : vertex_range) {
+ f.erase(v);
+ facets.emplace_back(f);
+ f.insert(v);
+ }
+ return facets;
+}
+
+} // namespace Gudhi
+
+#endif /* TOPLEX_MAP_H */
diff --git a/src/Toplex_map/test/CMakeLists.txt b/src/Toplex_map/test/CMakeLists.txt
new file mode 100644
index 00000000..59517db5
--- /dev/null
+++ b/src/Toplex_map/test/CMakeLists.txt
@@ -0,0 +1,11 @@
+project(Toplex_map_tests)
+
+include(GUDHI_test_coverage)
+
+add_executable( Toplex_map_unit_test toplex_map_unit_test.cpp )
+target_link_libraries(Toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+gudhi_add_coverage_test(Toplex_map_unit_test)
+
+add_executable( Lazy_toplex_map_unit_test lazy_toplex_map_unit_test.cpp )
+target_link_libraries(Lazy_toplex_map_unit_test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+gudhi_add_coverage_test(Lazy_toplex_map_unit_test)
diff --git a/src/Toplex_map/test/lazy_toplex_map_unit_test.cpp b/src/Toplex_map/test/lazy_toplex_map_unit_test.cpp
new file mode 100644
index 00000000..a050cc92
--- /dev/null
+++ b/src/Toplex_map/test/lazy_toplex_map_unit_test.cpp
@@ -0,0 +1,170 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <vector>
+#include <gudhi/Lazy_toplex_map.h>
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "lazy toplex map"
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE(toplex_map) {
+ using Vertex = Gudhi::Lazy_toplex_map::Vertex;
+
+ Gudhi::Lazy_toplex_map tm;
+ std::cout << "insert_simplex {1, 2, 3, 4}" << std::endl;
+ std::vector<Vertex> sigma1 = {1, 2, 3, 4};
+ tm.insert_simplex(sigma1);
+ std::cout << "insert_simplex {5, 2, 3, 6}" << std::endl;
+ std::vector<Vertex> sigma2 = {5, 2, 3, 6};
+ tm.insert_simplex(sigma2);
+ std::cout << "insert_simplex {5}" << std::endl;
+ std::vector<Vertex> sigma3 = {5};
+ tm.insert_simplex(sigma3);
+ std::cout << "insert_simplex {4, 5, 3}" << std::endl;
+ std::vector<Vertex> sigma6 = {4, 5, 3};
+ tm.insert_simplex(sigma6);
+ std::cout << "insert_simplex {4, 5, 9}" << std::endl;
+ std::vector<Vertex> sigma7 = {4, 5, 9};
+ tm.insert_simplex(sigma7);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 5);
+
+ std::vector<Vertex> sigma4 = {5, 2, 3};
+ std::vector<Vertex> sigma5 = {5, 2, 7};
+ BOOST_CHECK(tm.membership(sigma4));
+ BOOST_CHECK(!tm.membership(sigma5));
+ std::cout << "insert_simplex {5, 2, 7}" << std::endl;
+ tm.insert_simplex(sigma5);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 6);
+
+ BOOST_CHECK(tm.membership(sigma5));
+
+ std::cout << "contraction(4,5)" << std::endl;
+ auto r = tm.contraction(4, 5);
+ std::cout << "r=" << r << std::endl;
+ BOOST_CHECK(r == 5);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 6);
+
+ std::vector<Vertex> sigma8 = {1, 2, 3};
+ std::vector<Vertex> sigma9 = {2, 7};
+
+ sigma8.emplace_back(r);
+ sigma9.emplace_back(r);
+ BOOST_CHECK(!tm.membership(sigma6));
+ BOOST_CHECK(tm.membership(sigma8));
+ BOOST_CHECK(tm.membership(sigma9));
+
+ std::cout << "remove_simplex({2, 7, r = 5})" << std::endl;
+ tm.remove_simplex(sigma9);
+ BOOST_CHECK(!tm.membership(sigma9));
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 8);
+
+ // {2, 7, 5} is removed, but verify its edges are still there
+ std::vector<Vertex> edge = {2, 7};
+ BOOST_CHECK(tm.membership(edge));
+ edge = {2, 5};
+ BOOST_CHECK(tm.membership(edge));
+ edge = {7, 5};
+ BOOST_CHECK(tm.membership(edge));
+}
+
+BOOST_AUTO_TEST_CASE(toplex_map_empty_toplex) {
+ using Vertex = Gudhi::Lazy_toplex_map::Vertex;
+
+ Gudhi::Lazy_toplex_map tm;
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 0);
+ std::cout << "num_vertices = " << tm.num_vertices() << std::endl;
+ BOOST_CHECK(tm.num_vertices() == 0);
+
+ std::cout << "Check an empty simplex is a member." << std::endl;
+ std::vector<Vertex> empty_sigma = {};
+ BOOST_CHECK(tm.membership(empty_sigma));
+
+ std::cout << "Check the edge 2,7 is not a member." << std::endl;
+ std::vector<Vertex> edge = {2, 7};
+ BOOST_CHECK(!tm.membership(edge));
+
+ std::cout << "Insert an empty simplex." << std::endl;
+ tm.insert_simplex(empty_sigma);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 0);
+ std::cout << "num_vertices = " << tm.num_vertices() << std::endl;
+ BOOST_CHECK(tm.num_vertices() == 0);
+
+ std::cout << "Check an empty simplex is a member." << std::endl;
+ BOOST_CHECK(tm.membership(empty_sigma));
+ std::cout << "Check the edge 2,7 is not a member." << std::endl;
+ BOOST_CHECK(!tm.membership(edge));
+
+ std::cout << "Insert edge 2,7." << std::endl;
+ tm.insert_simplex(edge);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 1);
+ std::cout << "num_vertices = " << tm.num_vertices() << std::endl;
+ BOOST_CHECK(tm.num_vertices() == 2);
+
+ std::cout << "Check an empty simplex is a member." << std::endl;
+ BOOST_CHECK(tm.membership(empty_sigma));
+ std::cout << "Check the edge 2,7 is a member." << std::endl;
+ BOOST_CHECK(tm.membership(edge));
+
+ std::cout << "contraction(2,7)" << std::endl;
+ auto r = tm.contraction(2, 7);
+ std::cout << "r=" << r << std::endl;
+ BOOST_CHECK(r == 7);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 1);
+ std::cout << "num_vertices = " << tm.num_vertices() << std::endl;
+ BOOST_CHECK(tm.num_vertices() == 1);
+
+ std::cout << "Check an empty simplex is a member." << std::endl;
+ BOOST_CHECK(tm.membership(empty_sigma));
+ std::cout << "Check the edge 2,7 is not a member." << std::endl;
+ BOOST_CHECK(!tm.membership(edge));
+
+ std::cout << "Remove the vertex 7." << std::endl;
+ std::vector<Vertex> vertex = {7};
+ tm.remove_simplex(vertex);
+
+ std::cout << "num_maximal_simplices = " << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 0);
+ std::cout << "num_vertices = " << tm.num_vertices() << std::endl;
+ BOOST_CHECK(tm.num_vertices() == 0);
+
+ std::cout << "Check an empty simplex is a member." << std::endl;
+ BOOST_CHECK(tm.membership(empty_sigma));
+ std::cout << "Check the edge 2,7 is not a member." << std::endl;
+ BOOST_CHECK(!tm.membership(edge));
+}
diff --git a/src/Toplex_map/test/toplex_map_unit_test.cpp b/src/Toplex_map/test/toplex_map_unit_test.cpp
new file mode 100644
index 00000000..2bd27936
--- /dev/null
+++ b/src/Toplex_map/test/toplex_map_unit_test.cpp
@@ -0,0 +1,138 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author: François Godi, Vincent Rouvreau
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <vector>
+#include <gudhi/Toplex_map.h>
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MODULE "toplex map"
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE(toplex_map) {
+ using Vertex = Gudhi::Toplex_map::Vertex;
+
+ Gudhi::Toplex_map tm;
+ std::cout << "insert_simplex {1, 2, 3, 4}" << std::endl;
+ std::vector<Vertex> sigma1 = {1, 2, 3, 4};
+ tm.insert_simplex(sigma1);
+ std::cout << "insert_simplex {5, 2, 3, 6}" << std::endl;
+ std::vector<Vertex> sigma2 = {5, 2, 3, 6};
+ tm.insert_simplex(sigma2);
+ std::cout << "insert_simplex {5}" << std::endl;
+ std::vector<Vertex> sigma3 = {5};
+ tm.insert_simplex(sigma3);
+ std::cout << "insert_simplex {4, 5, 3}" << std::endl;
+ std::vector<Vertex> sigma6 = {4, 5, 3};
+ tm.insert_simplex(sigma6);
+ std::cout << "insert_simplex {4, 5, 9}" << std::endl;
+ std::vector<Vertex> sigma7 = {4, 5, 9};
+ tm.insert_simplex(sigma7);
+
+ std::cout << "num_maximal_simplices" << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 4);
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ BOOST_CHECK(tm.maximality(*simplex_ptr));
+ }
+
+ BOOST_CHECK(tm.maximality(sigma1));
+ BOOST_CHECK(tm.maximality(sigma2));
+ BOOST_CHECK(!tm.maximality(sigma3));
+ BOOST_CHECK(tm.maximality(sigma6));
+ BOOST_CHECK(tm.maximality(sigma7));
+
+ std::vector<Vertex> sigma4 = {5, 2, 3};
+ std::vector<Vertex> sigma5 = {5, 2, 7};
+ BOOST_CHECK(tm.membership(sigma4));
+ BOOST_CHECK(!tm.membership(sigma5));
+ std::cout << "insert_simplex {5, 2, 7}" << std::endl;
+ tm.insert_simplex(sigma5);
+
+ std::cout << "num_maximal_simplices" << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 5);
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ BOOST_CHECK(tm.maximality(*simplex_ptr));
+ }
+
+ BOOST_CHECK(tm.membership(sigma5));
+
+ std::cout << "contraction(4,5)" << std::endl;
+ auto r = tm.contraction(4, 5);
+ std::cout << "r=" << r << std::endl;
+ BOOST_CHECK(r == 5);
+
+ std::cout << "num_maximal_simplices" << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 4);
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ BOOST_CHECK(tm.maximality(*simplex_ptr));
+ }
+
+ std::vector<Vertex> sigma8 = {1, 2, 3};
+ std::vector<Vertex> sigma9 = {2, 7};
+
+ sigma8.emplace_back(r);
+ sigma9.emplace_back(r);
+ BOOST_CHECK(!tm.membership(sigma6));
+ BOOST_CHECK(tm.membership(sigma8));
+ BOOST_CHECK(tm.membership(sigma9));
+
+ std::cout << "remove_simplex({2, 7, r = 5})" << std::endl;
+ tm.remove_simplex(sigma9);
+ BOOST_CHECK(!tm.membership(sigma9));
+
+ std::cout << "num_maximal_simplices" << tm.num_maximal_simplices() << std::endl;
+ BOOST_CHECK(tm.num_maximal_simplices() == 5);
+ // Browse maximal simplices
+ std::cout << "Maximal simplices are :" << std::endl;
+ for (auto simplex_ptr : tm.maximal_simplices()) {
+ for (auto v : *simplex_ptr) {
+ std::cout << v << ", ";
+ }
+ std::cout << std::endl;
+ BOOST_CHECK(tm.maximality(*simplex_ptr));
+ }
+ // {2, 7, 5} is removed, but verify its edges are still there
+ std::vector<Vertex> edge = {2, 7};
+ BOOST_CHECK(tm.membership(edge));
+ edge = {2, 5};
+ BOOST_CHECK(tm.membership(edge));
+ edge = {7, 5};
+ BOOST_CHECK(tm.membership(edge));
+}
diff --git a/src/Witness_complex/example/example_nearest_landmark_table.cpp b/src/Witness_complex/example/example_nearest_landmark_table.cpp
index acaf7c54..441900c1 100644
--- a/src/Witness_complex/example/example_nearest_landmark_table.cpp
+++ b/src/Witness_complex/example/example_nearest_landmark_table.cpp
@@ -19,22 +19,19 @@ int main(int argc, char * const argv[]) {
using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology<Simplex_tree, Field_Zp>;
Simplex_tree simplex_tree;
- Nearest_landmark_table nlt;
// Example contains 5 witnesses and 5 landmarks
- Nearest_landmark_range w0 = {std::make_pair(0, 0), std::make_pair(1, 1), std::make_pair(2, 2),
- std::make_pair(3, 3), std::make_pair(4, 4)}; nlt.push_back(w0);
- Nearest_landmark_range w1 = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(3, 2),
- std::make_pair(4, 3), std::make_pair(0, 4)}; nlt.push_back(w1);
- Nearest_landmark_range w2 = {std::make_pair(2, 0), std::make_pair(3, 1), std::make_pair(4, 2),
- std::make_pair(0, 3), std::make_pair(1, 4)}; nlt.push_back(w2);
- Nearest_landmark_range w3 = {std::make_pair(3, 0), std::make_pair(4, 1), std::make_pair(0, 2),
- std::make_pair(1, 3), std::make_pair(2, 4)}; nlt.push_back(w3);
- Nearest_landmark_range w4 = {std::make_pair(4, 0), std::make_pair(0, 1), std::make_pair(1, 2),
- std::make_pair(2, 3), std::make_pair(3, 4)}; nlt.push_back(w4);
+ Nearest_landmark_table nlt = {
+ {{0, 0.0}, {1, 0.1}, {2, 0.2}, {3, 0.3}, {4, 0.4}}, // witness 0
+ {{1, 0.0}, {2, 0.1}, {3, 0.2}, {4, 0.3}, {0, 0.4}}, // witness 1
+ {{2, 0.0}, {3, 0.1}, {4, 0.2}, {0, 0.3}, {1, 0.4}}, // witness 2
+ {{3, 0.0}, {4, 0.1}, {0, 0.2}, {1, 0.3}, {2, 0.4}}, // witness 3
+ {{4, 0.0}, {0, 0.1}, {1, 0.2}, {2, 0.3}, {3, 0.4}} // witness 4
+ };
+ /* distance(witness3, landmark3) is 0, distance(witness3, landmark4) is 0.1, etc. */
Witness_complex witness_complex(nlt);
- witness_complex.create_complex(simplex_tree, 4.1);
+ witness_complex.create_complex(simplex_tree, .41);
std::cout << "Number of simplices: " << simplex_tree.num_simplices() << std::endl;
diff --git a/src/Witness_complex/include/gudhi/Strong_witness_complex.h b/src/Witness_complex/include/gudhi/Strong_witness_complex.h
index fd6b3f38..03d6d2e7 100644
--- a/src/Witness_complex/include/gudhi/Strong_witness_complex.h
+++ b/src/Witness_complex/include/gudhi/Strong_witness_complex.h
@@ -40,10 +40,13 @@ namespace witness_complex {
* \brief Constructs strong witness complex for a given table of nearest landmarks with respect to witnesses.
* \ingroup witness_complex
*
- * \tparam Nearest_landmark_table_ needs to be a range of a range of pairs of nearest landmarks and distances.
+ * \tparam Nearest_landmark_table_ needs to be a range (one entry per witness)
+ * of sorted ranges of pairs of nearest landmarks and distances.
* The class Nearest_landmark_table_::value_type must be a copiable range.
* The range of pairs must admit a member type 'iterator'. The dereference type
- * of the pair range iterator needs to be 'std::pair<std::size_t, double>'.
+ * of the pair range iterator needs to be 'std::pair<std::size_t, double>'
+ * where the first element is the index of the landmark, and the second its
+ * (squared) distance to the witness.
*/
template< class Nearest_landmark_table_ >
class Strong_witness_complex {
diff --git a/src/Witness_complex/include/gudhi/Witness_complex.h b/src/Witness_complex/include/gudhi/Witness_complex.h
index 67885258..1f61f8f2 100644
--- a/src/Witness_complex/include/gudhi/Witness_complex.h
+++ b/src/Witness_complex/include/gudhi/Witness_complex.h
@@ -75,10 +75,13 @@ class Witness_complex {
/**
* \brief Initializes member variables before constructing simplicial complex.
* \details Records nearest landmark table.
- * @param[in] nearest_landmark_table needs to be a range of a range of pairs of nearest landmarks and distances.
+ * @param[in] nearest_landmark_table needs to be a range (one entry per witness)
+ * of sorted ranges of pairs of nearest landmarks and distances.
* The class Nearest_landmark_table_::value_type must be a copiable range.
* The range of pairs must admit a member type 'iterator'. The dereference type
- * of the pair range iterator needs to be 'std::pair<std::size_t, double>'.
+ * of the pair range iterator needs to be 'std::pair<std::size_t, double>'
+ * where the first element is the index of the landmark, and the second its
+ * (squared) distance to the witness.
*/
Witness_complex(Nearest_landmark_table_ const & nearest_landmark_table)
@@ -108,8 +111,8 @@ class Witness_complex {
}
ActiveWitnessList active_witnesses;
Landmark_id k = 0; /* current dimension in iterative construction */
- for (auto w : nearest_landmark_table_)
- active_witnesses.push_back(ActiveWitness(w));
+ for (auto&& w : nearest_landmark_table_)
+ active_witnesses.emplace_back(w);
while (!active_witnesses.empty() && k <= limit_dimension) {
typename ActiveWitnessList::iterator aw_it = active_witnesses.begin();
std::vector<Landmark_id> simplex;
diff --git a/src/Witness_complex/utilities/witnesscomplex.md b/src/Witness_complex/utilities/witnesscomplex.md
index da453cce..7ea397b9 100644
--- a/src/Witness_complex/utilities/witnesscomplex.md
+++ b/src/Witness_complex/utilities/witnesscomplex.md
@@ -10,7 +10,7 @@ Leave the lines above as it is required by the web site generator 'Jekyll'
{:/comment}
-For more details about the witness complex, please read the [user manual of the package](/doc/latest/group__witness__complex.html).
+For more details about the witness complex, please read the [user manual of the package]({{ site.officialurl }}/doc/latest/group__witness__complex.html).
## weak_witness_persistence ##
This program computes the persistent homology with coefficient field *Z/pZ* of a Weak witness complex defined on a set of input points.
diff --git a/src/cmake/modules/GUDHI_compilation_flags.cmake b/src/cmake/modules/GUDHI_compilation_flags.cmake
index a01d6e13..86cd531b 100644
--- a/src/cmake/modules/GUDHI_compilation_flags.cmake
+++ b/src/cmake/modules/GUDHI_compilation_flags.cmake
@@ -5,7 +5,7 @@ include(CheckCXXSourceCompiles)
# add a compiler flag only if it is accepted
macro(add_cxx_compiler_flag _flag)
- string(REPLACE "-" "_" _flag_var ${_flag})
+ string(REPLACE "-" "_" "/" _flag_var ${_flag})
check_cxx_accepts_flag("${_flag}" CXX_COMPILER_${_flag_var}_OK)
if(CXX_COMPILER_${_flag_var}_OK)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_flag}")
@@ -43,12 +43,15 @@ set (CMAKE_CXX_STANDARD 11)
enable_testing()
if(MSVC)
- # Turn off some VC++ warnings
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4668 /wd4311 /wd4800 /wd4820 /wd4503 /wd4244 /wd4345 /wd4996 /wd4396 /wd4018")
+ add_cxx_compiler_flag("/W3")
+else()
+ add_cxx_compiler_flag("-Wall")
+ # Only for dev version
+ if(PROJECT_NAME STREQUAL "GUDHIdev")
+ add_cxx_compiler_flag("-pedantic")
+ endif()
endif()
-add_cxx_compiler_flag("-Wall")
-
if (DEBUG_TRACES)
# For programs to be more verbose
message(STATUS "DEBUG_TRACES are activated")
diff --git a/src/cmake/modules/GUDHI_doxygen_target.cmake b/src/cmake/modules/GUDHI_doxygen_target.cmake
index 9e10e566..7a84c4e0 100644
--- a/src/cmake/modules/GUDHI_doxygen_target.cmake
+++ b/src/cmake/modules/GUDHI_doxygen_target.cmake
@@ -1,7 +1,7 @@
# add a target to generate API documentation with Doxygen
-find_package(Doxygen)
+find_package(Doxygen QUIET)
if(DOXYGEN_FOUND)
- # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
+ set(GUDHI_MODULES ${GUDHI_MODULES} "cpp-documentation" CACHE INTERNAL "GUDHI_MODULES")
# starting from cmake 3.9 the usage of DOXYGEN_EXECUTABLE is deprecated
if(TARGET Doxygen::doxygen)
@@ -16,4 +16,6 @@ if(DOXYGEN_FOUND)
# In dev version, doxygen target depends on user_version target. Not existing in user version
add_dependencies(doxygen user_version)
endif()
-endif(DOXYGEN_FOUND)
+else()
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} "cpp-documentation" CACHE INTERNAL "GUDHI_MISSING_MODULES")
+endif()
diff --git a/src/cmake/modules/GUDHI_modules.cmake b/src/cmake/modules/GUDHI_modules.cmake
index f95d0c34..aab1dd08 100644
--- a/src/cmake/modules/GUDHI_modules.cmake
+++ b/src/cmake/modules/GUDHI_modules.cmake
@@ -1,11 +1,12 @@
# A function to add a new module in GUDHI
-set(GUDHI_MODULES "")
set(GUDHI_MODULES_FULL_LIST "")
function(add_gudhi_module file_path)
option("WITH_MODULE_GUDHI_${file_path}" "Activate/desactivate ${file_path} compilation and installation" ON)
if (WITH_MODULE_GUDHI_${file_path})
- set(GUDHI_MODULES ${GUDHI_MODULES} ${file_path} PARENT_SCOPE)
+ set(GUDHI_MODULES ${GUDHI_MODULES} ${file_path} CACHE INTERNAL "GUDHI_MODULES")
+ else()
+ set(GUDHI_MISSING_MODULES ${GUDHI_MISSING_MODULES} ${file_path} CACHE INTERNAL "GUDHI_MISSING_MODULES")
endif()
# Required by user_version
set(GUDHI_MODULES_FULL_LIST ${GUDHI_MODULES_FULL_LIST} ${file_path} PARENT_SCOPE)
diff --git a/src/cmake/modules/GUDHI_third_party_libraries.cmake b/src/cmake/modules/GUDHI_third_party_libraries.cmake
index f03c2177..57ea7d14 100644
--- a/src/cmake/modules/GUDHI_third_party_libraries.cmake
+++ b/src/cmake/modules/GUDHI_third_party_libraries.cmake
@@ -1,6 +1,6 @@
# This files manage third party libraries required by GUDHI
-find_package(Boost 1.48.0 REQUIRED COMPONENTS system filesystem unit_test_framework program_options thread)
+find_package(Boost 1.56.0 REQUIRED COMPONENTS system filesystem unit_test_framework program_options thread)
if(NOT Boost_FOUND)
message(FATAL_ERROR "NOTICE: This program requires Boost and will not be compiled.")
@@ -21,11 +21,11 @@ endif()
# A fix would be to use https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html
# or even better https://cmake.org/cmake/help/v3.1/variable/CMAKE_CXX_STANDARD.html
# but it implies to use cmake version 3.1 at least.
-find_package(CGAL)
+find_package(CGAL QUIET)
# Only CGAL versions > 4.4 supports what Gudhi uses from CGAL
if (CGAL_VERSION VERSION_LESS 4.4.0)
- message("CGAL version ${CGAL_VERSION} is considered too old to be used by Gudhi.")
+ message("++ CGAL version ${CGAL_VERSION} is considered too old to be used by Gudhi.")
unset(CGAL_FOUND)
endif()
if(CGAL_FOUND)
@@ -127,6 +127,10 @@ function( find_python_module PYTHON_MODULE_NAME )
RESULT_VARIABLE PYTHON_MODULE_RESULT
OUTPUT_VARIABLE PYTHON_MODULE_VERSION
ERROR_VARIABLE PYTHON_MODULE_ERROR)
+ message ("PYTHON_MODULE_NAME = ${PYTHON_MODULE_NAME}
+ - PYTHON_MODULE_RESULT = ${PYTHON_MODULE_RESULT}
+ - PYTHON_MODULE_VERSION = ${PYTHON_MODULE_VERSION}
+ - PYTHON_MODULE_ERROR = ${PYTHON_MODULE_ERROR}")
if(PYTHON_MODULE_RESULT EQUAL 0)
# Remove carriage return
string(STRIP ${PYTHON_MODULE_VERSION} PYTHON_MODULE_VERSION)
@@ -143,27 +147,27 @@ if( PYTHONINTERP_FOUND )
find_python_module("pytest")
find_python_module("matplotlib")
find_python_module("numpy")
+ find_python_module("scipy")
+ find_python_module("sphinx")
endif()
if(NOT GUDHI_CYTHON_PATH)
message(FATAL_ERROR "ERROR: GUDHI_CYTHON_PATH is not valid.")
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/cmake/modules/GUDHI_user_version_target.cmake b/src/cmake/modules/GUDHI_user_version_target.cmake
index d43a6fa6..2ed48c48 100644
--- a/src/cmake/modules/GUDHI_user_version_target.cmake
+++ b/src/cmake/modules/GUDHI_user_version_target.cmake
@@ -27,7 +27,7 @@ add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
copy ${CMAKE_SOURCE_DIR}/Conventions.txt ${GUDHI_USER_VERSION_DIR}/Conventions.txt)
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
- copy ${CMAKE_SOURCE_DIR}/README ${GUDHI_USER_VERSION_DIR}/README)
+ copy ${CMAKE_SOURCE_DIR}/README.md ${GUDHI_USER_VERSION_DIR}/README.md)
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
copy ${CMAKE_SOURCE_DIR}/COPYING ${GUDHI_USER_VERSION_DIR}/COPYING)
add_custom_command(TARGET user_version PRE_BUILD COMMAND ${CMAKE_COMMAND} -E
diff --git a/src/common/benchmark/CMakeLists.txt b/src/common/benchmark/CMakeLists.txt
new file mode 100644
index 00000000..a3787d6e
--- /dev/null
+++ b/src/common/benchmark/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(common_benchmark)
+
+add_executable(Graph_simplicial_complex_benchmark Graph_simplicial_complex_benchmark.cpp)
diff --git a/src/common/benchmark/Graph_simplicial_complex_benchmark.cpp b/src/common/benchmark/Graph_simplicial_complex_benchmark.cpp
new file mode 100644
index 00000000..825d6cb5
--- /dev/null
+++ b/src/common/benchmark/Graph_simplicial_complex_benchmark.cpp
@@ -0,0 +1,150 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2018 Inria
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/distance_functions.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Clock.h>
+#include <gudhi/Points_off_io.h>
+
+#include <boost/graph/adjacency_list.hpp>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <limits> // for numeric limits
+#include <fstream>
+#include <cassert>
+
+
+std::ofstream results_csv("results.csv");
+
+template< typename Adjacency_list, typename ForwardPointRange, typename Distance >
+Adjacency_list proximity_graph_computation(const ForwardPointRange& points, double threshold, Distance distance) {
+ std::vector<std::pair< int, int >> edges;
+ std::vector< double > edges_fil;
+ std::map< int, double > vertices;
+
+ int idx_u, idx_v;
+ double fil;
+ idx_u = 0;
+ for (auto it_u = points.begin(); it_u != points.end(); ++it_u) {
+ idx_v = idx_u + 1;
+ for (auto it_v = it_u + 1; it_v != points.end(); ++it_v, ++idx_v) {
+ fil = distance(*it_u, *it_v);
+ if (fil <= threshold) {
+ edges.emplace_back(idx_u, idx_v);
+ edges_fil.push_back(fil);
+ }
+ }
+ ++idx_u;
+ }
+
+ // Points are labeled from 0 to idx_u-1
+ Adjacency_list skel_graph(edges.begin(), edges.end(), edges_fil.begin(), idx_u);
+
+ auto vertex_prop = boost::get(Gudhi::vertex_filtration_t(), skel_graph);
+
+ typename boost::graph_traits<Adjacency_list>::vertex_iterator vi, vi_end;
+ for (std::tie(vi, vi_end) = boost::vertices(skel_graph);
+ vi != vi_end; ++vi) {
+ boost::put(vertex_prop, *vi, 0.);
+ }
+
+ return skel_graph;
+}
+
+template <typename Adjacency_list>
+void benchmark_proximity_graph(const std::string& msg, const std::string& off_file_name) {
+ Gudhi::Points_off_reader<std::vector<double>> off_reader(off_file_name);
+ assert(off_reader.is_valid());
+
+ std::cout << "+ " << msg << std::endl;
+
+ results_csv << "\"nb_points\";"
+ << "\"nb_simplices\";"
+ << "\"compute proximity graph(sec.)\";"
+ << "\"complex_creation_time(sec.)\";"
+ << "\"" << msg << "\";" << std::endl;
+
+ Gudhi::Clock pg_compute_proximity_graph(" benchmark_proximity_graph - compute proximity graph");
+ pg_compute_proximity_graph.begin();
+ // benchmark begin
+ Adjacency_list proximity_graph = proximity_graph_computation<Adjacency_list>(off_reader.get_point_cloud(),
+ std::numeric_limits<double>::infinity(),
+ Gudhi::Euclidean_distance());
+ // benchmark end
+ pg_compute_proximity_graph.end();
+ std::cout << pg_compute_proximity_graph;
+
+ Gudhi::Simplex_tree<> complex;
+ Gudhi::Clock st_create_clock(" benchmark_proximity_graph - complex creation");
+ st_create_clock.begin();
+ // benchmark begin
+ complex.insert_graph(proximity_graph);
+ // benchmark end
+ st_create_clock.end();
+ std::cout << st_create_clock;
+
+ results_csv << off_reader.get_point_cloud().size() << ";" << complex.num_simplices() << ";"
+ << pg_compute_proximity_graph.num_seconds() << ";"
+ << st_create_clock.num_seconds() << ";" << std::endl;
+
+ std::cout << " benchmark_proximity_graph - nb simplices = " << complex.num_simplices() << std::endl;
+}
+
+int main(int argc, char * const argv[]) {
+ std::string off_file_name(argv[1]);
+
+ // The fastest, the less memory used
+ using vecSdirectedS = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<vecSdirectedS>("vecSdirectedS", off_file_name);
+
+ using vecSundirectedS = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<vecSundirectedS>("vecSundirectedS", off_file_name);
+
+ using vecSbidirectionalS = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<vecSbidirectionalS>("vecSbidirectionalS", off_file_name);
+
+ using setSdirectedS = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<setSdirectedS>("setSdirectedS", off_file_name);
+
+ using setSundirectedS = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<setSundirectedS>("setSundirectedS", off_file_name);
+
+ using setSbidirectionalS = boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS,
+ boost::property<Gudhi::vertex_filtration_t, double>,
+ boost::property<Gudhi::edge_filtration_t, double>>;
+ benchmark_proximity_graph<setSbidirectionalS>("setSbidirectionalS", off_file_name);
+
+ return 0;
+}
diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h
index 40f202c7..c19b3444 100644
--- a/src/common/doc/examples.h
+++ b/src/common/doc/examples.h
@@ -53,10 +53,7 @@
* @example Spatial_searching/example_spatial_searching.cpp
* @example Alpha_complex/alpha_complex_3d_persistence.cpp
* @example Alpha_complex/alpha_complex_persistence.cpp
- * @example Alpha_complex/weighted_periodic_alpha_complex_3d_persistence.cpp
- * @example Alpha_complex/weighted_alpha_complex_3d_persistence.cpp
- * @example Alpha_complex/periodic_alpha_complex_3d_persistence.cpp
- * @example Alpha_complex/exact_alpha_complex_3d_persistence.cpp
+ * @example Alpha_complex/Weighted_alpha_complex_3d_from_points.cpp
* @example Bottleneck_distance/bottleneck_distance.cpp
* @example Witness_complex/weak_witness_persistence.cpp
* @example Witness_complex/strong_witness_persistence.cpp
diff --git a/src/common/doc/file_formats.h b/src/common/doc/file_formats.h
index 523153b8..23214e25 100644
--- a/src/common/doc/file_formats.h
+++ b/src/common/doc/file_formats.h
@@ -72,7 +72,7 @@ namespace Gudhi {
\section FileFormatsPerseus Perseus
- This file format is the format used by the Perseus software
+ This file format is a format inspired from the Perseus software
(http://www.sas.upenn.edu/~vnanda/perseus/) by Vidit Nanda.
The first line contains a number d begin the dimension of the
bitmap (2 in the example below). Next d lines are the numbers of top dimensional cubes in each dimensions (3 and 3
@@ -118,6 +118,10 @@ namespace Gudhi {
Indicate that we have imposed periodic boundary conditions in the direction x, but not in the direction y.
Other sample files can be found in the `data/bitmap` folder.
+
+ \note Unlike in Perseus format the filtration on the maximal cubes can be any double precision number.
+ Consequently one cannot mark the cubes that are not present with `-1`'s. To do that please set their filtration value
+ to \f$+\infty\f$ (aka. `inf` in the file).
*/
} // namespace Gudhi
diff --git a/src/common/doc/installation.h b/src/common/doc/installation.h
index c27e4f56..8fb8b330 100644
--- a/src/common/doc/installation.h
+++ b/src/common/doc/installation.h
@@ -5,7 +5,7 @@
* Examples of GUDHI headers inclusion can be found in \ref utilities.
*
* \section compiling Compiling
- * The library uses c++11 and requires <a target="_blank" href="http://www.boost.org/">Boost</a> &ge; 1.48.0
+ * The library uses c++11 and requires <a target="_blank" href="http://www.boost.org/">Boost</a> &ge; 1.56.0
* and <a target="_blank" href="https://www.cmake.org/">CMake</a> &ge; 3.1.
* It is a multi-platform library and compiles on Linux, Mac OSX and Visual Studio 2015.
*
@@ -18,7 +18,7 @@ cmake ..
make \endverbatim
* By default, examples are disabled. You can activate their compilation with
* <a href="https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html">ccmake</a> (on Linux and Mac OSX),
- * <a href="https://cmake.org/cmake/help/v3.0/manual/cmake-gui.1.html">cmake-gui</a> (on Windows) or y mofifying the
+ * <a href="https://cmake.org/cmake/help/v3.0/manual/cmake-gui.1.html">cmake-gui</a> (on Windows) or by modifying the
* cmake command as follows :
\verbatim cmake -DWITH_GUDHI_EXAMPLE=ON ..
make \endverbatim
@@ -72,12 +72,6 @@ make doxygen
*
* The following examples/utilities require the <a target="_blank" href="http://www.cgal.org/">Computational Geometry Algorithms
* Library</a> (CGAL \cite cgal:eb-15b) and will not be built if CGAL is not installed:
- * \li <a href="_alpha_complex_2alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/alpha_complex_3d_persistence.cpp</a>
- * \li <a href="_alpha_complex_2exact_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/exact_alpha_complex_3d_persistence.cpp</a>
- * \li <a href="_alpha_complex_2weighted_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/weighted_alpha_complex_3d_persistence.cpp</a>
* \li <a href="_simplex_tree_2example_alpha_shapes_3_simplex_tree_from_off_file_8cpp-example.html">
* Simplex_tree/example_alpha_shapes_3_simplex_tree_from_off_file.cpp</a>
*
@@ -100,8 +94,6 @@ make doxygen
* Alpha_complex/Alpha_complex_from_points.cpp</a>
* \li <a href="_alpha_complex_2alpha_complex_persistence_8cpp-example.html">
* Alpha_complex/alpha_complex_persistence.cpp</a>
- * \li <a href="_alpha_complex_2periodic_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/periodic_alpha_complex_3d_persistence.cpp</a>
* \li <a href="_persistent_cohomology_2custom_persistence_sort_8cpp-example.html">
* Persistent_cohomology/custom_persistence_sort.cpp</a>
*
@@ -135,6 +127,12 @@ make doxygen
* \li <a href="_tangential_complex_2example_with_perturb_8cpp-example.html">
* Tangential_complex/example_with_perturb.cpp</a>
*
+ * The following example requires CGAL version &ge; 4.11.0:
+ * \li <a href="_alpha_complex_2_weighted_alpha_complex_3d_from_points_8cpp-example.html">
+ * Alpha_complex/Weighted_alpha_complex_3d_from_points.cpp</a>
+ * \li <a href="_alpha_complex_2alpha_complex_3d_persistence_8cpp-example.html">
+ * Alpha_complex/alpha_complex_3d_persistence.cpp</a>
+ *
* \subsection eigen3 Eigen3
* The \ref alpha_complex data structure and few examples requires
* <a target="_blank" href="http://eigen.tuxfamily.org/">Eigen3</a> is a C++ template library for linear algebra:
@@ -148,8 +146,10 @@ make doxygen
* Alpha_complex/Alpha_complex_from_points.cpp</a>
* \li <a href="_alpha_complex_2alpha_complex_persistence_8cpp-example.html">
* Alpha_complex/alpha_complex_persistence.cpp</a>
- * \li <a href="_alpha_complex_2periodic_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/periodic_alpha_complex_3d_persistence.cpp</a>
+ * \li <a href="_alpha_complex_2alpha_complex_3d_persistence_8cpp-example.html">
+ * Alpha_complex/alpha_complex_3d_persistence.cpp</a>
+ * \li <a href="_alpha_complex_2_weighted_alpha_complex_3d_from_points_8cpp-example.html">
+ * Alpha_complex/Weighted_alpha_complex_3d_from_points.cpp</a>
* \li <a href="_bottleneck_distance_2alpha_rips_persistence_bottleneck_distance_8cpp-example.html">
* Bottleneck_distance/alpha_rips_persistence_bottleneck_distance.cpp.cpp</a>
* \li <a href="_persistent_cohomology_2custom_persistence_sort_8cpp-example.html">
@@ -195,12 +195,6 @@ make doxygen
* Alpha_complex/alpha_complex_3d_persistence.cpp</a>
* \li <a href="_alpha_complex_2alpha_complex_persistence_8cpp-example.html">
* Alpha_complex/alpha_complex_persistence.cpp</a>
- * \li <a href="_alpha_complex_2exact_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/exact_alpha_complex_3d_persistence.cpp</a>
- * \li <a href="_alpha_complex_2periodic_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/periodic_alpha_complex_3d_persistence.cpp</a>
- * \li <a href="_alpha_complex_2weighted_alpha_complex_3d_persistence_8cpp-example.html">
- * Alpha_complex/weighted_alpha_complex_3d_persistence.cpp</a>
* \li <a href="_bitmap_cubical_complex_2_bitmap_cubical_complex_8cpp-example.html">
* Bitmap_cubical_complex/cubical_complex_persistence.cpp</a>
* \li <a href="_bitmap_cubical_complex_2_bitmap_cubical_complex_periodic_boundary_conditions_8cpp-example.html">
@@ -239,10 +233,6 @@ make doxygen
* Persistent_cohomology/rips_multifield_persistence.cpp</a>
* \li <a href="_persistent_cohomology_2rips_persistence_step_by_step_8cpp-example.html">
* Persistent_cohomology/rips_persistence_step_by_step.cpp</a>
- * \li <a href="_persistent_cohomology_2exact_alpha_complex_3d_persistence_8cpp-example.html">
- * Persistent_cohomology/exact_alpha_complex_3d_persistence.cpp</a>
- * \li <a href="_persistent_cohomology_2weighted_alpha_complex_3d_persistence_8cpp-example.html">
- * Persistent_cohomology/weighted_alpha_complex_3d_persistence.cpp</a>
* \li <a href="_persistent_cohomology_2custom_persistence_sort_8cpp-example.html">
* Persistent_cohomology/custom_persistence_sort.cpp</a>
* \li <a href="_rips_complex_2example_one_skeleton_rips_from_points_8cpp-example.html">
diff --git a/src/common/doc/main_page.h b/src/common/doc/main_page.h
index db1e80ce..afe9b68c 100644
--- a/src/common/doc/main_page.h
+++ b/src/common/doc/main_page.h
@@ -29,7 +29,9 @@
<b>Author:</b> Vincent Rouvreau<br>
<b>Introduced in:</b> GUDHI 1.3.0<br>
<b>Copyright:</b> GPL v3<br>
- <b>Requires:</b> \ref cgal &ge; 4.7.0 and \ref eigen3
+ <b>Requires:</b> \ref eigen3 and<br>
+ \ref cgal &ge; 4.7.0 for Alpha_complex<br>
+ \ref cgal &ge; 4.11.0 for Alpha_complex_3d
</td>
<td width="75%">
Alpha_complex is a simplicial complex constructed from the finite cells of a Delaunay Triangulation.<br>
@@ -38,7 +40,8 @@
values of the codimension 1 cofaces that make it not Gabriel otherwise.
All simplices that have a filtration value strictly greater than a given alpha squared value are not inserted into
the complex.<br>
- <b>User manual:</b> \ref alpha_complex - <b>Reference manual:</b> Gudhi::alpha_complex::Alpha_complex
+ <b>User manual:</b> \ref alpha_complex - <b>Reference manual:</b> Gudhi::alpha_complex::Alpha_complex and
+ Gudhi::alpha_complex::Alpha_complex_3d
</td>
</tr>
</table>
@@ -168,6 +171,23 @@
</td>
</tr>
</table>
+ \subsection ToplexMapDataStructure Toplex Map
+ \image html "map.png" "Toplex map representation"
+<table border="0">
+ <tr>
+ <td width="25%">
+ <b>Author:</b> Fran&ccedil;ois Godi<br>
+ <b>Introduced in:</b> GUDHI 2.1.0<br>
+ <b>Copyright:</b> GPL v3<br>
+ </td>
+ <td width="75%">
+ The Toplex map data structure is composed firstly of a raw storage of toplices (the maximal simplices)
+ and secondly of a map which associate any vertex to a set of pointers toward all toplices
+ containing this vertex.
+ <b>User manual:</b> \ref toplex_map - <b>Reference manual:</b> Gudhi::Toplex_map
+ </td>
+ </tr>
+
\subsection WitnessComplexDataStructure Witness complex
\image html "Witness_complex_representation.png" "Witness complex representation"
<table border="0">
diff --git a/src/common/include/gudhi/Unitary_tests_utils.h b/src/common/include/gudhi/Unitary_tests_utils.h
index e07c8d42..22f00212 100644
--- a/src/common/include/gudhi/Unitary_tests_utils.h
+++ b/src/common/include/gudhi/Unitary_tests_utils.h
@@ -34,7 +34,7 @@ void GUDHI_TEST_FLOAT_EQUALITY_CHECK(FloatingType a, FloatingType b,
std::cout << "GUDHI_TEST_FLOAT_EQUALITY_CHECK - " << a << " versus " << b
<< " | diff = " << std::fabs(a - b) << " - epsilon = " << epsilon << std::endl;
#endif
- BOOST_CHECK(std::fabs(a - b) < epsilon);
+ BOOST_CHECK(std::fabs(a - b) <= epsilon);
}
#endif // UNITARY_TESTS_UTILS_H_
diff --git a/src/common/include/gudhi/graph_simplicial_complex.h b/src/common/include/gudhi/graph_simplicial_complex.h
index 49fe56cc..0d81ca71 100644
--- a/src/common/include/gudhi/graph_simplicial_complex.h
+++ b/src/common/include/gudhi/graph_simplicial_complex.h
@@ -49,7 +49,7 @@ struct vertex_filtration_t {
*
*/
template <typename SimplicialComplexForProximityGraph>
-using Proximity_graph = typename boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS
+using Proximity_graph = typename boost::adjacency_list < boost::vecS, boost::vecS, boost::directedS
, boost::property < vertex_filtration_t, typename SimplicialComplexForProximityGraph::Filtration_value >
, boost::property < edge_filtration_t, typename SimplicialComplexForProximityGraph::Filtration_value >>;
diff --git a/src/common/include/gudhi/reader_utils.h b/src/common/include/gudhi/reader_utils.h
index 26eeb76d..0ee7649d 100644
--- a/src/common/include/gudhi/reader_utils.h
+++ b/src/common/include/gudhi/reader_utils.h
@@ -172,10 +172,10 @@ bool read_simplex(std::istream& in_, std::vector<Vertex_handle>& simplex, Filtra
if (!(in_ >> dim)) return false;
Vertex_handle v;
for (int i = 0; i < dim + 1; ++i) {
- in_ >> v;
+ if (!(in_ >> v)) return false;
simplex.push_back(v);
}
- in_ >> fil;
+ if (!(in_ >> fil)) return false;
in_.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); // ignore until the carriage return
return true;
}
diff --git a/src/cython/CMakeLists.txt b/src/cython/CMakeLists.txt
index 1849a6ec..480332d7 100644
--- a/src/cython/CMakeLists.txt
+++ b/src/cython/CMakeLists.txt
@@ -31,362 +31,409 @@ 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(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(MATPLOTLIB_FOUND AND NUMPY_FOUND)
+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;")
- else()
- set(GUDHI_CYTHON_MISSING_MODULES "${GUDHI_CYTHON_MODULES}persistence_graphical_tools;")
- endif()
+ 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()
- 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()
+ 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()
- 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()
+ 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()
- 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}', ")
+ 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()
- # 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,
- 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)
- 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', ")
- 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)
+ if(UNIX AND WITH_GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS)
+ set( GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS "${GUDHI_CYTHON_LIBRARY_DIRS}")
+ endif(UNIX AND WITH_GUDHI_CYTHON_RUNTIME_LIBRARY_DIRS)
- if(MATPLOTLIB_FOUND AND NUMPY_FOUND)
- # Tangential
- add_test(NAME tangential_complex_plain_homology_from_off_file_example_py_test
+ # 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")
+
+ install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py install)")
+
+ # 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/tangential_complex_plain_homology_from_off_file_example.py"
- --no-diagram -i 2 -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off)
+ ${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_tangential_complex)
+ add_gudhi_py_test(test_bottleneck_distance)
- # Witness complex AND Subsampling
- add_test(NAME euclidean_strong_witness_complex_diagram_persistence_from_off_file_example_py_test
+ # 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/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)
+ ${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 euclidean_witness_complex_diagram_persistence_from_off_file_example_py_test
+ 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/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()
+ ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/example/coordinate_graph_induced_complex.py"
+ -f human.off -c 0 -v)
- # Subsampling
- add_gudhi_py_test(test_subsampling)
+ 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)
- 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_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_bottleneck_distance)
+ add_gudhi_py_test(test_cover_complex)
+ endif (NOT CGAL_VERSION VERSION_LESS 4.8.1)
- # 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)
+ 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_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)
+ 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_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_gudhi_py_test(test_alpha_complex)
- 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)
+ endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
- 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.6.0)
+ # Euclidean witness
+ add_gudhi_py_test(test_euclidean_witness_complex)
- 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")
+ endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0)
- if(MATPLOTLIB_FOUND AND NUMPY_FOUND)
- add_test(NAME alpha_complex_diagram_persistence_from_off_file_example_py_test
+ # 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/alpha_complex_diagram_persistence_from_off_file_example.py"
- --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -a 0.6)
- endif()
+ ${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_gudhi_py_test(test_alpha_complex)
-
- endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.7.0)
+ 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()
- if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0)
- # Euclidean witness
- add_gudhi_py_test(test_euclidean_witness_complex)
+ add_gudhi_py_test(test_cubical_complex)
- endif (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.6.0)
+ # 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)
- # 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 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()
- if(NUMPY_FOUND)
- add_test(NAME random_cubical_complex_persistence_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/random_cubical_complex_persistence_example.py"
- 10 10 10)
- endif()
+ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_from_points_example.py)
- add_gudhi_py_test(test_cubical_complex)
+ add_gudhi_py_test(test_rips_complex)
- # Rips
- if(MATPLOTLIB_FOUND AND NUMPY_FOUND)
- add_test(NAME rips_complex_diagram_persistence_from_distance_matrix_file_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/rips_complex_diagram_persistence_from_distance_matrix_file_example.py"
- --no-diagram -f ${CMAKE_SOURCE_DIR}/data/distance_matrix/lower_triangular_distance_matrix.csv -e 12.0 -d 3)
-
- add_test(NAME rips_complex_diagram_persistence_from_off_file_example_py_test
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}"
- ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_diagram_persistence_from_off_file_example.py
- --no-diagram -f ${CMAKE_SOURCE_DIR}/data/points/tore3D_300.off -e 0.25 -d 3)
- endif()
-
- add_test(NAME rips_complex_from_points_example_py_test
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}"
- ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/rips_complex_from_points_example.py)
+ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/simplex_tree_example.py)
- add_gudhi_py_test(test_rips_complex)
+ add_gudhi_py_test(test_simplex_tree)
- # 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 MATPLOTLIB_FOUND AND NUMPY_FOUND AND NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.8.1)
- set (GUDHI_SPHINX_MESSAGE "Generating API documentation with Sphinx in ${CMAKE_CURRENT_BINARY_DIR}/sphinx/")
- # User warning - Sphinx is a static pages generator, and configured to work fine with user_version
- # Images and biblio warnings because not found on developper version
- 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
+ # 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}"
- ${SPHINX_PATH} -b doctest ${CMAKE_CURRENT_SOURCE_DIR}/doc ${CMAKE_CURRENT_BINARY_DIR}/doctest)
-
- endif()
-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 314bd6db..d7be936f 100755..100644
--- a/src/cython/cython/persistence_graphical_tools.py
+++ b/src/cython/cython/persistence_graphical_tools.py
@@ -24,65 +24,70 @@ __author__ = "Vincent Rouvreau, Bertrand Michel"
__copyright__ = "Copyright (C) 2016 Inria"
__license__ = "GPL v3"
-try:
- import matplotlib.pyplot as plt
- import matplotlib.patches as mpatches
- import numpy as np
- import os
-
- def __min_birth_max_death(persistence, band=0.):
- """This function returns (min_birth, max_death) from the persistence.
-
- :param persistence: The persistence to plot.
- :type persistence: list of tuples(dimension, tuple(birth, death)).
- :param band: band
- :type band: float.
- :returns: (float, float) -- (min_birth, max_death).
- """
- # Look for minimum birth date and maximum death date for plot optimisation
- max_death = 0
- min_birth = persistence[0][1][0]
- for interval in reversed(persistence):
- if float(interval[1][1]) != float('inf'):
- if float(interval[1][1]) > max_death:
- max_death = float(interval[1][1])
- if float(interval[1][0]) > max_death:
- max_death = float(interval[1][0])
- if float(interval[1][0]) < min_birth:
- min_birth = float(interval[1][0])
- if band > 0.:
- max_death += band
- return (min_birth, max_death)
+def __min_birth_max_death(persistence, band=0.):
+ """This function returns (min_birth, max_death) from the persistence.
+ :param persistence: The persistence to plot.
+ :type persistence: list of tuples(dimension, tuple(birth, death)).
+ :param band: band
+ :type band: float.
+ :returns: (float, float) -- (min_birth, max_death).
"""
- Only 13 colors for the palette
+ # Look for minimum birth date and maximum death date for plot optimisation
+ max_death = 0
+ min_birth = persistence[0][1][0]
+ for interval in reversed(persistence):
+ if float(interval[1][1]) != float('inf'):
+ if float(interval[1][1]) > max_death:
+ max_death = float(interval[1][1])
+ if float(interval[1][0]) > max_death:
+ max_death = float(interval[1][0])
+ if float(interval[1][0]) < min_birth:
+ min_birth = float(interval[1][0])
+ if band > 0.:
+ max_death += band
+ return (min_birth, max_death)
+
+"""
+Only 13 colors for the palette
+"""
+palette = ['#ff0000', '#00ff00', '#0000ff', '#00ffff', '#ff00ff', '#ffff00',
+ '#000000', '#880000', '#008800', '#000088', '#888800', '#880088',
+ '#008888']
+
+def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6,
+ max_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 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).
+ :type alpha: float.
+ :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).
"""
- palette = ['#ff0000', '#00ff00', '#0000ff', '#00ffff', '#ff00ff', '#ffff00',
- '#000000', '#880000', '#008800', '#000088', '#888800', '#880088',
- '#008888']
-
- def plot_persistence_barcode(persistence=[], persistence_file='', alpha=0.6,
- max_barcodes=1000, inf_delta=0.1, legend=False):
- """This function plots the persistence bar code from persistence values list
- or from a :doc:`persistence file <fileformats>`.
-
- :param persistence: Persistence values list.
- :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).
- :type alpha: float.
- :param max_barcodes: number of maximal barcodes to be displayed.
- Set it to 0 to see all, Default value is 1000.
- (persistence will be sorted by life time if max_barcodes is set)
- :type max_barcodes: int.
- :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta).
- A reasonable value is between 0.05 and 0.5 - default is 0.1.
- :type inf_delta: float.
- :returns: A matplotlib object containing horizontal bar plot of persistence
- (launch `show()` method on it to display it).
- """
+ 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
@@ -95,9 +100,13 @@ try:
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]
+ 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])
@@ -134,30 +143,43 @@ try:
plt.axis([axis_start, infinity, 0, ind])
return plt
- def plot_persistence_diagram(persistence=[], persistence_file='', alpha=0.6,
- band=0., max_plots=1000, inf_delta=0.1, legend=False):
- """This function plots the persistence diagram from persistence values list
- or from a :doc:`persistence file <fileformats>`.
-
- :param persistence: Persistence values list.
- :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).
- :type alpha: float.
- :param band: band (not displayed if :math:`\leq` 0. - default is 0.)
- :type band: float.
- :param max_plots: number of maximal plots to be displayed
- Set it to 0 to see all, Default value is 1000.
- (persistence will be sorted by life time if max_plots is set)
- :type max_plots: int.
- :param inf_delta: Infinity is placed at ((max_death - min_birth) x inf_delta).
- A reasonable value is between 0.05 and 0.5 - default is 0.1.
- :type inf_delta: float.
- :returns: A matplotlib object containing diagram plot of persistence
- (launch `show()` method on it to display it).
- """
+ 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_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 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).
+ :type alpha: float.
+ :param band: band (not displayed if :math:`\leq` 0. - default is 0.)
+ :type band: float.
+ :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).
+ """
+ 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
@@ -170,12 +192,15 @@ try:
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]
+ 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)
- ind = 0
delta = ((max_death - min_birth) * inf_delta)
# Replace infinity values with max_death + delta for diagram to be more
# readable
@@ -202,7 +227,6 @@ try:
# 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))
@@ -215,6 +239,110 @@ try:
plt.axis([axis_start, infinity, axis_start, infinity + delta])
return plt
-except ImportError:
- # Continue in case of import error, functions won't be available
- pass
+ 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:
+ 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/rips_complex.pyx b/src/cython/cython/rips_complex.pyx
index 30ca4443..7c83241c 100644
--- a/src/cython/cython/rips_complex.pyx
+++ b/src/cython/cython/rips_complex.pyx
@@ -33,7 +33,11 @@ __license__ = "GPL v3"
cdef extern from "Rips_complex_interface.h" namespace "Gudhi":
cdef cppclass Rips_complex_interface "Gudhi::rips_complex::Rips_complex_interface":
- Rips_complex_interface(vector[vector[double]] values, double threshold, bool euclidean)
+ Rips_complex_interface()
+ void init_points(vector[vector[double]] values, double threshold)
+ void init_matrix(vector[vector[double]] values, double threshold)
+ void init_points_sparse(vector[vector[double]] values, double threshold, double sparse)
+ void init_matrix_sparse(vector[vector[double]] values, double threshold, double sparse)
void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, int dim_max)
# RipsComplex python interface
@@ -44,10 +48,11 @@ cdef class RipsComplex:
function, or a distance matrix.
"""
- cdef Rips_complex_interface * thisptr
+ cdef Rips_complex_interface thisref
# Fake constructor that does nothing but documenting the constructor
- def __init__(self, points=None, distance_matrix=None, max_edge_length=float('inf')):
+ def __init__(self, points=None, distance_matrix=None,
+ max_edge_length=float('inf'), sparse=None):
"""RipsComplex constructor.
:param max_edge_length: Rips value.
@@ -59,29 +64,38 @@ cdef class RipsComplex:
Or
:param distance_matrix: A distance matrix (full square or lower
- triangular).
+ triangular).
:type points: list of list of double
+
+ And in both cases
+
+ :param sparse: If this is not None, it switches to building a sparse
+ Rips and represents the approximation parameter epsilon.
+ :type sparse: float
"""
# The real cython constructor
- def __cinit__(self, points=None, distance_matrix=None, max_edge_length=float('inf')):
- if distance_matrix is not None:
- self.thisptr = new Rips_complex_interface(distance_matrix, max_edge_length, False)
+ def __cinit__(self, points=None, distance_matrix=None,
+ max_edge_length=float('inf'), sparse=None):
+ if sparse is not None:
+ if distance_matrix is not None:
+ self.thisref.init_matrix_sparse(distance_matrix,
+ max_edge_length,
+ sparse)
+ else:
+ if points is None:
+ # Empty Rips construction
+ points=[]
+ self.thisref.init_points_sparse(points, max_edge_length, sparse)
else:
- if points is None:
- # Empty Rips construction
- points=[]
- self.thisptr = new Rips_complex_interface(points, max_edge_length, True)
-
-
- def __dealloc__(self):
- if self.thisptr != NULL:
- del self.thisptr
+ if distance_matrix is not None:
+ self.thisref.init_matrix(distance_matrix, max_edge_length)
+ else:
+ if points is None:
+ # Empty Rips construction
+ points=[]
+ self.thisref.init_points(points, max_edge_length)
- def __is_defined(self):
- """Returns true if RipsComplex pointer is not NULL.
- """
- return self.thisptr != NULL
def create_simplex_tree(self, max_dimension=1):
"""
@@ -92,5 +106,5 @@ cdef class RipsComplex:
:rtype: SimplexTree
"""
simplex_tree = SimplexTree()
- self.thisptr.create_simplex_tree(simplex_tree.thisptr, max_dimension)
+ self.thisref.create_simplex_tree(simplex_tree.thisptr, max_dimension)
return simplex_tree
diff --git a/src/cython/cython/simplex_tree.pyx b/src/cython/cython/simplex_tree.pyx
index e302486b..0ab97f80 100644
--- a/src/cython/cython/simplex_tree.pyx
+++ b/src/cython/cython/simplex_tree.pyx
@@ -44,8 +44,8 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi":
void set_dimension(int dimension)
int dimension()
int upper_bound_dimension()
- bint find_simplex(vector[int] simplex)
- bint insert_simplex_and_subfaces(vector[int] simplex,
+ bool find_simplex(vector[int] simplex)
+ bool insert_simplex_and_subfaces(vector[int] simplex,
double filtration)
vector[pair[vector[int], double]] get_filtration()
vector[pair[vector[int], double]] get_skeleton(int dimension)
@@ -355,7 +355,7 @@ cdef class SimplexTree:
:param filtration: Maximum threshold value.
:type filtration: float.
:returns: The filtration modification information.
- :rtype: bint
+ :rtype: bool
.. note::
@@ -406,7 +406,7 @@ cdef class SimplexTree:
value than its faces by increasing the filtration values.
:returns: The filtration modification information.
- :rtype: bint
+ :rtype: bool
.. note::
@@ -432,6 +432,10 @@ cdef class SimplexTree:
0.0.
Sets min_persistence to -1.0 to see all values.
:type min_persistence: float.
+ :param persistence_dim_max: If true, the persistent homology for the
+ maximal dimension in the complex is computed. If false, it is
+ ignored. Default is false.
+ :type persistence_dim_max: bool
:returns: The persistence of the simplicial complex.
:rtype: list of pairs(dimension, pair(birth, death))
"""
@@ -515,7 +519,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/strong_witness_complex.pyx b/src/cython/cython/strong_witness_complex.pyx
index 74c5cb05..4b7bff34 100644
--- a/src/cython/cython/strong_witness_complex.pyx
+++ b/src/cython/cython/strong_witness_complex.pyx
@@ -47,8 +47,10 @@ cdef class StrongWitnessComplex:
def __init__(self, nearest_landmark_table=None):
"""StrongWitnessComplex constructor.
- :param nearest_landmark_table: A list of nearest landmark.
- :type nearest_landmark_table: list of list of pair of unsigned and double
+ :param nearest_landmark_table: A list of lists of nearest landmarks and their distances.
+ `nearest_landmark_table[w][k]==(l,d)` means that l is the k-th nearest landmark to
+ witness w, and d is the (squared) distance between l and w.
+ :type nearest_landmark_table: list of list of pair of int and float
"""
# The real cython constructor
@@ -65,10 +67,10 @@ cdef class StrongWitnessComplex:
"""
return self.thisptr != NULL
- def create_simplex_tree(self, max_alpha_square, limit_dimension = -1):
+ def create_simplex_tree(self, max_alpha_square = float('inf'), limit_dimension = -1):
"""
- :param max_alpha_square: The maximum alpha square threshold the
- simplices shall not exceed. Default is set to infinity.
+ :param max_alpha_square: The maximum relaxation parameter.
+ Default is set to infinity.
:type max_alpha_square: float
:returns: A simplex tree created from the Delaunay Triangulation.
:rtype: SimplexTree
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/cython/witness_complex.pyx b/src/cython/cython/witness_complex.pyx
index 8591465a..b1cce83f 100644
--- a/src/cython/cython/witness_complex.pyx
+++ b/src/cython/cython/witness_complex.pyx
@@ -47,8 +47,10 @@ cdef class WitnessComplex:
def __init__(self, nearest_landmark_table=None):
"""WitnessComplex constructor.
- :param nearest_landmark_table: A list of nearest landmark.
- :type nearest_landmark_table: list of list of pair of unsigned and double
+ :param nearest_landmark_table: A list of lists of nearest landmarks and their distances.
+ `nearest_landmark_table[w][k]==(l,d)` means that l is the k-th nearest landmark to
+ witness w, and d is the (squared) distance between l and w.
+ :type nearest_landmark_table: list of list of pair of int and float
"""
# The real cython constructor
@@ -65,10 +67,10 @@ cdef class WitnessComplex:
"""
return self.thisptr != NULL
- def create_simplex_tree(self, max_alpha_square, limit_dimension = -1):
+ def create_simplex_tree(self, max_alpha_square = float('inf'), limit_dimension = -1):
"""
- :param max_alpha_square: The maximum alpha square threshold the
- simplices shall not exceed. Default is set to infinity.
+ :param max_alpha_square: The maximum relaxation parameter.
+ Default is set to infinity.
:type max_alpha_square: float
:returns: A simplex tree created from the Delaunay Triangulation.
:rtype: SimplexTree
diff --git a/src/cython/doc/cubical_complex_user.rst b/src/cython/doc/cubical_complex_user.rst
index 320bd79b..19120360 100644
--- a/src/cython/doc/cubical_complex_user.rst
+++ b/src/cython/doc/cubical_complex_user.rst
@@ -83,9 +83,15 @@ Input Format.
In the current implantation, filtration is given at the maximal cubes, and it is then extended by the lower star
filtration to all cubes. There are a number of constructors that can be used to construct cubical complex by users
who want to use the code directly. They can be found in the :doc:`cubical_complex_ref`.
-Currently one input from a text file is used. It uses a format used already in
+Currently one input from a text file is used. It uses a format inspired from the Perseus software
`Perseus software <http://www.sas.upenn.edu/~vnanda/perseus/>`_ by Vidit Nanda.
-The file format is described here: :doc:`Perseus <fileformats>`.
+
+.. note::
+ While Perseus assume the filtration of all maximal cubes to be non-negative, over here we do not enforce this and
+ we allow any filtration values. As a consequence one cannot use ``-1``'s to indicate missing cubes. If you have
+ missing cubes in your complex, please set their filtration to :math:`+\infty` (aka. ``inf`` in the file).
+
+The file format is described in details in :ref:`Perseus file format` file format section.
.. testcode::
diff --git a/src/cython/doc/examples.rst b/src/cython/doc/examples.rst
index 1f02f8a2..edbc2f72 100644
--- a/src/cython/doc/examples.rst
+++ b/src/cython/doc/examples.rst
@@ -22,6 +22,7 @@ Examples
* :download:`rips_complex_diagram_persistence_from_off_file_example.py <../example/rips_complex_diagram_persistence_from_off_file_example.py>`
* :download:`rips_complex_diagram_persistence_from_distance_matrix_file_example.py <../example/rips_complex_diagram_persistence_from_distance_matrix_file_example.py>`
* :download:`rips_persistence_diagram.py <../example/rips_persistence_diagram.py>`
+ * :download:`sparse_rips_persistence_diagram.py <../example/sparse_rips_persistence_diagram.py>`
* :download:`random_cubical_complex_persistence_example.py <../example/random_cubical_complex_persistence_example.py>`
* :download:`coordinate_graph_induced_complex.py <../example/coordinate_graph_induced_complex.py>`
* :download:`functional_graph_induced_complex.py <../example/functional_graph_induced_complex.py>`
diff --git a/src/cython/doc/fileformats.rst b/src/cython/doc/fileformats.rst
index ff20f26e..e205cc8b 100644
--- a/src/cython/doc/fileformats.rst
+++ b/src/cython/doc/fileformats.rst
@@ -51,10 +51,12 @@ Here is a simple sample file in the 3D case::
1. 1. 1.
+.. _Perseus file format:
+
Perseus
*******
-This file format is the format used by the
+This file format is a format inspired from the
`Perseus software <http://www.sas.upenn.edu/~vnanda/perseus/>`_ by Vidit Nanda.
The first line contains a number d begin the dimension of the bitmap (2 in the
example below). Next d lines are the numbers of top dimensional cubes in each
@@ -88,3 +90,9 @@ Indicate that we have imposed periodic boundary conditions in the direction x,
but not in the direction y.
Other sample files can be found in the `data/bitmap` folder.
+
+.. note::
+ Unlike in Perseus format the filtration on the maximal cubes can be any
+ double precision number. Consequently one cannot mark the cubes that are
+ not present with ``-1``'s. To do that please set their filtration value to
+ :math:`+\infty` (aka. ``inf`` in the file). \ No newline at end of file
diff --git a/src/cython/doc/installation.rst b/src/cython/doc/installation.rst
index 43576ec9..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.48.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>`_.
@@ -195,7 +219,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>`
@@ -216,6 +240,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_ref.rst b/src/cython/doc/nerve_gic_complex_ref.rst
index e24e01fc..abde2e8c 100644
--- a/src/cython/doc/nerve_gic_complex_ref.rst
+++ b/src/cython/doc/nerve_gic_complex_ref.rst
@@ -1,3 +1,7 @@
+:orphan:
+
+.. To get rid of WARNING: document isn't included in any toctree
+
================================
Cover complexes reference manual
================================
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/nerve_gic_complex_user.rst b/src/cython/doc/nerve_gic_complex_user.rst
index d774827e..44f30e1a 100644
--- a/src/cython/doc/nerve_gic_complex_user.rst
+++ b/src/cython/doc/nerve_gic_complex_user.rst
@@ -1,3 +1,7 @@
+:orphan:
+
+.. To get rid of WARNING: document isn't included in any toctree
+
Cover complexes user manual
===========================
Definition
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/doc/persistent_cohomology_user.rst b/src/cython/doc/persistent_cohomology_user.rst
index ce7fc685..de83cda1 100644
--- a/src/cython/doc/persistent_cohomology_user.rst
+++ b/src/cython/doc/persistent_cohomology_user.rst
@@ -10,12 +10,14 @@ Definition
:Author: Clément Maria :Introduced in: GUDHI PYTHON 2.0.0 :Copyright: GPL v3
===================================== ===================================== =====================================
-+---------------------------------------------+----------------------------------------------------------------------+
-| :doc:`persistent_cohomology_user` | Please refer to each data structure that contains persistence |
-| | feature for reference: |
-| | |
-| | * :doc:`simplex_tree_ref` |
-+---------------------------------------------+----------------------------------------------------------------------+
++-----------------------------------------------------------------+-----------------------------------------------------------------------+
+| :doc:`persistent_cohomology_user` | Please refer to each data structure that contains persistence |
+| | feature for reference: |
+| | |
+| | * :doc:`simplex_tree_ref` |
+| | * :doc:`cubical_complex_ref` |
+| | * :doc:`periodic_cubical_complex_ref` |
++-----------------------------------------------------------------+-----------------------------------------------------------------------+
Computation of persistent cohomology using the algorithm of :cite:`DBLP:journals/dcg/SilvaMV11` and
diff --git a/src/cython/doc/rips_complex_sum.inc b/src/cython/doc/rips_complex_sum.inc
index 5616bfa9..ea26769a 100644
--- a/src/cython/doc/rips_complex_sum.inc
+++ b/src/cython/doc/rips_complex_sum.inc
@@ -1,6 +1,6 @@
-================================================================= =================================== ===================================
-:Author: Clément Maria, Pawel Dlotko, Vincent Rouvreau :Introduced in: GUDHI 2.0.0 :Copyright: GPL v3
-================================================================= =================================== ===================================
+===================================================================== =========================== ===================================
+:Author: Clément Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse :Introduced in: GUDHI 2.0.0 :Copyright: GPL v3
+===================================================================== =========================== ===================================
+----------------------------------------------------------------+------------------------------------------------------------------------+
| .. figure:: | Rips complex is a simplicial complex constructed from a one skeleton |
diff --git a/src/cython/doc/rips_complex_user.rst b/src/cython/doc/rips_complex_user.rst
index a8c06cf9..1d340dbe 100644
--- a/src/cython/doc/rips_complex_user.rst
+++ b/src/cython/doc/rips_complex_user.rst
@@ -7,27 +7,27 @@ Rips complex user manual
Definition
----------
-======================================================= ===================================== =====================================
-:Authors: Clément Maria, Pawel Dlotko, Vincent Rouvreau :Introduced in: GUDHI 2.0.0 :Copyright: GPL v3
-======================================================= ===================================== =====================================
+==================================================================== ================================ ======================
+:Authors: Clément Maria, Pawel Dlotko, Vincent Rouvreau, Marc Glisse :Introduced in: GUDHI 2.0.0 :Copyright: GPL v3
+==================================================================== ================================ ======================
+-------------------------------------------+----------------------------------------------------------------------+
| :doc:`rips_complex_user` | :doc:`rips_complex_ref` |
+-------------------------------------------+----------------------------------------------------------------------+
-`Rips complex <https://en.wikipedia.org/wiki/Vietoris%E2%80%93Rips_complex>`_ is a one skeleton graph that allows to
-construct a simplicial complex from it. The input can be a point cloud with a given distance function, or a distance
-matrix.
+The `Rips complex <https://en.wikipedia.org/wiki/Vietoris%E2%80%93Rips_complex>`_ is a simplicial complex that
+generalizes proximity (:math:`\varepsilon`-ball) graphs to higher dimensions. The vertices correspond to the input
+points, and a simplex is present if and only if its diameter is smaller than some parameter α. Considering all
+parameters α defines a filtered simplicial complex, where the filtration value of a simplex is its diameter.
+The filtration can be restricted to values α smaller than some threshold, to reduce its size.
-The filtration value of each edge is computed from a user-given distance function, or directly from the distance
-matrix.
+The input discrete metric space can be provided as a point cloud plus a distance function, or as a distance matrix.
-All edges that have a filtration value strictly greater than a given threshold value are not inserted into the complex.
+When creating a simplicial complex from the graph, :doc:`RipsComplex <rips_complex_ref>` first builds the graph and
+inserts it into the data structure. It then expands the simplicial complex (adds the simplices corresponding to cliques)
+when required. The expansion can be stopped at dimension `max_dimension`, by default 1.
-When creating a simplicial complex from this one skeleton graph, Rips inserts the one skeleton graph into the data
-structure, and then expands the simplicial complex when required.
-
-Vertex name correspond to the index of the point in the given range (aka. the point cloud).
+A vertex name corresponds to the index of the point in the given range (aka. the point cloud).
.. figure::
../../doc/Rips_complex/rips_complex_representation.png
@@ -38,8 +38,27 @@ Vertex name correspond to the index of the point in the given range (aka. the po
On this example, as edges (4,5), (4,6) and (5,6) are in the complex, simplex (4,5,6) is added with the filtration value
set with :math:`max(filtration(4,5), filtration(4,6), filtration(5,6))`. And so on for simplex (0,1,2,3).
-If the Rips_complex interfaces are not detailed enough for your need, please refer to rips_persistence_step_by_step.cpp
-example, where the graph construction over the Simplex_tree is more detailed.
+If the `RipsComplex` interfaces are not detailed enough for your need, please refer to rips_persistence_step_by_step.cpp
+C++ example, where the graph construction over the Simplex_tree is more detailed.
+
+A Rips complex can easily become huge, even if we limit the length of the edges
+and the dimension of the simplices. One easy trick, before building a Rips
+complex on a point cloud, is to call `sparsify_point_set` which removes points
+that are too close to each other. This does not change its persistence diagram
+by more than the length used to define "too close".
+
+A more general technique is to use a sparse approximation of the Rips
+introduced by Don Sheehy :cite:`sheehy13linear`. We are using the version
+described in :cite:`buchet16efficient` (except that we multiply all filtration
+values by 2, to match the usual Rips complex). :cite:`cavanna15geometric` proves
+a :math:`\frac{1}{1-\varepsilon}`-interleaving, although in practice the
+error is usually smaller. A more intuitive presentation of the idea is
+available in :cite:`cavanna15geometric`, and in a video
+:cite:`cavanna15visualizing`. Passing an extra argument `sparse=0.3` at the
+construction of a `RipsComplex` object asks it to build a sparse Rips with
+parameter :math:`\varepsilon=0.3`, while the default `sparse=None` builds the
+regular Rips complex.
+
Point cloud
-----------
@@ -47,7 +66,7 @@ Point cloud
Example from a point cloud
^^^^^^^^^^^^^^^^^^^^^^^^^^
-This example builds the one skeleton graph from the given points, and max_edge_length value.
+This example builds the neighborhood graph from the given points, up to max_edge_length.
Then it creates a :doc:`Simplex_tree <simplex_tree_ref>` with it.
Finally, it is asked to display information about the simplicial complex.
@@ -56,7 +75,7 @@ Finally, it is asked to display information about the simplicial complex.
import gudhi
rips_complex = gudhi.RipsComplex(points=[[1, 1], [7, 0], [4, 6], [9, 6], [0, 14], [2, 19], [9, 17]],
- max_edge_length=12.0)
+ max_edge_length=12.0)
simplex_tree = rips_complex.create_simplex_tree(max_dimension=1)
result_str = 'Rips complex is of dimension ' + repr(simplex_tree.dimension()) + ' - ' + \
@@ -92,10 +111,20 @@ until dimension 1 - one skeleton graph in other words), the output is:
[4, 6] -> 9.49
[3, 6] -> 11.00
+Notice that if we use
+
+.. code-block:: python
+
+ rips_complex = gudhi.RipsComplex(points=[[1, 1], [7, 0], [4, 6], [9, 6], [0, 14], [2, 19], [9, 17]],
+ max_edge_length=12.0, sparse=2)
+
+asking for a very sparse version (theory only gives some guarantee on the meaning of the output if `sparse<1`),
+2 to 5 edges disappear, depending on the random vertex used to start the sparsification.
+
Example from OFF file
^^^^^^^^^^^^^^^^^^^^^
-This example builds the :doc:`Rips_complex <rips_complex_ref>` from the given
+This example builds the :doc:`RipsComplex <rips_complex_ref>` from the given
points in an OFF file, and max_edge_length value.
Then it creates a :doc:`Simplex_tree <simplex_tree_ref>` with it.
@@ -200,7 +229,7 @@ until dimension 1 - one skeleton graph in other words), the output is:
Example from csv file
^^^^^^^^^^^^^^^^^^^^^
-This example builds the :doc:`Rips_complex <rips_complex_ref>` from the given
+This example builds the :doc:`RipsComplex <rips_complex_ref>` from the given
distance matrix in a csv file, and max_edge_length value.
Then it creates a :doc:`Simplex_tree <simplex_tree_ref>` with it.
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/sparse_rips_persistence_diagram.py b/src/cython/example/sparse_rips_persistence_diagram.py
new file mode 100755
index 00000000..d58c244c
--- /dev/null
+++ b/src/cython/example/sparse_rips_persistence_diagram.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+import gudhi
+
+"""This file is part of the Gudhi Library. The Gudhi library
+ (Geometric Understanding in Higher Dimensions) is a generic C++
+ library for computational topology.
+
+ Author(s): Marc Glisse
+
+ Copyright (C) 2018 Inria
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+
+__author__ = "Marc Glisse"
+__copyright__ = "Copyright (C) 2018 Inria"
+__license__ = "GPL v3"
+
+print("#####################################################################")
+print("Sparse RipsComplex creation from points")
+rips = gudhi.RipsComplex(points=[[0, 0], [0, 0.1], [1, 0], [0, 1], [1, 1]],
+ max_edge_length=42, sparse=.5)
+
+simplex_tree = rips.create_simplex_tree(max_dimension=2)
+
+
+diag = simplex_tree.persistence(homology_coeff_field=2, min_persistence=0)
+print("diag=", diag)
+
+pplot = gudhi.plot_persistence_diagram(diag)
+pplot.show()
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/example/witness_complex_from_nearest_landmark_table.py b/src/cython/example/witness_complex_from_nearest_landmark_table.py
index e6b295ee..1b79d9b2 100755
--- a/src/cython/example/witness_complex_from_nearest_landmark_table.py
+++ b/src/cython/example/witness_complex_from_nearest_landmark_table.py
@@ -30,14 +30,14 @@ __license__ = "GPL v3"
print("#####################################################################")
print("WitnessComplex creation from nearest landmark table")
-nearest_landmark_table = [[[0, 0], [1, 1], [2, 2], [3, 3], [4, 4]],
- [[1, 0], [2, 1], [3, 2], [4, 3], [0, 4]],
- [[2, 0], [3, 1], [4, 2], [0, 3], [1, 4]],
- [[3, 0], [4, 1], [0, 2], [1, 3], [2, 4]],
- [[4, 0], [0, 1], [1, 2], [2, 3], [3, 4]]]
+nearest_landmark_table = [[[0, 0.0], [1, 0.1], [2, 0.2], [3, 0.3], [4, 0.4]],
+ [[1, 0.0], [2, 0.1], [3, 0.2], [4, 0.3], [0, 0.4]],
+ [[2, 0.0], [3, 0.1], [4, 0.2], [0, 0.3], [1, 0.4]],
+ [[3, 0.0], [4, 0.1], [0, 0.2], [1, 0.3], [2, 0.4]],
+ [[4, 0.0], [0, 0.1], [1, 0.2], [2, 0.3], [3, 0.4]]]
witness_complex = StrongWitnessComplex(nearest_landmark_table=nearest_landmark_table)
-simplex_tree = witness_complex.create_simplex_tree(max_alpha_square=4.1)
+simplex_tree = witness_complex.create_simplex_tree(max_alpha_square=0.41)
message = "Number of simplices: " + repr(simplex_tree.num_simplices())
print(message)
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/Rips_complex_interface.h b/src/cython/include/Rips_complex_interface.h
index 8b6c9c35..40aff299 100644
--- a/src/cython/include/Rips_complex_interface.h
+++ b/src/cython/include/Rips_complex_interface.h
@@ -25,8 +25,11 @@
#include <gudhi/Simplex_tree.h>
#include <gudhi/Rips_complex.h>
+#include <gudhi/Sparse_rips_complex.h>
#include <gudhi/distance_functions.h>
+#include <boost/optional.hpp>
+
#include "Simplex_tree_interface.h"
#include <iostream>
@@ -43,28 +46,33 @@ class Rips_complex_interface {
using Distance_matrix = std::vector<std::vector<Simplex_tree_interface<>::Filtration_value>>;
public:
- Rips_complex_interface(const std::vector<std::vector<double>>& values, double threshold, bool euclidean) {
- if (euclidean) {
- // Rips construction where values is a vector of points
- rips_complex_ = new Rips_complex<Simplex_tree_interface<>::Filtration_value>(values, threshold,
- Gudhi::Euclidean_distance());
- } else {
- // Rips construction where values is a distance matrix
- rips_complex_ = new Rips_complex<Simplex_tree_interface<>::Filtration_value>(values, threshold);
- }
+ void init_points(const std::vector<std::vector<double>>& points, double threshold) {
+ rips_complex_.emplace(points, threshold, Gudhi::Euclidean_distance());
+ }
+ void init_matrix(const std::vector<std::vector<double>>& matrix, double threshold) {
+ rips_complex_.emplace(matrix, threshold);
}
- ~Rips_complex_interface() {
- delete rips_complex_;
+ void init_points_sparse(const std::vector<std::vector<double>>& points, double threshold, double epsilon) {
+ sparse_rips_complex_.emplace(points, Gudhi::Euclidean_distance(), epsilon, -std::numeric_limits<double>::infinity(), threshold);
+ }
+ void init_matrix_sparse(const std::vector<std::vector<double>>& matrix, double threshold, double epsilon) {
+ sparse_rips_complex_.emplace(matrix, epsilon, -std::numeric_limits<double>::infinity(), threshold);
}
void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, int dim_max) {
- rips_complex_->create_complex(*simplex_tree, dim_max);
+ if (rips_complex_)
+ rips_complex_->create_complex(*simplex_tree, dim_max);
+ else
+ sparse_rips_complex_->create_complex(*simplex_tree, dim_max);
simplex_tree->initialize_filtration();
}
private:
- Rips_complex<Simplex_tree_interface<>::Filtration_value>* rips_complex_;
+ // std::variant would work, but we don't require C++17 yet, and boost::variant is not super convenient.
+ // Anyway, storing a graph would make more sense. Or changing the interface completely so there is no such storage.
+ boost::optional<Rips_complex<Simplex_tree_interface<>::Filtration_value>> rips_complex_;
+ boost::optional<Sparse_rips_complex<Simplex_tree_interface<>::Filtration_value>> sparse_rips_complex_;
};
} // namespace rips_complex
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_rips_complex.py b/src/cython/test/test_rips_complex.py
index c37b5400..05dfcaf7 100755
--- a/src/cython/test/test_rips_complex.py
+++ b/src/cython/test/test_rips_complex.py
@@ -30,7 +30,6 @@ __license__ = "GPL v3"
def test_empty_rips():
rips_complex = RipsComplex()
- assert rips_complex.__is_defined() == True
def test_rips_from_points():
point_list = [[0, 0], [1, 0], [0, 1], [1, 1]]
@@ -68,12 +67,26 @@ def test_filtered_rips_from_points():
assert simplex_tree.num_simplices() == 8
assert simplex_tree.num_vertices() == 4
+def test_sparse_filtered_rips_from_points():
+ point_list = [[0, 0], [1, 0], [0, 1], [1, 1]]
+ filtered_rips = RipsComplex(points=point_list, max_edge_length=1.0,
+ sparse=.001)
+
+ simplex_tree = filtered_rips.create_simplex_tree(max_dimension=1)
+
+ assert simplex_tree.__is_defined() == True
+ assert simplex_tree.__is_persistence_defined() == False
+
+ assert simplex_tree.num_simplices() == 8
+ assert simplex_tree.num_vertices() == 4
+
def test_rips_from_distance_matrix():
distance_matrix = [[0],
[1, 0],
[1, sqrt(2), 0],
[sqrt(2), 1, 1, 0]]
- rips_complex = RipsComplex(distance_matrix=distance_matrix, max_edge_length=42)
+ rips_complex = RipsComplex(distance_matrix=distance_matrix,
+ max_edge_length=42)
simplex_tree = rips_complex.create_simplex_tree(max_dimension=1)
@@ -100,7 +113,8 @@ def test_filtered_rips_from_distance_matrix():
[1, 0],
[1, sqrt(2), 0],
[sqrt(2), 1, 1, 0]]
- filtered_rips = RipsComplex(distance_matrix=distance_matrix, max_edge_length=1.0)
+ filtered_rips = RipsComplex(distance_matrix=distance_matrix,
+ max_edge_length=1.0)
simplex_tree = filtered_rips.create_simplex_tree(max_dimension=1)
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