diff --git a/CMakeLists.txt b/CMakeLists.txt index f9cc110dd6..54a9bda0d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (WITH_GUDHI_THIRD_PARTY) +if (WITH_GUDHI_GUDHUI) add_subdirectory(src/GudhUI) endif() diff --git a/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_dynamic_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_dynamic_unit_test.cpp new file mode 100644 index 0000000000..fb04566ab7 --- /dev/null +++ b/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_dynamic_unit_test.cpp @@ -0,0 +1,28 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Vincent Rouvreau + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "alpha_complex_dim3_exact_kernel_dynamic" +#include + +#include + +#include "Alpha_complex_dim3_unit_test.h" + +// Use dynamic_dimension_tag for the user to be able to set dimension +typedef CGAL::Epeck_d< CGAL::Dynamic_dimension_tag > Exact_kernel_d; + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_OFF_file_exact_kernel_dynamic_dimension) { + test_alpha_complex_from_OFF_file(); +} + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_empty_points_exact_kernel_dynamic_dimension) { + test_alpha_complex_from_empty_points(); +} diff --git a/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_static_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_static_unit_test.cpp new file mode 100644 index 0000000000..009899b356 --- /dev/null +++ b/src/Alpha_complex/test/Alpha_complex_dim3_Epeck_static_unit_test.cpp @@ -0,0 +1,28 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Vincent Rouvreau + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "alpha_complex_dim3_exact_kernel_static" +#include + +#include + +#include "Alpha_complex_dim3_unit_test.h" + +// Use static dimension_tag for the user not to be able to set dimension +typedef CGAL::Epeck_d< CGAL::Dimension_tag<3> > Exact_kernel_s; + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_OFF_file_exact_kernel_static_dimension) { + test_alpha_complex_from_OFF_file(); +} + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_empty_points_exact_kernel_static_dimension) { + test_alpha_complex_from_empty_points(); +} diff --git a/src/Alpha_complex/test/Alpha_complex_dim3_Epick_dynamic_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_dim3_Epick_dynamic_unit_test.cpp new file mode 100644 index 0000000000..530ad63e03 --- /dev/null +++ b/src/Alpha_complex/test/Alpha_complex_dim3_Epick_dynamic_unit_test.cpp @@ -0,0 +1,28 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Vincent Rouvreau + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "alpha_complex_dim3_inexact_kernel_dynamic" +#include + +#include + +#include "Alpha_complex_dim3_unit_test.h" + +// Use dynamic_dimension_tag for the user to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dynamic_dimension_tag > Inexact_kernel_d; + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_OFF_file_inexact_kernel_dynamic_dimension) { + test_alpha_complex_from_OFF_file(); +} + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_empty_points_inexact_kernel_dynamic_dimension) { + test_alpha_complex_from_empty_points(); +} diff --git a/src/Alpha_complex/test/Alpha_complex_dim3_Epick_static_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_dim3_Epick_static_unit_test.cpp new file mode 100644 index 0000000000..02244f315a --- /dev/null +++ b/src/Alpha_complex/test/Alpha_complex_dim3_Epick_static_unit_test.cpp @@ -0,0 +1,28 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Vincent Rouvreau + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "alpha_complex_dim3_inexact_kernel_static" +#include + +#include + +#include "Alpha_complex_dim3_unit_test.h" + +// Use static dimension_tag for the user not to be able to set dimension +typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Inexact_kernel_s; + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_OFF_file_inexact_kernel_static_dimension) { + test_alpha_complex_from_OFF_file(); +} + +BOOST_AUTO_TEST_CASE(Alpha_complex_from_empty_points_inexact_kernel_static_dimension) { + test_alpha_complex_from_empty_points(); +} diff --git a/src/Alpha_complex/test/Alpha_complex_dim3_unit_test.cpp b/src/Alpha_complex/test/Alpha_complex_dim3_unit_test.h similarity index 88% rename from src/Alpha_complex/test/Alpha_complex_dim3_unit_test.cpp rename to src/Alpha_complex/test/Alpha_complex_dim3_unit_test.h index e7c261f1c3..1a95bebb84 100644 --- a/src/Alpha_complex/test/Alpha_complex_dim3_unit_test.cpp +++ b/src/Alpha_complex/test/Alpha_complex_dim3_unit_test.h @@ -5,13 +5,11 @@ * Copyright (C) 2015 Inria * * Modification(s): + * - 2023/11 Vincent Rouvreau: Split the test * - YYYY/MM Author: Description of the modification */ -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE "alpha_complex_dim3" #include -#include #include #include @@ -23,7 +21,7 @@ #include #include -// Use dynamic_dimension_tag for the user to be able to set dimension +/*// Use dynamic_dimension_tag for the user to be able to set dimension typedef CGAL::Epeck_d< CGAL::Dynamic_dimension_tag > Exact_kernel_d; // Use static dimension_tag for the user not to be able to set dimension typedef CGAL::Epeck_d< CGAL::Dimension_tag<3> > Exact_kernel_s; @@ -34,8 +32,10 @@ typedef CGAL::Epick_d< CGAL::Dimension_tag<3> > Inexact_kernel_s; // The triangulation uses the default instantiation of the TriangulationDataStructure template parameter typedef boost::mpl::list list_of_kernel_variants; +*/ -BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of_kernel_variants) { +template +void test_alpha_complex_from_OFF_file() { // ---------------------------------------------------------------------------- // // Init of an alpha-complex from a OFF file @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of std::clog << "========== OFF FILE NAME = " << off_file_name << " - alpha²=" << max_alpha_square_value << "==========" << std::endl; - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_file(off_file_name); Gudhi::Simplex_tree<> simplex_tree_60; BOOST_CHECK(alpha_complex_from_file.create_complex(simplex_tree_60, max_alpha_square_value)); @@ -84,18 +84,19 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_OFF_file, TestedKernel, list_of } -BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_from_empty_points, TestedKernel, list_of_kernel_variants) { +template +void test_alpha_complex_from_empty_points() { std::clog << "========== Alpha_complex_from_empty_points ==========" << std::endl; // ---------------------------------------------------------------------------- // Init of an empty list of points // ---------------------------------------------------------------------------- - std::vector points; + std::vector points; // ---------------------------------------------------------------------------- // Init of an alpha complex from the list of points // ---------------------------------------------------------------------------- - Gudhi::alpha_complex::Alpha_complex alpha_complex_from_points(points); + Gudhi::alpha_complex::Alpha_complex alpha_complex_from_points(points); std::clog << "alpha_complex_from_points.num_vertices()=" << alpha_complex_from_points.num_vertices() << std::endl; BOOST_CHECK(alpha_complex_from_points.num_vertices() == points.size()); diff --git a/src/Alpha_complex/test/CMakeLists.txt b/src/Alpha_complex/test/CMakeLists.txt index 069d705797..8e70d1c585 100644 --- a/src/Alpha_complex/test/CMakeLists.txt +++ b/src/Alpha_complex/test/CMakeLists.txt @@ -8,8 +8,14 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) add_executable ( Alpha_complex_test_unit Alpha_complex_unit_test.cpp ) target_link_libraries(Alpha_complex_test_unit ${CGAL_LIBRARY}) - add_executable ( Alpha_complex_dim3_test_unit Alpha_complex_dim3_unit_test.cpp ) - target_link_libraries(Alpha_complex_dim3_test_unit ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_dim3_Epeck_dynamic_test_unit Alpha_complex_dim3_Epeck_dynamic_unit_test.cpp ) + target_link_libraries(Alpha_complex_dim3_Epeck_dynamic_test_unit ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_dim3_Epeck_static_test_unit Alpha_complex_dim3_Epeck_static_unit_test.cpp ) + target_link_libraries(Alpha_complex_dim3_Epeck_static_test_unit ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_dim3_Epick_dynamic_test_unit Alpha_complex_dim3_Epick_dynamic_unit_test.cpp ) + target_link_libraries(Alpha_complex_dim3_Epick_dynamic_test_unit ${CGAL_LIBRARY}) + add_executable ( Alpha_complex_dim3_Epick_static_test_unit Alpha_complex_dim3_Epick_static_unit_test.cpp ) + target_link_libraries(Alpha_complex_dim3_Epick_static_test_unit ${CGAL_LIBRARY}) add_executable ( Delaunay_complex_Epeck_dynamic_test_unit Delaunay_complex_Epeck_dynamic_unit_test.cpp ) target_link_libraries(Delaunay_complex_Epeck_dynamic_test_unit ${CGAL_LIBRARY}) add_executable ( Delaunay_complex_Epeck_static_test_unit Delaunay_complex_Epeck_static_unit_test.cpp ) @@ -20,7 +26,10 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) target_link_libraries(Delaunay_complex_Epick_static_test_unit ${CGAL_LIBRARY}) if(TARGET TBB::tbb) target_link_libraries(Alpha_complex_test_unit TBB::tbb) - target_link_libraries(Alpha_complex_dim3_test_unit TBB::tbb) + target_link_libraries(Alpha_complex_dim3_Epeck_dynamic_test_unit TBB::tbb) + target_link_libraries(Alpha_complex_dim3_Epeck_static_test_unit TBB::tbb) + target_link_libraries(Alpha_complex_dim3_Epick_dynamic_test_unit TBB::tbb) + target_link_libraries(Alpha_complex_dim3_Epick_static_test_unit TBB::tbb) target_link_libraries(Delaunay_complex_Epeck_dynamic_test_unit TBB::tbb) target_link_libraries(Delaunay_complex_Epeck_static_test_unit TBB::tbb) target_link_libraries(Delaunay_complex_Epick_dynamic_test_unit TBB::tbb) @@ -28,7 +37,10 @@ if (NOT CGAL_WITH_EIGEN3_VERSION VERSION_LESS 4.11.0) endif() gudhi_add_boost_test(Alpha_complex_test_unit) - gudhi_add_boost_test(Alpha_complex_dim3_test_unit) + gudhi_add_boost_test(Alpha_complex_dim3_Epeck_dynamic_test_unit) + gudhi_add_boost_test(Alpha_complex_dim3_Epeck_static_test_unit) + gudhi_add_boost_test(Alpha_complex_dim3_Epick_dynamic_test_unit) + gudhi_add_boost_test(Alpha_complex_dim3_Epick_static_test_unit) gudhi_add_boost_test(Delaunay_complex_Epeck_dynamic_test_unit) gudhi_add_boost_test(Delaunay_complex_Epeck_static_test_unit) gudhi_add_boost_test(Delaunay_complex_Epick_dynamic_test_unit) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2bf9bfebe2..52c21166ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,7 +72,7 @@ foreach(GUDHI_MODULE ${GUDHI_MODULES}) endforeach() endforeach() -if (WITH_GUDHI_THIRD_PARTY) +if (WITH_GUDHI_GUDHUI) add_subdirectory(GudhUI) endif() diff --git a/src/Nerve_GIC/utilities/CMakeLists.txt b/src/Nerve_GIC/utilities/CMakeLists.txt index 32de78a85b..ee24d2e093 100644 --- a/src/Nerve_GIC/utilities/CMakeLists.txt +++ b/src/Nerve_GIC/utilities/CMakeLists.txt @@ -8,7 +8,7 @@ if(TARGET TBB::tbb) target_link_libraries(VoronoiGIC TBB::tbb) endif() -file(COPY KeplerMapperVisuFromTxtFile.py km.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) +file(COPY KeplerMapperVisuFromTxtFile.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) # Copy files for not to pollute sources when testing file(COPY "${CMAKE_SOURCE_DIR}/data/points/human.off" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) @@ -20,4 +20,4 @@ add_test(NAME Nerve_GIC_utilities_VoronoiGIC COMMAND $ install(TARGETS Nerve DESTINATION bin) install(TARGETS VoronoiGIC DESTINATION bin) -install(FILES KeplerMapperVisuFromTxtFile.py km.py km.py.COPYRIGHT DESTINATION bin) +install(FILES KeplerMapperVisuFromTxtFile.py DESTINATION bin) diff --git a/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py b/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py index b4bbabcb79..4f5f42a0e6 100755 --- a/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py +++ b/src/Nerve_GIC/utilities/KeplerMapperVisuFromTxtFile.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -import km -import numpy as np -from collections import defaultdict import argparse +from gudhi.cover_complex import _save_to_html """This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. @@ -30,48 +28,16 @@ args = parser.parse_args() with open(args.file, 'r') as f: - network = {} - mapper = km.KeplerMapper(verbose=0) - data = np.zeros((3,3)) - projected_data = mapper.fit_transform( data, projection="sum", scaler=None ) - - nodes = defaultdict(list) - links = defaultdict(list) - custom = defaultdict(list) dat = f.readline() lens = f.readline() color = f.readline(); param = [float(i) for i in f.readline().split(" ")] - nums = [int(i) for i in f.readline().split(" ")] - num_nodes = nums[0] - num_edges = nums[1] - - for i in range(0,num_nodes): - point = [float(j) for j in f.readline().split(" ")] - nodes[ str(int(point[0])) ] = [ int(point[0]), point[1], int(point[2]) ] - links[ str(int(point[0])) ] = [] - custom[ int(point[0]) ] = point[1] - - m = min([custom[i] for i in range(0,num_nodes)]) - M = max([custom[i] for i in range(0,num_nodes)]) - - for i in range(0,num_edges): - edge = [int(j) for j in f.readline().split(" ")] - links[ str(edge[0]) ].append( str(edge[1]) ) - links[ str(edge[1]) ].append( str(edge[0]) ) - - network["nodes"] = nodes - network["links"] = links - network["meta"] = lens - + points = [[float(j) for j in f.readline().split(" ")] for i in range(0, nums[0])] + edges = [[int(j) for j in f.readline().split(" ")] for i in range(0, nums[1])] html_output_filename = args.file.rsplit('.', 1)[0] + '.html' - mapper.visualize(network, color_function=color, path_html=html_output_filename, title=dat, - graph_link_distance=30, graph_gravity=0.1, graph_charge=-120, custom_tooltips=custom, width_html=0, - height_html=0, show_tooltips=True, show_title=True, show_meta=True, res=param[0],gain=param[1], minimum=m,maximum=M) - message = repr(html_output_filename) + " is generated. You can now use your favorite web browser to visualize it." - print(message) - f.close() + +_save_to_html(dat, lens, color, param, points, edges, html_output_filename) diff --git a/src/Persistent_cohomology/example/plain_homology.cpp b/src/Persistent_cohomology/example/plain_homology.cpp index 236b67de8d..7b09c2013a 100644 --- a/src/Persistent_cohomology/example/plain_homology.cpp +++ b/src/Persistent_cohomology/example/plain_homology.cpp @@ -16,13 +16,13 @@ #include // for std::uint8_t /* We could perfectly well use the default Simplex_tree<> (which uses - * Simplex_tree_options_full_featured), the following simply demonstrates + * Simplex_tree_options_default), the following simply demonstrates * how to save on storage by not storing a filtration value. */ -struct MyOptions : Gudhi::Simplex_tree_options_full_featured { - // Implicitly use 0 as filtration value for all simplices - static const bool store_filtration = false; - // The persistence algorithm needs this +struct MyOptions : Gudhi::Simplex_tree_options_minimal { + // Implicitly use 0 as filtration value for all simplices - inherited from Simplex_tree_options_minimal + // static const bool store_filtration = false; + // However the persistence algorithm needs this static const bool store_key = true; // I have few vertices typedef short Vertex_handle; diff --git a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp index eccd17b21f..65cde75e70 100644 --- a/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp +++ b/src/Persistent_cohomology/test/betti_numbers_unit_test.cpp @@ -12,7 +12,7 @@ #include #include -struct MiniSTOptions : Gudhi::Simplex_tree_options_full_featured { +struct MiniSTOptions : Gudhi::Simplex_tree_options_minimal { // Implicitly use 0 as filtration value for all simplices static const bool store_filtration = false; // The persistence algorithm needs this diff --git a/src/Simplex_tree/benchmark/simplex_tree_cofaces_benchmark.cpp b/src/Simplex_tree/benchmark/simplex_tree_cofaces_benchmark.cpp index f6e001b631..d20ea35f2d 100644 --- a/src/Simplex_tree/benchmark/simplex_tree_cofaces_benchmark.cpp +++ b/src/Simplex_tree/benchmark/simplex_tree_cofaces_benchmark.cpp @@ -81,7 +81,7 @@ void benchmark_stars_computation(int nb_vertices) { std::clog << benchmark_cofaces << std::endl; } -struct Simplex_tree_options_stable_simplex_handles { +struct Stree_basic_cofaces_options { typedef Gudhi::linear_indexing_tag Indexing_tag; typedef int Vertex_handle; typedef double Filtration_value; @@ -89,6 +89,15 @@ struct Simplex_tree_options_stable_simplex_handles { static const bool store_key = true; static const bool store_filtration = true; static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; + static const bool stable_simplex_handles = false; +}; + +struct Stree_fast_cofaces_options : Stree_basic_cofaces_options { + static const bool link_nodes_by_label = true; +}; + +struct Stree_fast_cofaces_stable_simplex_handles_options : Stree_basic_cofaces_options { static const bool link_nodes_by_label = true; static const bool stable_simplex_handles = true; }; @@ -105,13 +114,13 @@ int main(int argc, char *argv[]) { nb_vertices = atoi(argv[1]); std::clog << "** Without cofaces computation optimization" << std::endl; - benchmark_stars_computation>(nb_vertices); + benchmark_stars_computation>(nb_vertices); std::clog << "** With cofaces computation optimization" << std::endl; - benchmark_stars_computation>(nb_vertices); + benchmark_stars_computation>(nb_vertices); std::clog << "** With cofaces computation optimization and stable simplex handles" << std::endl; - benchmark_stars_computation >(nb_vertices); + benchmark_stars_computation >(nb_vertices); return EXIT_SUCCESS; } diff --git a/src/Simplex_tree/concept/SimplexTreeOptions.h b/src/Simplex_tree/concept/SimplexTreeOptions.h index 0c95d3011c..b9e60779d5 100644 --- a/src/Simplex_tree/concept/SimplexTreeOptions.h +++ b/src/Simplex_tree/concept/SimplexTreeOptions.h @@ -8,28 +8,34 @@ * - YYYY/MM Author: Description of the modification */ -/** \brief Concept of the template parameter for the class `Gudhi::Simplex_tree`. +/** @brief Concept of the template parameter for the class `Gudhi::Simplex_tree`. * - * One model for this is `Gudhi::Simplex_tree_options_full_featured`. If you want to provide your own, it is recommended that you derive from it and override some parts instead of writing a class from scratch. + * A model for this is `Gudhi::Simplex_tree_options_full_featured` or `Gudhi::Simplex_tree_options_minimal`. + * If you want to provide your own, it is recommended that you derive from it and override some parts instead of + * writing a class from scratch. */ struct SimplexTreeOptions { - /// Forced for now. + /** @brief Forced for now. */ typedef IndexingTag Indexing_tag; - /// Must be a signed integer type. It admits a total order <. + /** @brief Must be a signed integer type. It admits a total order <. */ typedef VertexHandle Vertex_handle; - /// Must be comparable with operator<. + /** @brief Must be comparable with operator<. */ typedef FiltrationValue Filtration_value; - /// Must be an integer type. + /** @brief Must be an integer type. */ typedef SimplexKey Simplex_key; - /// If true, each simplex has extra storage for one `Simplex_key`. Necessary for `Persistent_cohomology`. + /** @brief If true, each simplex has extra storage for one `Simplex_key`. Necessary for `Persistent_cohomology`. */ static const bool store_key; - /// If true, each simplex has extra storage for one `Filtration_value`, and this value is propagated by operations like `Gudhi::Simplex_tree::expansion`. Without it, `Persistent_cohomology` degenerates to computing usual (non-persistent) cohomology. + /** @brief If true, each simplex has extra storage for one `Filtration_value`, and this value is propagated by + * operations like `Gudhi::Simplex_tree::expansion`. Without it, `Persistent_cohomology` degenerates to computing + * usual (non-persistent) cohomology. + */ static const bool store_filtration; - /// If true, the list of vertices present in the complex must always be 0, ..., num_vertices-1, without any hole. + + /** @brief If true, the list of vertices present in the complex must always be 0, ..., num_vertices-1, without any hole. */ static constexpr bool contiguous_vertices; - /// If true, the lists of `Node` with same label are stored to enhance cofaces and stars access. + /** @brief If true, the lists of `Node` with same label are stored to enhance cofaces and stars access. */ static const bool link_nodes_by_label; - /// If true, Simplex_handle will not be invalidated after insertions or removals. + /** @brief If true, Simplex_handle will not be invalidated after insertions or removals. */ static const bool stable_simplex_handles; }; diff --git a/src/Simplex_tree/example/mini_simplex_tree.cpp b/src/Simplex_tree/example/mini_simplex_tree.cpp index 4043bffd2c..c834f3c701 100644 --- a/src/Simplex_tree/example/mini_simplex_tree.cpp +++ b/src/Simplex_tree/example/mini_simplex_tree.cpp @@ -12,11 +12,10 @@ #include #include -struct MyOptions : Gudhi::Simplex_tree_options_full_featured { - // Not doing persistence, so we don't need those - static const bool store_key = false; - static const bool store_filtration = false; - // I have few vertices +// With Gudhi::Simplex_tree_options_minimal filtration values are not stored and persistence cannot be computed. +// I can still improve the memory footprint +struct MyOptions : Gudhi::Simplex_tree_options_minimal { + // As I have few vertices typedef short Vertex_handle; }; diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index ae4545876c..0e14bd524e 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -16,11 +16,11 @@ #ifndef SIMPLEX_TREE_H_ #define SIMPLEX_TREE_H_ +#include #include #include #include #include -#include #include // for Gudhi::simplex_tree::de/serialize_trivial #include @@ -53,7 +53,6 @@ #include // Inf #include #include // for std::max -#include // for std::uint32_t #include // for std::distance #include // for std::conditional #include @@ -79,8 +78,6 @@ namespace Gudhi { */ enum class Extended_simplex_type {UP, DOWN, EXTRA}; -struct Simplex_tree_options_full_featured; - /** * \class Simplex_tree Simplex_tree.h gudhi/Simplex_tree.h * \brief Simplex Tree data structure for representing simplicial complexes. @@ -94,7 +91,7 @@ struct Simplex_tree_options_full_featured; * */ -template +template class Simplex_tree { public: typedef SimplexTreeOptions Options; @@ -2597,56 +2594,6 @@ std::istream& operator>>(std::istream & is, Simplex_tree & st) { return is; } -/** Model of SimplexTreeOptions. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 4 billions of simplices). */ -struct Simplex_tree_options_full_featured { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::uint32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; - static const bool stable_simplex_handles = false; -}; - -/** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe - * `contiguous_vertices` option. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 4 billions of simplices). */ -struct Simplex_tree_options_fast_persistence { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef float Filtration_value; - typedef std::uint32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = true; - static const bool link_nodes_by_label = false; - static const bool stable_simplex_handles = false; -}; - -/** Model of SimplexTreeOptions, faster cofaces than `Simplex_tree_options_full_featured`, note the - * `link_nodes_by_label` option. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 4 billions of simplices). */ -struct Simplex_tree_options_fast_cofaces { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::uint32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = true; - static const bool stable_simplex_handles = false; -}; - /** @}*/ // end addtogroup simplex_tree } // namespace Gudhi diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/simplex_tree_options.h b/src/Simplex_tree/include/gudhi/Simplex_tree/simplex_tree_options.h new file mode 100644 index 0000000000..a828b8f810 --- /dev/null +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/simplex_tree_options.h @@ -0,0 +1,96 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Vincent Rouvreau + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef SIMPLEX_TREE_SIMPLEX_TREE_OPTIONS_H_ +#define SIMPLEX_TREE_SIMPLEX_TREE_OPTIONS_H_ + +#include + +#include + +namespace Gudhi { + +/** \addtogroup simplex_tree + * Pre-defined options for the Simplex_tree. + * @{ + */ + +/** Model of SimplexTreeOptions. + * + * Default options version of the Simplex_tree. + * + * Maximum number of simplices to compute persistence is std::numeric_limits::max() + * (about 4 billions of simplices). */ +struct Simplex_tree_options_default { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::uint32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; + static const bool stable_simplex_handles = false; +}; + +/** Model of SimplexTreeOptions. + * + * Maximum number of simplices to compute persistence is std::numeric_limits::max() + * (about 4 billions of simplices). */ +struct Simplex_tree_options_full_featured { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::uint32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = true; + static const bool stable_simplex_handles = true; +}; + +/** Model of SimplexTreeOptions. + * + * Minimal version of the Simplex_tree. No filtration values are stored and it is impossible to compute persistence + * with these options. */ +struct Simplex_tree_options_minimal { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::uint32_t Simplex_key; + static const bool store_key = false; + static const bool store_filtration = false; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; + static const bool stable_simplex_handles = false; +}; + +/** @private @brief Model of SimplexTreeOptions, faster than `Simplex_tree_options_default` but note the unsafe + * `contiguous_vertices` option. + * + * Maximum number of simplices to compute persistence is std::numeric_limits::max() + * (about 4 billions of simplices). */ +struct Simplex_tree_options_fast_persistence { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef float Filtration_value; + typedef std::uint32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = true; + static const bool link_nodes_by_label = false; + static const bool stable_simplex_handles = false; +}; + +/** @}*/ // end addtogroup simplex_tree + +} // namespace Gudhi + +#endif // SIMPLEX_TREE_SIMPLEX_TREE_OPTIONS_H_ 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 index 986f811b54..46d3bbc14a 100644 --- 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 @@ -40,7 +40,7 @@ struct Simplex_tree_options_stable_simplex_handles { typedef boost::mpl::list, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; template @@ -202,7 +202,7 @@ std::vector> get_star(Simplex_ BOOST_AUTO_TEST_CASE(simplex_fast_cofaces_rule_of_five) { // Only for fast cofaces version to check the data structure for this feature is up to date - using STree = Simplex_tree; + using STree = Simplex_tree; STree st; st.insert_simplex_and_subfaces({2, 1, 0}, 3.0); diff --git a/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp index ed7d08f391..0bcc031767 100644 --- a/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_edge_expansion_unit_test.cpp @@ -25,7 +25,7 @@ using namespace Gudhi; -struct Simplex_tree_options_stable_simplex_handles { +struct Simplex_tree_options_fast_cofaces { typedef linear_indexing_tag Indexing_tag; typedef int Vertex_handle; typedef double Filtration_value; @@ -34,6 +34,10 @@ struct Simplex_tree_options_stable_simplex_handles { static const bool store_filtration = true; static const bool contiguous_vertices = false; static const bool link_nodes_by_label = true; + static const bool stable_simplex_handles = false; +}; + +struct Simplex_tree_options_stable_simplex_handles_fast_cofaces : Simplex_tree_options_fast_cofaces { static const bool stable_simplex_handles = true; }; @@ -122,15 +126,15 @@ BOOST_AUTO_TEST_CASE(simplex_tree_random_rips_expansion) { std::clog << "simplex_tree_random_rips_expansion\n"; std::clog << "Test tree 1: insertion by filtration values\n"; std::clog << "************************************************************************\n"; + test_insert_as_flag,Simplex_tree_options_stable_simplex_handles_fast_cofaces>(simplex_tree); test_insert_as_flag,Simplex_tree_options_fast_cofaces>(simplex_tree); - test_insert_as_flag,Simplex_tree_options_stable_simplex_handles>(simplex_tree); std::clog << i << " ********************************************************************\n"; std::clog << "simplex_tree_random_rips_expansion\n"; std::clog << "Test tree 2: insertion by lexicographical order\n"; //equivalent to random order std::clog << "************************************************************************\n"; + test_unordered_insert_as_flag,Simplex_tree_options_stable_simplex_handles_fast_cofaces>(simplex_tree); test_unordered_insert_as_flag,Simplex_tree_options_fast_cofaces>(simplex_tree); - test_unordered_insert_as_flag,Simplex_tree_options_stable_simplex_handles>(simplex_tree); } } @@ -148,20 +152,20 @@ BOOST_AUTO_TEST_CASE(simplex_tree_random_rips_expansion_with_max_dim) { std::clog << "simplex_tree_random_rips_expansion\n"; std::clog << "Test tree 3: insertion by filtration values and max dimension = " << maxDim << "\n"; std::clog << "************************************************************************\n"; + test_insert_as_flag,Simplex_tree_options_stable_simplex_handles_fast_cofaces>(simplex_tree, maxDim); test_insert_as_flag,Simplex_tree_options_fast_cofaces>(simplex_tree, maxDim); - test_insert_as_flag,Simplex_tree_options_stable_simplex_handles>(simplex_tree, maxDim); std::clog << i << " ********************************************************************\n"; std::clog << "simplex_tree_random_rips_expansion\n"; std::clog << "Test tree 4: insertion by lexicographical order and max dimension = " << maxDim << "\n"; //equivalent to random order std::clog << "************************************************************************\n"; + test_unordered_insert_as_flag,Simplex_tree_options_stable_simplex_handles_fast_cofaces>(simplex_tree, maxDim); test_unordered_insert_as_flag,Simplex_tree_options_fast_cofaces>(simplex_tree, maxDim); - test_unordered_insert_as_flag,Simplex_tree_options_stable_simplex_handles>(simplex_tree, maxDim); } } BOOST_AUTO_TEST_CASE(flag_expansion) { - using typeST = Simplex_tree; + using typeST = Simplex_tree; { std::cout << "************************************************************************" << std::endl; std::cout << "Test flag expansion 1" << std::endl; diff --git a/src/Simplex_tree/test/simplex_tree_extended_filtration_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_extended_filtration_unit_test.cpp index 4fd91131aa..39ec3e5ee3 100644 --- a/src/Simplex_tree/test/simplex_tree_extended_filtration_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_extended_filtration_unit_test.cpp @@ -19,7 +19,7 @@ #include #include -struct Low_options : Gudhi::Simplex_tree_options_full_featured { +struct Low_options : Gudhi::Simplex_tree_options_default { typedef float Filtration_value; typedef std::uint8_t Vertex_handle; }; diff --git a/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp index ed0f38d632..e523cce1ec 100644 --- a/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_graph_expansion_unit_test.cpp @@ -35,7 +35,7 @@ struct Simplex_tree_options_stable_simplex_handles { typedef boost::mpl::list, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_expansion_all_is_blocked, typeST, list_of_tested_variants) { diff --git a/src/Simplex_tree/test/simplex_tree_iostream_operator_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_iostream_operator_unit_test.cpp index 8298bee7fe..edeb40ca50 100644 --- a/src/Simplex_tree/test/simplex_tree_iostream_operator_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_iostream_operator_unit_test.cpp @@ -21,7 +21,7 @@ using namespace Gudhi; -struct MyOptions : Simplex_tree_options_full_featured { +struct MyOptions : Simplex_tree_options_minimal { // Not doing persistence, so we don't need those static const bool store_key = false; static const bool store_filtration = false; @@ -29,14 +29,14 @@ struct MyOptions : Simplex_tree_options_full_featured { typedef short Vertex_handle; }; -struct MyStableOptions : Simplex_tree_options_full_featured { +struct MyStableOptions : Simplex_tree_options_default { //disabled by default. static const bool stable_simplex_handles = true; }; typedef boost::mpl::list, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; BOOST_AUTO_TEST_CASE_TEMPLATE(iostream_operator, Stree_type, list_of_tested_variants) { diff --git a/src/Simplex_tree/test/simplex_tree_make_filtration_non_decreasing_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_make_filtration_non_decreasing_unit_test.cpp index 05b7052612..34adee3b98 100644 --- a/src/Simplex_tree/test/simplex_tree_make_filtration_non_decreasing_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_make_filtration_non_decreasing_unit_test.cpp @@ -37,7 +37,7 @@ struct Simplex_tree_options_stable_simplex_handles { typedef boost::mpl::list, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; BOOST_AUTO_TEST_CASE_TEMPLATE(make_filtration_non_decreasing, typeST, list_of_tested_variants) { diff --git a/src/Simplex_tree/test/simplex_tree_remove_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_remove_unit_test.cpp index ad3cd8b01a..f2527050bd 100644 --- a/src/Simplex_tree/test/simplex_tree_remove_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_remove_unit_test.cpp @@ -20,7 +20,7 @@ using namespace Gudhi; -struct MyOptions : Simplex_tree_options_full_featured { +struct MyOptions : Simplex_tree_options_minimal { // Not doing persistence, so we don't need those static const bool store_key = false; static const bool store_filtration = false; diff --git a/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp index 451418dfcc..a8450cfa72 100644 --- a/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_serialization_unit_test.cpp @@ -40,7 +40,7 @@ struct Low_options : Gudhi::Simplex_tree_options_full_featured { typedef std::uint8_t Simplex_key; }; -struct Stable_options : Gudhi::Simplex_tree_options_full_featured { +struct Stable_options : Gudhi::Simplex_tree_options_default { //disabled by default. static const bool stable_simplex_handles = true; }; @@ -48,7 +48,7 @@ struct Stable_options : Gudhi::Simplex_tree_options_full_featured { typedef boost::mpl::list, Simplex_tree, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; template diff --git a/src/Simplex_tree/test/simplex_tree_unit_test.cpp b/src/Simplex_tree/test/simplex_tree_unit_test.cpp index fa1a444151..5b133ca225 100644 --- a/src/Simplex_tree/test/simplex_tree_unit_test.cpp +++ b/src/Simplex_tree/test/simplex_tree_unit_test.cpp @@ -45,7 +45,7 @@ struct Simplex_tree_options_stable_simplex_handles { typedef boost::mpl::list, Simplex_tree, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants; template @@ -1157,7 +1157,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_boundaries_and_opposite_vertex_iterat } typedef boost::mpl::list, - Simplex_tree, + Simplex_tree, Simplex_tree > list_of_tested_variants_wo_fast_persistence; diff --git a/src/cmake/modules/GUDHI_options.cmake b/src/cmake/modules/GUDHI_options.cmake index 8379e3c63f..56bbc2166d 100644 --- a/src/cmake/modules/GUDHI_options.cmake +++ b/src/cmake/modules/GUDHI_options.cmake @@ -4,6 +4,7 @@ option(WITH_GUDHI_REMOTE_TEST "Activate/deactivate datasets fetching test which option(WITH_GUDHI_PYTHON "Activate/deactivate python module compilation and installation" ON) option(WITH_GUDHI_TEST "Activate/deactivate examples compilation and installation" ON) option(WITH_GUDHI_UTILITIES "Activate/deactivate utilities compilation and installation" ON) +option(WITH_GUDHI_GUDHUI "Activate/deactivate GudhUI" ON) option(WITH_GUDHI_THIRD_PARTY "Activate/deactivate third party libraries cmake detection. When set to OFF, it is useful for doxygen or user_version i.e." ON) if (NOT WITH_GUDHI_THIRD_PARTY) @@ -12,4 +13,5 @@ if (NOT WITH_GUDHI_THIRD_PARTY) set (WITH_GUDHI_PYTHON OFF) set (WITH_GUDHI_TEST OFF) set (WITH_GUDHI_UTILITIES OFF) + set (WITH_GUDHI_GUDHUI OFF) endif() diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 721223cfd9..df7a0433d8 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -325,6 +325,8 @@ if(PYTHONINTERP_FOUND) file(COPY "gudhi/weighted_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/dtm_rips_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/cover_complex.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/_kepler_mapper.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") + file(COPY "gudhi/_kepler_mapper.py.COPYRIGHT" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi") file(COPY "gudhi/hera/__init__.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/hera") file(COPY "gudhi/datasets" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi" FILES_MATCHING PATTERN "*.py") file(COPY "gudhi/sklearn" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/") diff --git a/src/python/doc/nerve_gic_complex_sum.inc b/src/python/doc/nerve_gic_complex_sum.inc index db8b1c3b80..e3f9a7b409 100644 --- a/src/python/doc/nerve_gic_complex_sum.inc +++ b/src/python/doc/nerve_gic_complex_sum.inc @@ -12,7 +12,7 @@ | | | | | | | | +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------------------------------------------+ - | * :doc:`cover_complex_sklearn_user` | * :doc:`cover_complex_sklearn_isk_ref` | :requires: `Scikit-learn `_ | + | * :doc:`cover_complex_sklearn_user` | * :doc:`cover_complex_sklearn_isk_ref` | :Requires: `Scikit-learn `_ | +----------------------------------------------------------------+------------------------------------------------------------------------+-----------------------------------------------------------------+ | * :doc:`nerve_gic_complex_user` | * :doc:`nerve_gic_complex_ref` | | | | | | diff --git a/src/Nerve_GIC/utilities/km.py b/src/python/gudhi/_kepler_mapper.py similarity index 100% rename from src/Nerve_GIC/utilities/km.py rename to src/python/gudhi/_kepler_mapper.py diff --git a/src/Nerve_GIC/utilities/km.py.COPYRIGHT b/src/python/gudhi/_kepler_mapper.py.COPYRIGHT similarity index 94% rename from src/Nerve_GIC/utilities/km.py.COPYRIGHT rename to src/python/gudhi/_kepler_mapper.py.COPYRIGHT index 5358d28711..b60c67b5c1 100644 --- a/src/Nerve_GIC/utilities/km.py.COPYRIGHT +++ b/src/python/gudhi/_kepler_mapper.py.COPYRIGHT @@ -1,4 +1,4 @@ -km.py is a fork of https://github.com/MLWave/kepler-mapper. +_kepler_mapper.py is a fork of https://github.com/MLWave/kepler-mapper. Only the visualization part has been kept (Mapper part has been removed). This file has the following Copyright : diff --git a/src/python/gudhi/alpha_complex.pyx b/src/python/gudhi/alpha_complex.pyx index 85d9c02181..254fba7393 100644 --- a/src/python/gudhi/alpha_complex.pyx +++ b/src/python/gudhi/alpha_complex.pyx @@ -30,7 +30,7 @@ cdef extern from "Alpha_complex_interface.h" namespace "Gudhi": cdef cppclass Alpha_complex_interface "Gudhi::alpha_complex::Alpha_complex_interface": Alpha_complex_interface(vector[vector[double]] points, vector[double] weights, bool fast_version, bool exact_version) nogil except + vector[double] get_point(int vertex) nogil except + - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, bool default_filtration_value) nogil except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square, bool default_filtration_value) nogil except + @staticmethod void set_float_relative_precision(double precision) nogil @staticmethod @@ -134,7 +134,7 @@ cdef class AlphaComplex: cdef intptr_t stree_int_ptr=stree.thisptr cdef bool compute_filtration = default_filtration_value == True with nogil: - self.this_ptr.create_simplex_tree(stree_int_ptr, + self.this_ptr.create_simplex_tree(stree_int_ptr, mas, compute_filtration) return stree diff --git a/src/python/gudhi/cover_complex.py b/src/python/gudhi/cover_complex.py index b8c71b5bba..9288b26dca 100644 --- a/src/python/gudhi/cover_complex.py +++ b/src/python/gudhi/cover_complex.py @@ -17,8 +17,50 @@ from sklearn.metrics import pairwise_distances from scipy.spatial.distance import directed_hausdorff +from sklearn import __version__ as sklearn_version +from sklearn.utils.fixes import parse_version +agglomerative_clustering_metric = parse_version(sklearn_version) >= parse_version('1.2.0') + from . import SimplexTree, CoverComplex +def _save_to_html(dat, lens, color, param, points, edges, html_output_filename): + + from ._kepler_mapper import KeplerMapper + network = {} + mapper = KeplerMapper(verbose=0) + data = np.zeros((3,3)) + projected_data = mapper.fit_transform( data, projection="sum", scaler=None ) + + from collections import defaultdict + nodes = defaultdict(list) + links = defaultdict(list) + custom = defaultdict(list) + + for point in points: + nodes[ str(int(point[0])) ] = [ int(point[0]), point[1], int(point[2]) ] + links[ str(int(point[0])) ] = [] + custom[ int(point[0]) ] = point[1] + + for edge in edges: + links[ str(edge[0]) ].append( str(edge[1]) ) + links[ str(edge[1]) ].append( str(edge[0]) ) + + custom_val = custom.values() + m = min(custom_val) + M = max(custom_val) + + network["nodes"] = nodes + network["links"] = links + network["meta"] = lens + + + mapper.visualize(network, color_function=color, path_html=html_output_filename, title=dat, + graph_link_distance=30, graph_gravity=0.1, graph_charge=-120, custom_tooltips=custom, width_html=0, + height_html=0, show_tooltips=True, show_title=True, show_meta=True, res=param[0], gain=param[1], minimum=m, maximum=M) + message = repr(html_output_filename) + " is generated. You can now use your favorite web browser to visualize it." + print(message) + + class CoverComplexPy(BaseEstimator): """ This is a mother class for MapperComplex, GraphInducedComplex and NerveComplex. @@ -138,7 +180,7 @@ def save_to_txt(self, file_name="cover_complex", data_name="data", cover_name="c f.write(data_name + "\n") f.write(cover_name + "\n") f.write(color_name + "\n") - f.write("0 0\n") + f.write(str(self.resolutions[0]) + " " + str(self.gains[0]) + "\n") f.write(str(st.num_vertices()) + " " + str(len(list(st.get_skeleton(1)))-st.num_vertices()) + "\n") name2id = {} idv = 0 @@ -150,6 +192,39 @@ def save_to_txt(self, file_name="cover_complex", data_name="data", cover_name="c if len(s) == 2: f.write(str(name2id[s[0]]) + " " + str(name2id[s[1]]) + "\n") + def save_to_html(self, file_name="cover_complex", data_name="data", cover_name="cover", color_name="color"): + """ + Write the cover complex to an HTML file called "{file_name}.html", that can be visualized in a browser. This function is based on a fork of https://github.com/MLWave/kepler-mapper + + Parameters + ---------- + file_name : string + name for the output .html file, default "cover_complex" + data_name : string + name to use for the data on which the cover complex was computed, default "data". + cover_name : string + name to use for the cover used to compute the cover complex, default "cover". + color_name : string + name to use for the color used to color the cover complex nodes, default "color". + """ + + st = self.simplex_tree_ + + if not file_name.lower().endswith(".html"): + file_name += ".html" + + points, edges, name2id, idv = [], [], {}, 0 + for s,_ in st.get_skeleton(0): + points.append([ idv, self.node_info_[s[0]]["colors"][0], self.node_info_[s[0]]["size"] ]) + name2id[s[0]] = idv + idv += 1 + for s,_ in st.get_skeleton(1): + if len(s) == 2: + edges.append([ name2id[s[0]] , name2id[s[1]] ]) + + _save_to_html(data_name, cover_name, color_name, [self.resolutions[0], self.gains[0]], points, edges, file_name) + + class _constant_clustering(): def fit_predict(X): return np.zeros([len(X)], dtype=np.int32) @@ -324,10 +399,10 @@ def fit(self, X, y=None, filters=None, colors=None): if self.resolutions is None or self.clustering is None: delta, resolutions = self.get_optimal_parameters_for_agglomerative_clustering(X=X, beta=self.beta, C=self.C, N=self.N) if self.clustering is None: - if self.input_type == "point cloud": - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="euclidean") - else: - self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, affinity="precomputed") + self.clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=delta, **{ + "metric" if agglomerative_clustering_metric else "affinity": + "euclidean" if self.input_type == "point cloud" else "precomputed" + }) if self.resolutions is None: self.resolutions = np.multiply(resolutions, 1./self.gains) self.resolutions = np.array([int( (self.filter_bnds[ir,1]-self.filter_bnds[ir,0])/r) for ir, r in enumerate(self.resolutions)]) diff --git a/src/python/gudhi/dtm_rips_complex.py b/src/python/gudhi/dtm_rips_complex.py index 63c9b1387e..3e5ae46c13 100644 --- a/src/python/gudhi/dtm_rips_complex.py +++ b/src/python/gudhi/dtm_rips_complex.py @@ -11,41 +11,41 @@ from gudhi.weighted_rips_complex import WeightedRipsComplex from gudhi.point_cloud.dtm import DistanceToMeasure from scipy.spatial.distance import cdist +import numpy as np + class DTMRipsComplex(WeightedRipsComplex): """ - Class to generate a DTM Rips complex from a distance matrix or a point set, + Class to generate a DTM Rips complex from a distance matrix or a point set, in the way described in :cite:`dtmfiltrations`. - Remark that all the filtration values are doubled compared to the definition in the paper + Remark that all the filtration values are doubled compared to the definition in the paper for the consistency with RipsComplex. + :Requires: `SciPy `_ """ - def __init__(self, - points=None, - distance_matrix=None, - k=1, - q=2, - max_filtration=float('inf')): + + def __init__(self, points=None, distance_matrix=None, k=1, q=2, max_filtration=float("inf")): """ Args: points (numpy.ndarray): array of points. distance_matrix (numpy.ndarray): full distance matrix. - k (int): number of neighbors for the computation of DTM. Defaults to 1, which is equivalent to the usual Rips complex. + k (int): number of neighbors for the computation of DTM. Defaults to 1, which is equivalent to the usual + Rips complex. q (float): order used to compute the distance to measure. Defaults to 2. - max_filtration (float): specifies the maximal filtration value to be considered. + max_filtration (float): specifies the maximal filtration value to be considered. """ if distance_matrix is None: - if points is None: + if points is not None: + distance_matrix = cdist(points, points) + else: # Empty Rips construction - points=[] - distance_matrix = cdist(points,points) + distance_matrix = np.ndarray((0,0)) self.distance_matrix = distance_matrix - # TODO: address the error when k is too large + # TODO: address the error when k is too large if k <= 1: self.weights = [0] * len(distance_matrix) else: - dtm = DistanceToMeasure(k, q=q, metric="precomputed") + dtm = DistanceToMeasure(k, q=q, metric="precomputed") self.weights = dtm.fit_transform(distance_matrix) self.max_filtration = max_filtration - diff --git a/src/python/gudhi/euclidean_strong_witness_complex.pyx b/src/python/gudhi/euclidean_strong_witness_complex.pyx index 080f8c2973..79b24760b5 100644 --- a/src/python/gudhi/euclidean_strong_witness_complex.pyx +++ b/src/python/gudhi/euclidean_strong_witness_complex.pyx @@ -22,8 +22,8 @@ __license__ = "GPL v3" cdef extern from "Euclidean_strong_witness_complex_interface.h" namespace "Gudhi": cdef cppclass Euclidean_strong_witness_complex_interface "Gudhi::witness_complex::Euclidean_strong_witness_complex_interface": Euclidean_strong_witness_complex_interface(vector[vector[double]] landmarks, vector[vector[double]] witnesses) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) except + - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square) except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square, unsigned limit_dimension) except + vector[double] get_point(unsigned vertex) @@ -71,10 +71,10 @@ cdef class EuclideanStrongWitnessComplex: stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr if limit_dimension != -1: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) return stree diff --git a/src/python/gudhi/euclidean_witness_complex.pyx b/src/python/gudhi/euclidean_witness_complex.pyx index 8464d0c50f..d56f463845 100644 --- a/src/python/gudhi/euclidean_witness_complex.pyx +++ b/src/python/gudhi/euclidean_witness_complex.pyx @@ -22,8 +22,8 @@ __license__ = "GPL v3" cdef extern from "Euclidean_witness_complex_interface.h" namespace "Gudhi": cdef cppclass Euclidean_witness_complex_interface "Gudhi::witness_complex::Euclidean_witness_complex_interface": Euclidean_witness_complex_interface(vector[vector[double]] landmarks, vector[vector[double]] witnesses) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) except + - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square) except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square, unsigned limit_dimension) except + vector[double] get_point(unsigned vertex) @@ -71,10 +71,10 @@ cdef class EuclideanWitnessComplex: stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr if limit_dimension != -1: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) return stree diff --git a/src/python/gudhi/nerve_gic.pyx b/src/python/gudhi/nerve_gic.pyx index 774b149704..a02a10aa27 100644 --- a/src/python/gudhi/nerve_gic.pyx +++ b/src/python/gudhi/nerve_gic.pyx @@ -35,7 +35,7 @@ cdef extern from "Nerve_gic_interface.h" namespace "Gudhi": double compute_p_value() vector[pair[double, double]] compute_PD() void find_simplices() - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree) + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree) bool read_point_cloud(string off_file_name) double set_automatic_resolution() void set_color_from_coordinate(int k) @@ -174,7 +174,7 @@ cdef class CoverComplex: stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr self.thisptr.create_simplex_tree( - stree_int_ptr) + stree_int_ptr) return stree def find_simplices(self): diff --git a/src/python/gudhi/point_cloud/knn.py b/src/python/gudhi/point_cloud/knn.py index 7dc8381731..fd6c100b3d 100644 --- a/src/python/gudhi/point_cloud/knn.py +++ b/src/python/gudhi/point_cloud/knn.py @@ -34,10 +34,14 @@ def __init__(self, k, return_index=True, return_distance=False, metric="euclidea return_distance (bool): if True, return the distance to each neighbor. implementation (str): choice of the library that does the real work. - * 'keops' for a brute-force, CUDA implementation through pykeops. Useful when the dimension becomes large (10+) but the number of points remains low (less than a million). Only "minkowski" and its aliases are supported. + * 'keops' for a brute-force, CUDA implementation through pykeops. Useful when the dimension becomes + large (10+) but the number of points remains low (less than a million). Only "minkowski" and its + aliases are supported. * 'ckdtree' for scipy's cKDTree. Only "minkowski" and its aliases are supported. - * 'sklearn' for scikit-learn's NearestNeighbors. Note that this provides in particular an option algorithm="brute". - * 'hnsw' for hnswlib.Index. It can be very fast but does not provide guarantees. Only supports "euclidean" for now. + * 'sklearn' for scikit-learn's NearestNeighbors. Note that this provides in particular an option + algorithm="brute". + * 'hnsw' for hnswlib.Index. It can be very fast but does not provide guarantees. Only supports + "euclidean" for now. * None will try to select a sensible one (scipy if possible, scikit-learn otherwise). metric (str): see `sklearn.neighbors.NearestNeighbors`. eps (float): relative error when computing nearest neighbors with the cKDTree. @@ -53,6 +57,8 @@ def __init__(self, k, return_index=True, return_distance=False, metric="euclidea Defaults to False. kwargs: additional parameters are forwarded to the backends. """ + if k < 1: + raise ValueError(f"Expected number of neighbors (aka. 'k') > 0. Got {k}") self.k = k self.return_index = return_index self.return_distance = return_distance @@ -90,6 +96,10 @@ def fit(self, X, y=None): Args: X (numpy.array): coordinates for reference points. """ + if self.k > len(X): + raise ValueError( + f"Expected number of neighbors (aka. 'k') <= number of samples, but k={self.k} and number of samples={len(X)}" + ) self.ref_points = X if self.params.get("enable_autodiff", False): import eagerpy as ep @@ -166,9 +176,7 @@ def transform(self, X): assert self.metric == "minkowski" p = self.params["p"] Y = ep.astensor(self.ref_points) - neighbor_pts = Y[ - neighbors, - ] + neighbor_pts = Y[neighbors,] diff = neighbor_pts - X[:, None, :] if isinstance(diff, ep.PyTorchTensor): # https://github.com/jonasrauber/eagerpy/issues/6 @@ -184,7 +192,8 @@ def transform(self, X): k = self.k if metric == "precomputed": - # scikit-learn could handle that, but they insist on calling fit() with an unused square array, which is too unnatural. + # scikit-learn could handle that, but they insist on calling fit() with an unused square array + # which is too unnatural. if self.return_index: n_jobs = self.params.get("n_jobs", 1) # Supposedly numpy can be compiled with OpenMP and handle this, but nobody does that?! @@ -259,7 +268,7 @@ def func(M): self.graph.set_ef(ef) neighbors, distances = self.graph.knn_query(X, k, num_threads=self.params["num_threads"]) with warnings.catch_warnings(): - if not(numpy.all(numpy.isfinite(distances))): + if not (numpy.all(numpy.isfinite(distances))): warnings.warn("Overflow/infinite value encountered while computing 'distances'", RuntimeWarning) # The k nearest neighbors are always sorted. I couldn't find it in the doc, but the code calls searchKnn, # which returns a priority_queue, and then fills the return array backwards with top/pop on the queue. @@ -295,8 +304,9 @@ def func(M): if self.return_distance: distances, neighbors = mat.Kmin_argKmin(k, dim=1) with warnings.catch_warnings(): - if not(torch.isfinite(distances).all()): - warnings.warn("Overflow/infinite value encountered while computing 'distances'", RuntimeWarning) + if not (torch.isfinite(distances).all()): + warnings.warn("Overflow/infinite value encountered while computing 'distances'", + RuntimeWarning) if p != numpy.inf: distances = distances ** (1.0 / p) return neighbors, distances @@ -306,8 +316,9 @@ def func(M): if self.return_distance: distances = mat.Kmin(k, dim=1) with warnings.catch_warnings(): - if not(torch.isfinite(distances).all()): - warnings.warn("Overflow/infinite value encountered while computing 'distances'", RuntimeWarning) + if not (torch.isfinite(distances).all()): + warnings.warn("Overflow/infinite value encountered while computing 'distances'", + RuntimeWarning) if p != numpy.inf: distances = distances ** (1.0 / p) return distances diff --git a/src/python/gudhi/rips_complex.pyx b/src/python/gudhi/rips_complex.pyx index 27c9dd7db2..4abb507ecd 100644 --- a/src/python/gudhi/rips_complex.pyx +++ b/src/python/gudhi/rips_complex.pyx @@ -28,7 +28,7 @@ cdef extern from "Rips_complex_interface.h" namespace "Gudhi": void init_matrix(vector[vector[double]] values, double threshold) nogil void init_points_sparse(vector[vector[double]] values, double threshold, double sparse) nogil void init_matrix_sparse(vector[vector[double]] values, double threshold, double sparse) nogil - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, int dim_max) nogil except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, int dim_max) nogil except + # RipsComplex python interface cdef class RipsComplex: @@ -92,5 +92,5 @@ cdef class RipsComplex: cdef intptr_t stree_int_ptr=stree.thisptr cdef int maxdim = max_dimension with nogil: - self.thisref.create_simplex_tree(stree_int_ptr, maxdim) + self.thisref.create_simplex_tree(stree_int_ptr, maxdim) return stree diff --git a/src/python/gudhi/simplex_tree.pxd b/src/python/gudhi/simplex_tree.pxd index 8e89868bd8..c2a101b4bf 100644 --- a/src/python/gudhi/simplex_tree.pxd +++ b/src/python/gudhi/simplex_tree.pxd @@ -19,34 +19,31 @@ __copyright__ = "Copyright (C) 2016 Inria" __license__ = "MIT" cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": - cdef cppclass Simplex_tree_options_full_featured: + cdef cppclass Simplex_tree_simplex_handle "Gudhi::Simplex_tree_interface::Simplex_handle": pass - cdef cppclass Simplex_tree_simplex_handle "Gudhi::Simplex_tree_interface::Simplex_handle": - pass - - cdef cppclass Simplex_tree_simplices_iterator "Gudhi::Simplex_tree_interface::Complex_simplex_iterator": + cdef cppclass Simplex_tree_simplices_iterator "Gudhi::Simplex_tree_interface::Complex_simplex_iterator": Simplex_tree_simplices_iterator() nogil Simplex_tree_simplex_handle& operator*() nogil Simplex_tree_simplices_iterator operator++() nogil bint operator!=(Simplex_tree_simplices_iterator) nogil - cdef cppclass Simplex_tree_skeleton_iterator "Gudhi::Simplex_tree_interface::Skeleton_simplex_iterator": + cdef cppclass Simplex_tree_skeleton_iterator "Gudhi::Simplex_tree_interface::Skeleton_simplex_iterator": Simplex_tree_skeleton_iterator() nogil Simplex_tree_simplex_handle& operator*() nogil Simplex_tree_skeleton_iterator operator++() nogil bint operator!=(Simplex_tree_skeleton_iterator) nogil - cdef cppclass Simplex_tree_boundary_iterator "Gudhi::Simplex_tree_interface::Boundary_simplex_iterator": + cdef cppclass Simplex_tree_boundary_iterator "Gudhi::Simplex_tree_interface::Boundary_simplex_iterator": Simplex_tree_boundary_iterator() nogil Simplex_tree_simplex_handle& operator*() nogil Simplex_tree_boundary_iterator operator++() nogil bint operator!=(Simplex_tree_boundary_iterator) nogil - cdef cppclass Simplex_tree_interface_full_featured "Gudhi::Simplex_tree_interface": - Simplex_tree_interface_full_featured() nogil - Simplex_tree_interface_full_featured(Simplex_tree_interface_full_featured&) nogil + cdef cppclass Simplex_tree_python_interface "Gudhi::Simplex_tree_interface": + Simplex_tree_python_interface() nogil + Simplex_tree_python_interface(Simplex_tree_python_interface&) nogil double simplex_filtration(vector[int] simplex) nogil void assign_simplex_filtration(vector[int] simplex, double filtration) nogil except + void initialize_filtration() nogil @@ -68,9 +65,9 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": bool prune_above_dimension(int dimension) nogil bool make_filtration_non_decreasing() nogil void compute_extended_filtration() nogil - Simplex_tree_interface_full_featured* collapse_edges(int nb_collapse_iteration) nogil except + + Simplex_tree_python_interface* collapse_edges(int nb_collapse_iteration) nogil except + void reset_filtration(double filtration, int dimension) nogil - bint operator==(Simplex_tree_interface_full_featured) nogil + bint operator==(Simplex_tree_python_interface) nogil # Iterators over Simplex tree pair[vector[int], double] get_simplex_and_filtration(Simplex_tree_simplex_handle f_simplex) nogil Simplex_tree_simplices_iterator get_simplices_iterator_begin() nogil @@ -88,8 +85,8 @@ cdef extern from "Simplex_tree_interface.h" namespace "Gudhi": size_t get_serialization_size() nogil cdef extern from "Persistent_cohomology_interface.h" namespace "Gudhi": - cdef cppclass Simplex_tree_persistence_interface "Gudhi::Persistent_cohomology_interface>": - Simplex_tree_persistence_interface(Simplex_tree_interface_full_featured * st, bool persistence_dim_max) nogil + cdef cppclass Simplex_tree_persistence_interface "Gudhi::Persistent_cohomology_interface": + Simplex_tree_persistence_interface(Simplex_tree_python_interface * st, bool persistence_dim_max) nogil void compute_persistence(int homology_coeff_field, double min_persistence) nogil except + vector[pair[int, pair[double, double]]] get_persistence() nogil vector[int] betti_numbers() nogil diff --git a/src/python/gudhi/simplex_tree.pyx b/src/python/gudhi/simplex_tree.pyx index f9a8413616..039e364c95 100644 --- a/src/python/gudhi/simplex_tree.pyx +++ b/src/python/gudhi/simplex_tree.pyx @@ -41,13 +41,13 @@ cdef class SimplexTree: This class is a filtered, with keys, and non contiguous vertices version of the simplex tree. """ - # unfortunately 'cdef public Simplex_tree_interface_full_featured* thisptr' is not possible + # unfortunately 'cdef public Simplex_tree_python_interface* thisptr' is not possible # Use intptr_t instead to cast the pointer cdef public intptr_t thisptr # Get the pointer casted as it should be - cdef Simplex_tree_interface_full_featured* get_ptr(self) nogil: - return (self.thisptr) + cdef Simplex_tree_python_interface* get_ptr(self) nogil: + return (self.thisptr) cdef Simplex_tree_persistence_interface * pcohptr @@ -74,10 +74,10 @@ cdef class SimplexTree: else: raise TypeError("`other` argument requires to be of type `SimplexTree`, or `None`.") else: - self.thisptr = (new Simplex_tree_interface_full_featured()) + self.thisptr = (new Simplex_tree_python_interface()) def __dealloc__(self): - cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() + cdef Simplex_tree_python_interface* ptr = self.get_ptr() if ptr != NULL: del ptr if self.pcohptr != NULL: @@ -802,7 +802,7 @@ cdef class SimplexTree: :type nb_iterations: int """ # Backup old pointer - cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() + cdef Simplex_tree_python_interface* ptr = self.get_ptr() cdef int nb_iter = nb_iterations with nogil: # New pointer is a new collapsed simplex tree @@ -841,13 +841,13 @@ cdef class SimplexTree: cdef size_t buffer_size = state.shape[0] cdef char* buffer_start = &buffer[0] # Delete pointer, just in case, as deserialization requires an empty SimplexTree - cdef Simplex_tree_interface_full_featured* ptr = self.get_ptr() + cdef Simplex_tree_python_interface* ptr = self.get_ptr() del ptr - self.thisptr = (new Simplex_tree_interface_full_featured()) + self.thisptr = (new Simplex_tree_python_interface()) with nogil: # New pointer is a deserialized simplex tree self.get_ptr().deserialize(buffer_start, buffer_size) cdef intptr_t _get_copy_intptr(SimplexTree stree) nogil: - return (new Simplex_tree_interface_full_featured(dereference(stree.get_ptr()))) + return (new Simplex_tree_python_interface(dereference(stree.get_ptr()))) diff --git a/src/python/gudhi/strong_witness_complex.pyx b/src/python/gudhi/strong_witness_complex.pyx index 93dded972f..877dd0ac11 100644 --- a/src/python/gudhi/strong_witness_complex.pyx +++ b/src/python/gudhi/strong_witness_complex.pyx @@ -22,8 +22,8 @@ __license__ = "MIT" cdef extern from "Strong_witness_complex_interface.h" namespace "Gudhi": cdef cppclass Strong_witness_complex_interface "Gudhi::witness_complex::Strong_witness_complex_interface": Strong_witness_complex_interface(vector[vector[pair[size_t, double]]] nearest_landmark_table) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) except + - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square) except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square, unsigned limit_dimension) except + # StrongWitnessComplex python interface @@ -69,9 +69,9 @@ cdef class StrongWitnessComplex: stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr if limit_dimension != -1: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) return stree diff --git a/src/python/gudhi/tangential_complex.pyx b/src/python/gudhi/tangential_complex.pyx index 354385819c..ae96159152 100644 --- a/src/python/gudhi/tangential_complex.pyx +++ b/src/python/gudhi/tangential_complex.pyx @@ -33,7 +33,7 @@ cdef extern from "Tangential_complex_interface.h" namespace "Gudhi": unsigned number_of_simplices() unsigned number_of_inconsistent_simplices() unsigned number_of_inconsistent_stars() - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree) + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree) void fix_inconsistencies_using_perturbation(double max_perturb, double time_limit) void set_max_squared_edge_length(double max_squared_edge_length) @@ -143,7 +143,7 @@ cdef class TangentialComplex: """ stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr - self.thisptr.create_simplex_tree(stree_int_ptr) + self.thisptr.create_simplex_tree(stree_int_ptr) return stree def fix_inconsistencies_using_perturbation(self, max_perturb, time_limit=-1.0): diff --git a/src/python/gudhi/witness_complex.pyx b/src/python/gudhi/witness_complex.pyx index 4db981149a..2fb600319a 100644 --- a/src/python/gudhi/witness_complex.pyx +++ b/src/python/gudhi/witness_complex.pyx @@ -22,8 +22,8 @@ __license__ = "MIT" cdef extern from "Witness_complex_interface.h" namespace "Gudhi": cdef cppclass Witness_complex_interface "Gudhi::witness_complex::Witness_complex_interface": Witness_complex_interface(vector[vector[pair[size_t, double]]] nearest_landmark_table) - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square) except + - void create_simplex_tree(Simplex_tree_interface_full_featured* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square) except + + void create_simplex_tree(Simplex_tree_python_interface* simplex_tree, double max_alpha_square, unsigned limit_dimension) except + # WitnessComplex python interface @@ -69,9 +69,9 @@ cdef class WitnessComplex: stree = SimplexTree() cdef intptr_t stree_int_ptr=stree.thisptr if limit_dimension != -1: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square, limit_dimension) else: - self.thisptr.create_simplex_tree(stree_int_ptr, + self.thisptr.create_simplex_tree(stree_int_ptr, max_alpha_square) return stree diff --git a/src/python/include/Alpha_complex_factory.h b/src/python/include/Alpha_complex_factory.h index 41eb72c11b..a8ff8d32ac 100644 --- a/src/python/include/Alpha_complex_factory.h +++ b/src/python/include/Alpha_complex_factory.h @@ -68,7 +68,7 @@ class Abstract_alpha_complex { public: virtual std::vector get_point(int vh) = 0; - virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + virtual bool create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, bool default_filtration_value) = 0; virtual std::size_t num_vertices() const = 0; @@ -101,7 +101,7 @@ class Exact_alpha_complex_dD final : public Abstract_alpha_complex { return Point_cgal_to_cython()(alpha_complex_.get_point(vh)); } - virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + virtual bool create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, bool default_filtration_value) override { return alpha_complex_.create_complex(*simplex_tree, max_alpha_square, exact_version_, default_filtration_value); } @@ -136,7 +136,7 @@ class Inexact_alpha_complex_dD final : public Abstract_alpha_complex { // Can be a Weighted or a Bare point in function of Weighted return Point_cgal_to_cython()(alpha_complex_.get_point(vh)); } - virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + virtual bool create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, bool default_filtration_value) override { return alpha_complex_.create_complex(*simplex_tree, max_alpha_square, false, default_filtration_value); } diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index 469b91ce9f..2d34dbc5de 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -50,7 +50,7 @@ class Alpha_complex_interface { return alpha_ptr_->get_point(vh); } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, bool default_filtration_value) { // Nothing to be done in case of an empty point set if (alpha_ptr_->num_vertices() > 0) diff --git a/src/python/include/Euclidean_strong_witness_complex_interface.h b/src/python/include/Euclidean_strong_witness_complex_interface.h index f94c51ef36..1bc7a75ed0 100644 --- a/src/python/include/Euclidean_strong_witness_complex_interface.h +++ b/src/python/include/Euclidean_strong_witness_complex_interface.h @@ -47,12 +47,12 @@ class Euclidean_strong_witness_complex_interface { delete witness_complex_; } - void create_simplex_tree(Gudhi::Simplex_tree<>* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, std::size_t limit_dimension) { witness_complex_->create_complex(*simplex_tree, max_alpha_square, limit_dimension); } - void create_simplex_tree(Gudhi::Simplex_tree<>* simplex_tree, double max_alpha_square) { + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square) { witness_complex_->create_complex(*simplex_tree, max_alpha_square); } diff --git a/src/python/include/Euclidean_witness_complex_interface.h b/src/python/include/Euclidean_witness_complex_interface.h index 4411ae7957..82950a6c49 100644 --- a/src/python/include/Euclidean_witness_complex_interface.h +++ b/src/python/include/Euclidean_witness_complex_interface.h @@ -47,11 +47,11 @@ class Euclidean_witness_complex_interface { delete witness_complex_; } - void create_simplex_tree(Gudhi::Simplex_tree<>* simplex_tree, double max_alpha_square, std::size_t limit_dimension) { + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, std::size_t limit_dimension) { witness_complex_->create_complex(*simplex_tree, max_alpha_square, limit_dimension); } - void create_simplex_tree(Gudhi::Simplex_tree<>* simplex_tree, double max_alpha_square) { + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square) { witness_complex_->create_complex(*simplex_tree, max_alpha_square); } diff --git a/src/python/include/Nerve_gic_interface.h b/src/python/include/Nerve_gic_interface.h index ab14c31850..7e7c01b59e 100644 --- a/src/python/include/Nerve_gic_interface.h +++ b/src/python/include/Nerve_gic_interface.h @@ -27,7 +27,7 @@ namespace cover_complex { class Nerve_gic_interface : public Cover_complex> { public: - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree) { + void create_simplex_tree(Simplex_tree_interface* simplex_tree) { create_complex(*simplex_tree); } void set_cover_from_Euclidean_Voronoi(int m) { diff --git a/src/python/include/Rips_complex_interface.h b/src/python/include/Rips_complex_interface.h index d98b02262b..2a289d0725 100644 --- a/src/python/include/Rips_complex_interface.h +++ b/src/python/include/Rips_complex_interface.h @@ -31,7 +31,7 @@ namespace rips_complex { class Rips_complex_interface { using Point_d = std::vector; - using Distance_matrix = std::vector::Filtration_value>>; + using Distance_matrix = std::vector>; public: void init_points(const std::vector>& points, double threshold) { @@ -48,7 +48,7 @@ class Rips_complex_interface { sparse_rips_complex_.emplace(matrix, epsilon, -std::numeric_limits::infinity(), threshold); } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, int dim_max) { + void create_simplex_tree(Simplex_tree_interface* simplex_tree, int dim_max) { if (rips_complex_) rips_complex_->create_complex(*simplex_tree, dim_max); else @@ -58,8 +58,8 @@ class Rips_complex_interface { private: // 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::Filtration_value>> rips_complex_; - boost::optional::Filtration_value>> sparse_rips_complex_; + boost::optional> rips_complex_; + boost::optional> sparse_rips_complex_; }; } // namespace rips_complex diff --git a/src/python/include/Simplex_tree_interface.h b/src/python/include/Simplex_tree_interface.h index 41ba19c340..eb7d9bad2e 100644 --- a/src/python/include/Simplex_tree_interface.h +++ b/src/python/include/Simplex_tree_interface.h @@ -25,10 +25,25 @@ namespace Gudhi { -template -class Simplex_tree_interface : public Simplex_tree { +/** Model of SimplexTreeOptions. + * + * Specific to python interfaces of the Simplex_tree */ +struct Simplex_tree_options_for_python { + typedef linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::uint32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = false; + static const bool link_nodes_by_label = false; + static const bool stable_simplex_handles = false; +}; + + +class Simplex_tree_interface : public Simplex_tree { public: - using Base = Simplex_tree; + using Base = Simplex_tree; using Filtration_value = typename Base::Filtration_value; using Vertex_handle = typename Base::Vertex_handle; using Simplex_handle = typename Base::Simplex_handle; @@ -97,24 +112,12 @@ class Simplex_tree_interface : public Simplex_tree { return (result.second); } - // Do not interface this function, only used in interface for complex creation - bool insert_simplex_and_subfaces(const Simplex& simplex, Filtration_value filtration = 0) { - Insertion_result result = Base::insert_simplex_and_subfaces(simplex, filtration); - return (result.second); - } - // Do not interface this function, only used in strong witness interface for complex creation bool insert_simplex(const std::vector& simplex, Filtration_value filtration = 0) { Insertion_result result = Base::insert_simplex(simplex, filtration); return (result.second); } - // Do not interface this function, only used in strong witness interface for complex creation - bool insert_simplex_and_subfaces(const std::vector& simplex, Filtration_value filtration = 0) { - Insertion_result result = Base::insert_simplex_and_subfaces(simplex, filtration); - return (result.second); - } - Filtration_value simplex_filtration(const Simplex& simplex) { return Base::filtration(Base::find(simplex)); } diff --git a/src/python/include/Strong_witness_complex_interface.h b/src/python/include/Strong_witness_complex_interface.h index e9ab0c7bd6..14f58d9e3c 100644 --- a/src/python/include/Strong_witness_complex_interface.h +++ b/src/python/include/Strong_witness_complex_interface.h @@ -38,12 +38,12 @@ class Strong_witness_complex_interface { delete witness_complex_; } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, std::size_t limit_dimension) { witness_complex_->create_complex(*simplex_tree, max_alpha_square, limit_dimension); } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square) { witness_complex_->create_complex(*simplex_tree, max_alpha_square); } diff --git a/src/python/include/Tangential_complex_interface.h b/src/python/include/Tangential_complex_interface.h index b1afce94e9..1d9c2e0e17 100644 --- a/src/python/include/Tangential_complex_interface.h +++ b/src/python/include/Tangential_complex_interface.h @@ -88,8 +88,8 @@ class Tangential_complex_interface { num_inconsistencies_ = tangential_complex_->number_of_inconsistent_simplices(); } - void create_simplex_tree(Simplex_tree<>* simplex_tree) { - tangential_complex_->create_complex>(*simplex_tree); + void create_simplex_tree(Simplex_tree_interface* simplex_tree) { + tangential_complex_->create_complex(*simplex_tree); } void set_max_squared_edge_length(double max_squared_edge_length) { diff --git a/src/python/include/Witness_complex_interface.h b/src/python/include/Witness_complex_interface.h index 76947e537a..9981e6a096 100644 --- a/src/python/include/Witness_complex_interface.h +++ b/src/python/include/Witness_complex_interface.h @@ -38,12 +38,12 @@ class Witness_complex_interface { delete witness_complex_; } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square, std::size_t limit_dimension) { witness_complex_->create_complex(*simplex_tree, max_alpha_square, limit_dimension); } - void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, + void create_simplex_tree(Simplex_tree_interface* simplex_tree, double max_alpha_square) { witness_complex_->create_complex(*simplex_tree, max_alpha_square); } diff --git a/src/python/test/test_dtm_rips_complex.py b/src/python/test/test_dtm_rips_complex.py index e1c0ee4481..62fcab98f6 100644 --- a/src/python/test/test_dtm_rips_complex.py +++ b/src/python/test/test_dtm_rips_complex.py @@ -14,14 +14,18 @@ from math import sqrt import pytest + def test_dtm_rips_complex(): pts = np.array([[2.0, 2.0], [0.0, 1.0], [3.0, 4.0]]) dtm_rips = DTMRipsComplex(points=pts, k=2) st = dtm_rips.create_simplex_tree(max_dimension=2) st.persistence() persistence_intervals0 = st.persistence_intervals_in_dimension(0) - assert persistence_intervals0 == pytest.approx(np.array([[3.16227766, 5.39834564],[3.16227766, 5.39834564], [3.16227766, float("inf")]])) - + assert persistence_intervals0 == pytest.approx( + np.array([[3.16227766, 5.39834564], [3.16227766, 5.39834564], [3.16227766, float("inf")]]) + ) + + def test_compatibility_with_rips(): distance_matrix = np.array([[0, 1, 1, sqrt(2)], [1, 0, sqrt(2), 1], [1, sqrt(2), 0, 1], [sqrt(2), 1, 1, 0]]) dtm_rips = DTMRipsComplex(distance_matrix=distance_matrix, max_filtration=42) @@ -30,3 +34,9 @@ def test_compatibility_with_rips(): st_from_rips = rips_complex.create_simplex_tree(max_dimension=1) assert list(st.get_filtration()) == list(st_from_rips.get_filtration()) + +def test_empty_dtm_rips_complex(): + dtm_rips = DTMRipsComplex() + st = dtm_rips.create_simplex_tree(max_dimension=1) + assert st.num_simplices() == 0 + assert st.dimension() == -1 diff --git a/src/python/test/test_knn.py b/src/python/test/test_knn.py index a87ec2125e..792b9fa394 100644 --- a/src/python/test/test_knn.py +++ b/src/python/test/test_knn.py @@ -33,7 +33,14 @@ def test_knn_explicit(): ) assert r == pytest.approx(np.array([[0.0, 1], [1, 1], [1, 2]])) r = ( - KNearestNeighbors(2, metric="chebyshev", return_distance=True, return_index=False, implementation="keops", enable_autodiff=True) + KNearestNeighbors( + 2, + metric="chebyshev", + return_distance=True, + return_index=False, + implementation="keops", + enable_autodiff=True, + ) .fit(base) .transform(query) ) @@ -59,7 +66,9 @@ def test_knn_explicit(): assert np.array_equal(r[0], [[0, 1], [1, 0], [2, 0]]) assert np.array_equal(r[1], [[0, 3], [0, 1], [0, 1]]) # Second time in parallel - knn = KNearestNeighbors(2, metric="precomputed", return_index=True, return_distance=False, n_jobs=2, sort_results=True) + knn = KNearestNeighbors( + 2, metric="precomputed", return_index=True, return_distance=False, n_jobs=2, sort_results=True + ) r = knn.fit_transform(dist) assert np.array_equal(r, [[0, 1], [1, 0], [2, 0]]) knn = KNearestNeighbors(2, metric="precomputed", return_index=True, return_distance=True, n_jobs=2) @@ -128,3 +137,17 @@ def test_knn_nop(): assert None is KNearestNeighbors( k=1, return_index=False, return_distance=False, metric="precomputed" ).fit_transform(p) + + +def test_knn_k_limits(): + nb_sample = 1000 + for impl in ["sklearn", "ckdtree", "hnsw", "keops"]: + data = np.random.rand(nb_sample, 4) + with pytest.raises(ValueError): + KNearestNeighbors( + k=0, return_index=False, return_distance=True, sort_results=False, implementation=impl + ).fit_transform(data) + with pytest.raises(ValueError): + KNearestNeighbors( + k=nb_sample + 1, return_index=False, return_distance=True, sort_results=False, implementation=impl + ).fit_transform(data)