diff options
Diffstat (limited to 'src/Nerve_GIC/example')
-rw-r--r-- | src/Nerve_GIC/example/CMakeLists.txt | 27 | ||||
-rw-r--r-- | src/Nerve_GIC/example/GIC.cpp | 65 | ||||
-rw-r--r-- | src/Nerve_GIC/example/GIC.txt | 89 | ||||
-rw-r--r-- | src/Nerve_GIC/example/GICvoronoi.cpp | 60 | ||||
-rw-r--r-- | src/Nerve_GIC/example/GICvoronoi.txt | 563 | ||||
-rw-r--r-- | src/Nerve_GIC/example/MapperDeltaCoord.cpp | 62 | ||||
-rw-r--r-- | src/Nerve_GIC/example/MapperDeltaCoord.txt | 143 | ||||
-rw-r--r-- | src/Nerve_GIC/example/MapperDeltaFunc.cpp | 62 | ||||
-rw-r--r-- | src/Nerve_GIC/example/MapperDeltaFunc.txt | 10 | ||||
-rw-r--r-- | src/Nerve_GIC/example/Nerve.cpp | 64 | ||||
-rw-r--r-- | src/Nerve_GIC/example/Nerve.txt | 43 | ||||
-rwxr-xr-x | src/Nerve_GIC/example/km.py | 390 | ||||
-rwxr-xr-x | src/Nerve_GIC/example/visu.py | 44 |
13 files changed, 1622 insertions, 0 deletions
diff --git a/src/Nerve_GIC/example/CMakeLists.txt b/src/Nerve_GIC/example/CMakeLists.txt new file mode 100644 index 00000000..b2c501c3 --- /dev/null +++ b/src/Nerve_GIC/example/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.6) +project(Nerve_GIC_examples) + +add_executable ( Nerve Nerve.cpp ) +target_link_libraries(Nerve ${Boost_SYSTEM_LIBRARY}) + +add_executable ( GIC GIC.cpp ) +target_link_libraries(GIC ${Boost_SYSTEM_LIBRARY}) + +add_executable ( MapperDeltaCoord MapperDeltaCoord.cpp ) +target_link_libraries(MapperDeltaCoord ${Boost_SYSTEM_LIBRARY}) + +add_executable ( MapperDeltaFunc MapperDeltaFunc.cpp ) +target_link_libraries(MapperDeltaFunc ${Boost_SYSTEM_LIBRARY}) + +add_executable ( GICvoronoi GICvoronoi.cpp ) +target_link_libraries(MapperDeltaFunc ${Boost_SYSTEM_LIBRARY}) + +file(COPY visu.py km.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) + +if (TBB_FOUND) + target_link_libraries(Nerve ${TBB_LIBRARIES}) + target_link_libraries(GIC ${TBB_LIBRARIES}) + target_link_libraries(MapperDeltaCoord ${TBB_LIBRARIES}) + target_link_libraries(MapperDeltaFunc ${TBB_LIBRARIES}) + target_link_libraries(GICvoronoi ${TBB_LIBRARIES}) +endif()
\ No newline at end of file diff --git a/src/Nerve_GIC/example/GIC.cpp b/src/Nerve_GIC/example/GIC.cpp new file mode 100644 index 00000000..1889cb33 --- /dev/null +++ b/src/Nerve_GIC/example/GIC.cpp @@ -0,0 +1,65 @@ +#include <gudhi/GIC.h> + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off threshold coordinate resolution gain [--v] \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/points/human.off 0.075 2 0.075 0 --v \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 6) && (argc != 7)) usage(argc, (argv[0] - 1)); + + std::string off_file_name(argv[1]); + double threshold = atof(argv[2]); + int coord = atoi(argv[3]); + double resolution = atof(argv[4]); + double gain = atof(argv[5]); + bool verb = 0; if(argc == 7) verb = 1; + + // ---------------------------------------------------------------------------- + // Init of a graph induced complex from an OFF file + // ---------------------------------------------------------------------------- + + Gudhi::graph_induced_complex::Graph_induced_complex GIC; + GIC.set_verbose(verb); + + GIC.read_point_cloud(off_file_name); + + GIC.set_color_from_coordinate(coord); + GIC.set_function_from_coordinate(coord); + + GIC.set_graph_from_rips(threshold); + + GIC.set_resolution_double(resolution); GIC.set_gain(gain); + GIC.set_cover_from_function(1); + + GIC.find_GIC_simplices(); + + GIC.plot_with_KeplerMapper(); + + Simplex_tree stree; GIC.create_complex(stree); + + std::streambuf* streambufffer = std::cout.rdbuf(); + std::ostream output_stream(streambufffer); + + // ---------------------------------------------------------------------------- + // Display information about the graph induced complex + // ---------------------------------------------------------------------------- + + if(verb){ + output_stream << "Graph induced complex is of dimension " << stree.dimension() << + " - " << stree.num_simplices() << " simplices - " << + stree.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on graph induced complex simplices" << std::endl; + for (auto f_simplex : stree.filtration_simplex_range()) { + for (auto vertex : stree.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << std::endl; + } + } + + return 0; +} diff --git a/src/Nerve_GIC/example/GIC.txt b/src/Nerve_GIC/example/GIC.txt new file mode 100644 index 00000000..79f61e92 --- /dev/null +++ b/src/Nerve_GIC/example/GIC.txt @@ -0,0 +1,89 @@ +Graph induced complex is of dimension 1 - 87 simplices - 44 vertices. +Iterator on graph induced complex simplices +0 +1 +2 +2 1 +3 +3 0 +4 +4 2 +5 +5 3 +6 +6 5 +7 +7 4 +8 +8 7 +9 +9 6 +10 +10 8 +11 +11 9 +12 +12 11 +13 +13 10 +14 +14 12 +15 +15 13 +16 +16 14 +17 +17 15 +18 +18 16 +18 17 +19 +19 18 +20 +21 +22 +22 20 +23 +23 19 +24 +24 21 +25 +25 23 +26 +26 24 +27 +27 22 +28 +28 25 +29 +29 26 +30 +30 27 +31 +31 28 +32 +32 30 +33 +33 29 +34 +34 32 +35 +35 31 +36 +36 33 +37 +37 34 +37 35 +37 36 +38 +38 37 +39 +39 38 +40 +40 39 +41 +41 40 +42 +42 41 +43 +43 42 diff --git a/src/Nerve_GIC/example/GICvoronoi.cpp b/src/Nerve_GIC/example/GICvoronoi.cpp new file mode 100644 index 00000000..9bf3de5e --- /dev/null +++ b/src/Nerve_GIC/example/GICvoronoi.cpp @@ -0,0 +1,60 @@ +#include <gudhi/GIC.h> + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off N [--v] \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/points/human.off 100 --v \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 3) && (argc != 4)) usage(argc, (argv[0] - 1)); + + std::string off_file_name(argv[1]); + int m = atoi(argv[2]); + bool verb = 0; if(argc == 4) verb = 1; + + // ---------------------------------------------------------------------------- + // Init of a graph induced complex from an OFF file + // ---------------------------------------------------------------------------- + + Gudhi::graph_induced_complex::Graph_induced_complex GIC; + GIC.set_verbose(verb); + + GIC.read_point_cloud(off_file_name); + + GIC.set_color_from_coordinate(); + + GIC.set_graph_from_OFF(off_file_name); + + GIC.set_cover_from_Voronoi(m); + + GIC.find_GIC_simplices(); + + GIC.plot_with_Geomview(); + + Simplex_tree stree; GIC.create_complex(stree); + + std::streambuf* streambufffer = std::cout.rdbuf(); + std::ostream output_stream(streambufffer); + + // ---------------------------------------------------------------------------- + // Display information about the graph induced complex + // ---------------------------------------------------------------------------- + + if(verb){ + output_stream << "Graph induced complex is of dimension " << stree.dimension() << + " - " << stree.num_simplices() << " simplices - " << + stree.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on graph induced complex simplices" << std::endl; + for (auto f_simplex : stree.filtration_simplex_range()) { + for (auto vertex : stree.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << std::endl; + } + } + + return 0; +} diff --git a/src/Nerve_GIC/example/GICvoronoi.txt b/src/Nerve_GIC/example/GICvoronoi.txt new file mode 100644 index 00000000..34517933 --- /dev/null +++ b/src/Nerve_GIC/example/GICvoronoi.txt @@ -0,0 +1,563 @@ +Graph induced complex is of dimension 2 - 561 simplices - 100 vertices. +Iterator on graph induced complex simplices +0 +1 +2 +3 +3 0 +4 +4 1 +5 +6 +7 +8 +9 +9 1 +10 +11 +11 1 +11 9 +11 9 1 +12 +12 1 +12 11 +12 11 1 +13 +13 2 +14 +14 11 +15 +15 4 +16 +16 2 +16 15 +17 +17 3 +18 +19 +19 8 +20 +20 11 +20 12 +20 12 11 +21 +22 +22 12 +23 +23 22 +24 +24 19 +25 +25 0 +25 3 +25 18 +26 +26 1 +26 4 +26 4 1 +27 +27 26 +28 +28 27 +29 +30 +31 +31 30 +32 +32 30 +32 31 +32 31 30 +33 +33 1 +33 4 +33 4 1 +33 24 +33 29 +34 +34 5 +34 21 +35 +35 2 +35 16 +35 16 2 +36 +36 17 +37 +37 24 +37 29 +38 +38 3 +38 25 +38 25 3 +39 +39 8 +39 25 +39 38 +39 38 25 +40 +40 7 +40 19 +40 24 +40 24 19 +40 37 +40 37 24 +40 39 +41 +41 5 +41 34 +41 34 5 +42 +42 21 +42 41 +43 +43 5 +43 41 +43 41 5 +43 42 +43 42 41 +44 +44 14 +44 27 +45 +45 21 +46 +46 8 +46 19 +46 19 8 +46 39 +46 39 8 +47 +47 0 +47 3 +47 3 0 +47 19 +47 38 +47 38 3 +47 39 +47 39 38 +47 40 +47 40 19 +47 40 39 +47 46 +47 46 19 +47 46 39 +48 +48 37 +48 45 +49 +49 10 +49 37 +49 48 +49 48 37 +50 +50 10 +50 37 +50 45 +50 49 +50 49 10 +50 49 37 +51 +51 45 +51 48 +51 48 45 +51 50 +51 50 45 +52 +52 27 +52 30 +52 31 +52 31 30 +53 +53 10 +53 45 +53 48 +53 48 45 +53 50 +53 50 10 +54 +54 6 +55 +55 54 +56 +56 44 +57 +57 13 +58 +58 24 +58 29 +58 33 +58 33 24 +58 33 29 +58 37 +58 37 24 +58 37 29 +59 +59 7 +59 29 +60 +60 1 +60 12 +60 12 1 +60 22 +60 22 12 +60 26 +60 26 1 +60 27 +60 27 26 +60 44 +60 44 27 +60 56 +60 56 44 +61 +61 5 +61 21 +61 34 +61 34 5 +61 34 21 +61 42 +61 42 21 +61 43 +61 43 5 +61 43 42 +62 +62 6 +62 54 +62 54 6 +62 55 +62 55 54 +63 +63 6 +63 62 +63 62 6 +64 +64 10 +64 48 +64 49 +64 49 10 +64 49 48 +64 53 +64 53 10 +64 53 48 +65 +65 21 +65 45 +65 45 21 +65 50 +65 50 45 +65 53 +65 53 50 +66 +66 14 +66 44 +66 44 14 +66 56 +66 56 44 +67 +67 13 +67 57 +67 57 13 +68 +68 2 +68 13 +68 13 2 +68 35 +68 35 2 +69 +69 21 +69 34 +69 34 21 +69 41 +69 41 34 +69 42 +69 42 21 +69 42 41 +69 65 +69 65 21 +70 +70 11 +70 14 +70 14 11 +70 20 +70 20 11 +70 56 +70 66 +70 66 14 +70 66 56 +71 +71 6 +71 54 +71 54 6 +71 55 +71 55 54 +71 62 +71 62 55 +71 63 +71 63 6 +71 63 62 +72 +72 37 +72 48 +72 48 37 +72 51 +72 51 48 +73 +73 3 +73 17 +73 17 3 +73 36 +73 36 17 +74 +74 7 +74 8 +74 39 +74 39 8 +74 40 +74 40 7 +74 40 39 +74 52 +74 59 +74 59 7 +75 +75 4 +75 15 +75 15 4 +76 +76 15 +76 75 +76 75 15 +77 +77 4 +77 75 +77 75 4 +78 +78 4 +78 26 +78 26 4 +78 27 +78 27 26 +78 28 +78 28 27 +78 75 +78 77 +78 77 4 +78 77 75 +79 +79 9 +79 11 +79 11 9 +79 14 +79 14 11 +79 27 +79 30 +79 32 +79 32 30 +79 44 +79 44 14 +79 44 27 +79 52 +79 52 27 +79 52 30 +80 +80 0 +80 25 +80 25 0 +80 39 +80 39 25 +80 47 +80 47 0 +80 47 39 +81 +81 2 +81 4 +81 15 +81 15 4 +81 16 +81 16 2 +81 16 15 +81 27 +81 29 +81 33 +81 33 4 +81 33 29 +81 52 +81 52 27 +81 59 +81 59 29 +81 74 +81 74 52 +81 74 59 +81 75 +81 78 +81 78 27 +81 78 75 +82 +82 2 +82 75 +82 81 +82 81 2 +82 81 75 +83 +83 0 +83 3 +83 3 0 +83 25 +83 25 0 +83 73 +83 73 3 +84 +84 1 +84 8 +84 19 +84 19 8 +84 24 +84 24 19 +84 33 +84 33 1 +84 33 24 +84 74 +84 74 8 +85 +85 17 +85 36 +85 36 17 +85 73 +85 73 36 +86 +86 6 +86 63 +86 63 6 +87 +87 3 +87 17 +87 17 3 +87 18 +87 25 +87 25 3 +87 25 18 +88 +88 17 +88 18 +88 85 +88 85 17 +88 87 +88 87 17 +88 87 18 +89 +89 63 +89 86 +89 86 63 +90 +90 7 +90 37 +90 40 +90 40 7 +90 40 37 +90 59 +90 59 7 +91 +91 21 +91 45 +91 45 21 +91 53 +91 53 45 +91 65 +91 65 21 +91 65 53 +92 +92 9 +92 55 +92 71 +92 71 55 +93 +93 2 +93 15 +93 16 +93 16 15 +93 35 +93 35 2 +93 35 16 +93 75 +93 76 +93 76 15 +93 76 75 +93 82 +93 82 2 +93 82 75 +94 +94 1 +94 9 +94 9 1 +94 31 +94 32 +94 32 31 +94 52 +94 52 31 +94 55 +94 74 +94 74 52 +94 79 +94 79 9 +94 79 32 +94 84 +94 84 1 +94 84 74 +94 92 +94 92 9 +94 92 55 +95 +95 18 +95 25 +95 25 18 +95 83 +95 83 25 +95 85 +95 88 +95 88 18 +95 88 85 +96 +96 63 +96 86 +96 86 63 +96 89 +96 89 63 +96 89 86 +97 +97 73 +97 83 +97 83 73 +97 85 +97 85 73 +97 95 +97 95 83 +97 95 85 +98 +98 12 +98 20 +98 20 12 +98 22 +98 22 12 +98 23 +98 23 22 +98 56 +98 60 +98 60 22 +98 60 56 +98 70 +98 70 20 +98 70 56 +99 +99 29 +99 37 +99 37 29 +99 50 +99 50 37 +99 51 +99 51 50 +99 59 +99 59 29 +99 72 +99 72 37 +99 72 51 +99 90 +99 90 37 +99 90 59 diff --git a/src/Nerve_GIC/example/MapperDeltaCoord.cpp b/src/Nerve_GIC/example/MapperDeltaCoord.cpp new file mode 100644 index 00000000..9445e988 --- /dev/null +++ b/src/Nerve_GIC/example/MapperDeltaCoord.cpp @@ -0,0 +1,62 @@ +#include <gudhi/GIC.h> + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off coordinate [--v] \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/points/human.off 2 --v \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 3) && (argc != 4)) usage(argc, (argv[0] - 1)); + + std::string off_file_name(argv[1]); + int coord = atoi(argv[2]); + bool verb = 0; if(argc == 4) verb = 1; + + // --------------------------------------- + // Init of a Mapper Delta from an OFF file + // --------------------------------------- + + Gudhi::graph_induced_complex::Graph_induced_complex GIC; + GIC.set_verbose(verb); + + GIC.read_point_cloud(off_file_name); + + GIC.set_color_from_coordinate(coord); + GIC.set_function_from_coordinate(coord); + + GIC.set_graph_from_automatic_rips(); + + GIC.set_automatic_resolution_for_GICMAP(); GIC.set_gain(); + GIC.set_cover_from_function(1); + + GIC.find_GICMAP_simplices_with_functional_minimal_cover(); + + GIC.plot_with_KeplerMapper(); + + Simplex_tree stree; GIC.create_complex(stree); + + std::streambuf* streambufffer = std::cout.rdbuf(); + std::ostream output_stream(streambufffer); + + // ------------------------------------------ + // Display information about the Mapper Delta + // ------------------------------------------ + + if(verb){ + output_stream << "Mapper Delta is of dimension " << stree.dimension() << + " - " << stree.num_simplices() << " simplices - " << + stree.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on Mapper Delta simplices" << std::endl; + for (auto f_simplex : stree.filtration_simplex_range()) { + for (auto vertex : stree.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << std::endl; + } + } + + return 0; +} diff --git a/src/Nerve_GIC/example/MapperDeltaCoord.txt b/src/Nerve_GIC/example/MapperDeltaCoord.txt new file mode 100644 index 00000000..faf832c7 --- /dev/null +++ b/src/Nerve_GIC/example/MapperDeltaCoord.txt @@ -0,0 +1,143 @@ +Mapper Delta is of dimension 1 - 141 simplices - 71 vertices. +Iterator on Mapper Delta simplices +0 +1 +2 +2 1 +3 +3 0 +4 +4 2 +5 +5 3 +6 +6 4 +7 +7 5 +8 +8 6 +9 +9 7 +10 +10 9 +11 +11 8 +12 +12 11 +13 +13 10 +14 +14 12 +15 +15 13 +16 +16 14 +17 +17 15 +18 +18 17 +19 +19 16 +20 +20 18 +21 +21 19 +22 +22 20 +23 +23 21 +24 +24 22 +25 +25 23 +26 +26 24 +27 +27 25 +28 +28 26 +28 27 +29 +29 28 +30 +30 29 +31 +32 +33 +33 30 +34 +34 31 +35 +35 32 +36 +36 34 +37 +37 33 +38 +38 35 +39 +39 37 +40 +40 38 +41 +41 36 +42 +42 39 +43 +43 40 +44 +44 41 +45 +45 42 +46 +46 44 +47 +47 43 +48 +48 45 +49 +49 46 +50 +50 47 +51 +51 48 +52 +52 49 +53 +53 50 +54 +54 51 +55 +55 52 +56 +56 53 +57 +57 54 +58 +58 55 +59 +59 56 +60 +60 57 +60 58 +60 59 +61 +61 60 +62 +62 61 +63 +63 62 +64 +64 63 +65 +65 64 +66 +66 65 +67 +67 66 +68 +68 67 +69 +69 68 +70 +70 69 diff --git a/src/Nerve_GIC/example/MapperDeltaFunc.cpp b/src/Nerve_GIC/example/MapperDeltaFunc.cpp new file mode 100644 index 00000000..448323b1 --- /dev/null +++ b/src/Nerve_GIC/example/MapperDeltaFunc.cpp @@ -0,0 +1,62 @@ +#include <gudhi/GIC.h> + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off function [--v] \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/points/COIL_database/lucky_cat.off ../../../../data/points/COIL_database/lucky_cat_PCA1 --v \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 3) && (argc != 4)) usage(argc, (argv[0] - 1)); + + std::string off_file_name(argv[1]); + std::string func_file_name = argv[2]; + bool verb = 0; if(argc == 4) verb = 1; + + // --------------------------------------- + // Init of a Mapper Delta from an OFF file + // --------------------------------------- + + Gudhi::graph_induced_complex::Graph_induced_complex GIC; + GIC.set_verbose(verb); + + GIC.read_point_cloud(off_file_name); + + GIC.set_color_from_file(func_file_name); + GIC.set_function_from_file(func_file_name); + + GIC.set_graph_from_automatic_rips(); + + GIC.set_automatic_resolution_for_GICMAP(); GIC.set_gain(); + GIC.set_cover_from_function(1); + + GIC.find_GICMAP_simplices_with_functional_minimal_cover(); + + GIC.plot_with_KeplerMapper(); + + Simplex_tree stree; GIC.create_complex(stree); + + std::streambuf* streambufffer = std::cout.rdbuf(); + std::ostream output_stream(streambufffer); + + // ------------------------------------------ + // Display information about the Mapper Delta + // ------------------------------------------ + + if(verb){ + output_stream << "Mapper Delta is of dimension " << stree.dimension() << + " - " << stree.num_simplices() << " simplices - " << + stree.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on Mapper Delta simplices" << std::endl; + for (auto f_simplex : stree.filtration_simplex_range()) { + for (auto vertex : stree.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << std::endl; + } + } + + return 0; +} diff --git a/src/Nerve_GIC/example/MapperDeltaFunc.txt b/src/Nerve_GIC/example/MapperDeltaFunc.txt new file mode 100644 index 00000000..d13d0695 --- /dev/null +++ b/src/Nerve_GIC/example/MapperDeltaFunc.txt @@ -0,0 +1,10 @@ +Mapper Delta is of dimension 1 - 8 simplices - 4 vertices. +Iterator on Mapper Delta simplices +0 +1 +1 0 +2 +2 0 +3 +3 1 +3 2 diff --git a/src/Nerve_GIC/example/Nerve.cpp b/src/Nerve_GIC/example/Nerve.cpp new file mode 100644 index 00000000..84f74625 --- /dev/null +++ b/src/Nerve_GIC/example/Nerve.cpp @@ -0,0 +1,64 @@ +#include <gudhi/GIC.h> + +void usage(int nbArgs, char * const progName) { + std::cerr << "Error: Number of arguments (" << nbArgs << ") is not correct\n"; + std::cerr << "Usage: " << progName << " filename.off coordinate resolution gain [--v] \n"; + std::cerr << " i.e.: " << progName << " ../../../../data/human.off 2 10 0.3 --v \n"; + exit(-1); // ----- >> +} + +int main(int argc, char **argv) { + if ((argc != 5) && (argc != 6)) usage(argc, (argv[0] - 1)); + + std::string off_file_name(argv[1]); + int coord = atoi(argv[2]); + int resolution = atoi(argv[3]); + double gain = atof(argv[4]); + bool verb = 0; if(argc == 6) verb = 1; + + // -------------------------------- + // Init of a Nerve from an OFF file + // -------------------------------- + + Gudhi::graph_induced_complex::Graph_induced_complex GIC; + GIC.set_verbose(verb); + + GIC.read_point_cloud(off_file_name); + + GIC.set_color_from_coordinate(coord); + GIC.set_function_from_coordinate(coord); + + GIC.set_graph_from_OFF(off_file_name); + + GIC.set_resolution_int(resolution); GIC.set_gain(gain); + GIC.set_cover_from_function(0); + + GIC.find_Nerve_simplices(); + + GIC.plot_with_KeplerMapper(); + + Simplex_tree stree; GIC.create_complex(stree); + + std::streambuf* streambufffer = std::cout.rdbuf(); + std::ostream output_stream(streambufffer); + + // ---------------------------------------------------------------------------- + // Display information about the graph induced complex + // ---------------------------------------------------------------------------- + + if(verb){ + output_stream << "Nerve is of dimension " << stree.dimension() << + " - " << stree.num_simplices() << " simplices - " << + stree.num_vertices() << " vertices." << std::endl; + + output_stream << "Iterator on Nerve simplices" << std::endl; + for (auto f_simplex : stree.filtration_simplex_range()) { + for (auto vertex : stree.simplex_vertex_range(f_simplex)) { + output_stream << vertex << " "; + } + output_stream << std::endl; + } + } + + return 0; +} diff --git a/src/Nerve_GIC/example/Nerve.txt b/src/Nerve_GIC/example/Nerve.txt new file mode 100644 index 00000000..2a861c5f --- /dev/null +++ b/src/Nerve_GIC/example/Nerve.txt @@ -0,0 +1,43 @@ +Nerve is of dimension 1 - 41 simplices - 21 vertices. +Iterator on Nerve simplices +0 +1 +2 +2 0 +3 +3 1 +4 +4 3 +5 +5 2 +6 +6 4 +7 +7 5 +8 +9 +9 6 +10 +10 7 +11 +12 +12 8 +13 +13 9 +13 10 +14 +14 11 +15 +15 13 +16 +16 12 +17 +17 14 +18 +18 15 +18 16 +18 17 +19 +19 18 +20 +20 19 diff --git a/src/Nerve_GIC/example/km.py b/src/Nerve_GIC/example/km.py new file mode 100755 index 00000000..a1a08156 --- /dev/null +++ b/src/Nerve_GIC/example/km.py @@ -0,0 +1,390 @@ +from __future__ import division +import numpy as np +from collections import defaultdict +import json +import itertools +from sklearn import cluster, preprocessing, manifold +from datetime import datetime +import sys + +class KeplerMapper(object): + # With this class you can build topological networks from (high-dimensional) data. + # + # 1) Fit a projection/lens/function to a dataset and transform it. + # For instance "mean_of_row(x) for x in X" + # 2) Map this projection with overlapping intervals/hypercubes. + # Cluster the points inside the interval + # (Note: we cluster on the inverse image/original data to lessen projection loss). + # If two clusters/nodes have the same members (due to the overlap), then: + # connect these with an edge. + # 3) Visualize the network using HTML and D3.js. + # + # functions + # --------- + # fit_transform: Create a projection (lens) from a dataset + # map: Apply Mapper algorithm on this projection and build a simplicial complex + # visualize: Turns the complex dictionary into a HTML/D3.js visualization + + def __init__(self, verbose=2): + self.verbose = verbose + + self.chunk_dist = [] + self.overlap_dist = [] + self.d = [] + self.nr_cubes = 0 + self.overlap_perc = 0 + self.clusterer = False + + def fit_transform(self, X, projection="sum", scaler=preprocessing.MinMaxScaler()): + # Creates the projection/lens from X. + # + # Input: X. Input features as a numpy array. + # Output: projected_X. original data transformed to a projection (lens). + # + # parameters + # ---------- + # projection: Projection parameter is either a string, + # a scikit class with fit_transform, like manifold.TSNE(), + # or a list of dimension indices. + # scaler: if None, do no scaling, else apply scaling to the projection + # Default: Min-Max scaling + + self.scaler = scaler + self.projection = str(projection) + + # Detect if projection is a class (for scikit-learn) + if str(type(projection))[1:6] == "class": #TODO: de-ugly-fy + reducer = projection + if self.verbose > 0: + try: + projection.set_params(**{"verbose":self.verbose}) + except: + pass + print("\n..Projecting data using: \n\t%s\n"%str(projection)) + X = reducer.fit_transform(X) + + # Detect if projection is a string (for standard functions) + if isinstance(projection, str): + if self.verbose > 0: + print("\n..Projecting data using: %s"%(projection)) + # Stats lenses + if projection == "sum": # sum of row + X = np.sum(X, axis=1).reshape((X.shape[0],1)) + if projection == "mean": # mean of row + X = np.mean(X, axis=1).reshape((X.shape[0],1)) + if projection == "median": # mean of row + X = np.median(X, axis=1).reshape((X.shape[0],1)) + if projection == "max": # max of row + X = np.max(X, axis=1).reshape((X.shape[0],1)) + if projection == "min": # min of row + X = np.min(X, axis=1).reshape((X.shape[0],1)) + if projection == "std": # std of row + X = np.std(X, axis=1).reshape((X.shape[0],1)) + + if projection == "dist_mean": # Distance of x to mean of X + X_mean = np.mean(X, axis=0) + X = np.sum(np.sqrt((X - X_mean)**2), axis=1).reshape((X.shape[0],1)) + + # Detect if projection is a list (with dimension indices) + if isinstance(projection, list): + if self.verbose > 0: + print("\n..Projecting data using: %s"%(str(projection))) + X = X[:,np.array(projection)] + + # Scaling + if scaler is not None: + if self.verbose > 0: + print("\n..Scaling with: %s\n"%str(scaler)) + X = scaler.fit_transform(X) + + return X + + def map(self, projected_X, inverse_X=None, clusterer=cluster.DBSCAN(eps=0.5,min_samples=3), nr_cubes=10, overlap_perc=0.1): + # This maps the data to a simplicial complex. Returns a dictionary with nodes and links. + # + # Input: projected_X. A Numpy array with the projection/lens. + # Output: complex. A dictionary with "nodes", "links" and "meta information" + # + # parameters + # ---------- + # projected_X projected_X. A Numpy array with the projection/lens. Required. + # inverse_X Numpy array or None. If None then the projection itself is used for clustering. + # clusterer Scikit-learn API compatible clustering algorithm. Default: DBSCAN + # nr_cubes Int. The number of intervals/hypercubes to create. + # overlap_perc Float. The percentage of overlap "between" the intervals/hypercubes. + + start = datetime.now() + + # Helper function + def cube_coordinates_all(nr_cubes, nr_dimensions): + # Helper function to get origin coordinates for our intervals/hypercubes + # Useful for looping no matter the number of cubes or dimensions + # Example: if there are 4 cubes per dimension and 3 dimensions + # return the bottom left (origin) coordinates of 64 hypercubes, + # as a sorted list of Numpy arrays + # TODO: elegance-ify... + l = [] + for x in range(nr_cubes): + l += [x] * nr_dimensions + return [np.array(list(f)) for f in sorted(set(itertools.permutations(l,nr_dimensions)))] + + nodes = defaultdict(list) + links = defaultdict(list) + complex = {} + self.nr_cubes = nr_cubes + self.clusterer = clusterer + self.overlap_perc = overlap_perc + + if self.verbose > 0: + print("Mapping on data shaped %s using dimensions\n"%(str(projected_X.shape))) + + # If inverse image is not provided, we use the projection as the inverse image (suffer projection loss) + if inverse_X is None: + inverse_X = projected_X + + # We chop up the min-max column ranges into 'nr_cubes' parts + self.chunk_dist = (np.max(projected_X, axis=0) - np.min(projected_X, axis=0))/nr_cubes + + # We calculate the overlapping windows distance + self.overlap_dist = self.overlap_perc * self.chunk_dist + + # We find our starting point + self.d = np.min(projected_X, axis=0) + + # Use a dimension index array on the projected X + # (For now this uses the entire dimensionality, but we keep for experimentation) + di = np.array([x for x in range(projected_X.shape[1])]) + + # Prefix'ing the data with ID's + ids = np.array([x for x in range(projected_X.shape[0])]) + projected_X = np.c_[ids,projected_X] + inverse_X = np.c_[ids,inverse_X] + + # Subdivide the projected data X in intervals/hypercubes with overlap + if self.verbose > 0: + total_cubes = len(cube_coordinates_all(nr_cubes,projected_X.shape[1])) + print("Creating %s hypercubes."%total_cubes) + + for i, coor in enumerate(cube_coordinates_all(nr_cubes,di.shape[0])): + # Slice the hypercube + hypercube = projected_X[ np.invert(np.any((projected_X[:,di+1] >= self.d[di] + (coor * self.chunk_dist[di])) & + (projected_X[:,di+1] < self.d[di] + (coor * self.chunk_dist[di]) + self.chunk_dist[di] + self.overlap_dist[di]) == False, axis=1 )) ] + + if self.verbose > 1: + print("There are %s points in cube_%s / %s with starting range %s"% + (hypercube.shape[0],i,total_cubes,self.d[di] + (coor * self.chunk_dist[di]))) + + # If at least one sample inside the hypercube + if hypercube.shape[0] > 0: + # Cluster the data point(s) in the cube, skipping the id-column + # Note that we apply clustering on the inverse image (original data samples) that fall inside the cube. + inverse_x = inverse_X[[int(nn) for nn in hypercube[:,0]]] + + clusterer.fit(inverse_x[:,1:]) + + if self.verbose > 1: + print("Found %s clusters in cube_%s\n"%(np.unique(clusterer.labels_[clusterer.labels_ > -1]).shape[0],i)) + + #Now for every (sample id in cube, predicted cluster label) + for a in np.c_[hypercube[:,0],clusterer.labels_]: + if a[1] != -1: #if not predicted as noise + cluster_id = str(coor[0])+"_"+str(i)+"_"+str(a[1])+"_"+str(coor)+"_"+str(self.d[di] + (coor * self.chunk_dist[di])) # TODO: de-rudimentary-ify + nodes[cluster_id].append( int(a[0]) ) # Append the member id's as integers + else: + if self.verbose > 1: + print("Cube_%s is empty.\n"%(i)) + + # Create links when clusters from different hypercubes have members with the same sample id. + candidates = itertools.combinations(nodes.keys(),2) + for candidate in candidates: + # if there are non-unique members in the union + if len(nodes[candidate[0]]+nodes[candidate[1]]) != len(set(nodes[candidate[0]]+nodes[candidate[1]])): + links[candidate[0]].append( candidate[1] ) + + # Reporting + if self.verbose > 0: + nr_links = 0 + for k in links: + nr_links += len(links[k]) + print("\ncreated %s edges and %s nodes in %s."%(nr_links,len(nodes),str(datetime.now()-start))) + + complex["nodes"] = nodes + complex["links"] = links + complex["meta"] = self.projection + + return complex + + def visualize(self, complex, color_function="", path_html="mapper_visualization_output.html", title="My Data", + graph_link_distance=30, graph_gravity=0.1, graph_charge=-120, custom_tooltips=None, width_html=0, + height_html=0, show_tooltips=True, show_title=True, show_meta=True, res=0,gain=0,minimum=0,maximum=0): + # Turns the dictionary 'complex' in a html file with d3.js + # + # Input: complex. Dictionary (output from calling .map()) + # Output: a HTML page saved as a file in 'path_html'. + # + # parameters + # ---------- + # color_function string. Not fully implemented. Default: "" (distance to origin) + # path_html file path as string. Where to save the HTML page. + # title string. HTML page document title and first heading. + # graph_link_distance int. Edge length. + # graph_gravity float. "Gravity" to center of layout. + # graph_charge int. charge between nodes. + # custom_tooltips None or Numpy Array. You could use "y"-label array for this. + # width_html int. Width of canvas. Default: 0 (full width) + # height_html int. Height of canvas. Default: 0 (full height) + # show_tooltips bool. default:True + # show_title bool. default:True + # show_meta bool. default:True + + # Format JSON for D3 graph + json_s = {} + json_s["nodes"] = [] + json_s["links"] = [] + k2e = {} # a key to incremental int dict, used for id's when linking + + for e, k in enumerate(complex["nodes"]): + # Tooltip and node color formatting, TODO: de-mess-ify + if custom_tooltips is not None: + tooltip_s = "<h2>Cluster %s</h2>"%k + " ".join(str(custom_tooltips[complex["nodes"][k][0]]).split(" ")) + if maximum == minimum: + tooltip_i = 0 + else: + tooltip_i = int(30*(custom_tooltips[complex["nodes"][k][0]]-minimum)/(maximum-minimum)) + json_s["nodes"].append({"name": str(k), "tooltip": tooltip_s, "group": 2 * int(np.log(complex["nodes"][k][2])), "color": tooltip_i}) + else: + tooltip_s = "<h2>Cluster %s</h2>Contains %s members."%(k,len(complex["nodes"][k])) + json_s["nodes"].append({"name": str(k), "tooltip": tooltip_s, "group": 2 * int(np.log(len(complex["nodes"][k]))), "color": str(k.split("_")[0])}) + k2e[k] = e + for k in complex["links"]: + for link in complex["links"][k]: + json_s["links"].append({"source": k2e[k], "target":k2e[link],"value":1}) + + # Width and height of graph in HTML output + if width_html == 0: + width_css = "100%" + width_js = 'document.getElementById("holder").offsetWidth-20' + else: + width_css = "%spx" % width_html + width_js = "%s" % width_html + if height_html == 0: + height_css = "100%" + height_js = 'document.getElementById("holder").offsetHeight-20' + else: + height_css = "%spx" % height_html + height_js = "%s" % height_html + + # Whether to show certain UI elements or not + if show_tooltips == False: + tooltips_display = "display: none;" + else: + tooltips_display = "" + + if show_meta == False: + meta_display = "display: none;" + else: + meta_display = "" + + if show_title == False: + title_display = "display: none;" + else: + title_display = "" + + with open(path_html,"wb") as outfile: + html = """<!DOCTYPE html> + <meta charset="utf-8"> + <meta name="generator" content="KeplerMapper"> + <title>%s | KeplerMapper</title> + <link href='https://fonts.googleapis.com/css?family=Roboto:700,300' rel='stylesheet' type='text/css'> + <style> + * {margin: 0; padding: 0;} + html { height: 100%%;} + body {background: #111; height: 100%%; font: 100 16px Roboto, Sans-serif;} + .link { stroke: #999; stroke-opacity: .333; } + .divs div { border-radius: 50%%; background: red; position: absolute; } + .divs { position: absolute; top: 0; left: 0; } + #holder { position: relative; width: %s; height: %s; background: #111; display: block;} + h1 { %s padding: 20px; color: #fafafa; text-shadow: 0px 1px #000,0px -1px #000; position: absolute; font: 300 30px Roboto, Sans-serif;} + h2 { text-shadow: 0px 1px #000,0px -1px #000; font: 700 16px Roboto, Sans-serif;} + .meta { position: absolute; opacity: 0.9; width: 220px; top: 80px; left: 20px; display: block; %s background: #000; line-height: 25px; color: #fafafa; border: 20px solid #000; font: 100 16px Roboto, Sans-serif;} + div.tooltip { position: absolute; width: 380px; display: block; %s padding: 20px; background: #000; border: 0px; border-radius: 3px; pointer-events: none; z-index: 999; color: #FAFAFA;} + } + </style> + <body> + <div id="holder"> + <h1>%s</h1> + <p class="meta"> + <b>Lens</b><br>%s<br><br> + <b>Length of intervals</b><br>%s<br><br> + <b>Overlap percentage</b><br>%s%%<br><br> + <b>Color Function</b><br>%s + </p> + </div> + <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> + <script> + var width = %s, + height = %s; + var color = d3.scale.ordinal() + .domain(["0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30"]) + .range(["#FF0000","#FF1400","#FF2800","#FF3c00","#FF5000","#FF6400","#FF7800","#FF8c00","#FFa000","#FFb400","#FFc800","#FFdc00","#FFf000","#fdff00","#b0ff00","#65ff00","#17ff00","#00ff36","#00ff83","#00ffd0","#00e4ff","#00c4ff","#00a4ff","#00a4ff","#0084ff","#0064ff","#0044ff","#0022ff","#0002ff","#0100ff","#0300ff","#0500ff"]); + var force = d3.layout.force() + .charge(%s) + .linkDistance(%s) + .gravity(%s) + .size([width, height]); + var svg = d3.select("#holder").append("svg") + .attr("width", width) + .attr("height", height); + + var div = d3.select("#holder").append("div") + .attr("class", "tooltip") + .style("opacity", 0.0); + + var divs = d3.select('#holder').append('div') + .attr('class', 'divs') + .attr('style', function(d) { return 'overflow: hidden; width: ' + width + 'px; height: ' + height + 'px;'; }); + + graph = %s; + force + .nodes(graph.nodes) + .links(graph.links) + .start(); + var link = svg.selectAll(".link") + .data(graph.links) + .enter().append("line") + .attr("class", "link") + .style("stroke-width", function(d) { return Math.sqrt(d.value); }); + var node = divs.selectAll('div') + .data(graph.nodes) + .enter().append('div') + .on("mouseover", function(d) { + div.transition() + .duration(200) + .style("opacity", .9); + div .html(d.tooltip + "<br/>") + .style("left", (d3.event.pageX + 100) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + }) + .on("mouseout", function(d) { + div.transition() + .duration(500) + .style("opacity", 0); + }) + .call(force.drag); + + node.append("title") + .text(function(d) { return d.name; }); + force.on("tick", function() { + link.attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); + node.attr("cx", function(d) { return d.x; }) + .attr("cy", function(d) { return d.y; }) + .attr('style', function(d) { return 'width: ' + (d.group * 2) + 'px; height: ' + (d.group * 2) + 'px; ' + 'left: '+(d.x-(d.group))+'px; ' + 'top: '+(d.y-(d.group))+'px; background: '+color(d.color)+'; box-shadow: 0px 0px 3px #111; box-shadow: 0px 0px 33px '+color(d.color)+', inset 0px 0px 5px rgba(0, 0, 0, 0.2);'}) + ; + }); + </script>"""%(title,width_css, height_css, title_display, meta_display, tooltips_display, title,complex["meta"],res,gain*100,color_function,width_js,height_js,graph_charge,graph_link_distance,graph_gravity,json.dumps(json_s)) + outfile.write(html.encode("utf-8")) + if self.verbose > 0: + print("\nWrote d3.js graph to '%s'"%path_html)
\ No newline at end of file diff --git a/src/Nerve_GIC/example/visu.py b/src/Nerve_GIC/example/visu.py new file mode 100755 index 00000000..06d22abd --- /dev/null +++ b/src/Nerve_GIC/example/visu.py @@ -0,0 +1,44 @@ +import km +import numpy as np +from collections import defaultdict + +network = {} +mapper = km.KeplerMapper(verbose=0) +data = np.zeros((3,3)) +projected_data = mapper.fit_transform( data, projection="sum", scaler=None ) + +f = open('SC.txt','r') +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 + +mapper.visualize(network, color_function = color, path_html="SC.html", 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)
\ No newline at end of file |