16#include <tbb/parallel_for.h>
20#if __has_include(<CGAL/version.h>)
21# define GUDHI_GIC_USE_CGAL 1
22# include <gudhi/Bottleneck.h>
23#elif __has_include(<hera/bottleneck.h>)
24# define GUDHI_GIC_USE_HERA 1
28# include <type_traits>
30using ssize_t = std::make_signed_t<std::size_t>;
32# include <hera/bottleneck.h>
35#include <gudhi/Debug_utils.h>
38#include <gudhi/Simplex_tree.h>
39#include <gudhi/Rips_complex.h>
40#include <gudhi/Points_off_io.h>
42#include <gudhi/Persistent_cohomology.h>
44#include <boost/config.hpp>
45#include <boost/graph/graph_traits.hpp>
46#include <boost/graph/adjacency_list.hpp>
47#include <boost/graph/connected_components.hpp>
48#include <boost/graph/dijkstra_shortest_paths.hpp>
49#include <boost/graph/subgraph.hpp>
50#include <boost/graph/graph_utility.hpp>
65namespace cover_complex {
70using Persistence_diagram = std::vector<std::pair<double, double> >;
71using Graph = boost::subgraph<
72 boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, boost::no_property,
73 boost::property<boost::edge_index_t, int, boost::property<boost::edge_weight_t, double> > > >;
74using Vertex_t = boost::graph_traits<Graph>::vertex_descriptor;
75using Index_map = boost::property_map<Graph, boost::vertex_index_t>::type;
76using Weight_map = boost::property_map<Graph, boost::edge_weight_t>::type;
98template <
typename Po
int>
101 bool verbose =
false;
104 std::vector<Point> point_cloud;
105 std::vector<std::vector<double> > distances;
110 std::vector<double> func;
111 std::vector<double> func_color;
112 bool functional_cover =
false;
114 Graph one_skeleton_OFF;
116 std::vector<Vertex_t> vertices;
118 std::vector<std::vector<int> > simplices;
119 std::vector<int> voronoi_subsamples;
121 Persistence_diagram PD;
122 std::vector<double> distribution;
124 std::vector<std::vector<int> >
126 std::map<int, std::vector<int> >
128 std::map<int, double> cover_std;
132 std::map<int, std::pair<int, double> >
135 int resolution_int = -1;
136 double resolution_double = -1;
138 double rate_constant = 10;
139 double rate_power = 0.001;
142 std::map<int, int> name2id, name2idinv;
144 std::string cover_name;
145 std::string point_cloud_name;
146 std::string color_name;
149 void remove_edges(Graph& G) {
150 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
151 for (boost::tie(ei, ei_end) = boost::edges(G); ei != ei_end; ++ei) boost::remove_edge(*ei, G);
155 double GetUniform() {
156 thread_local std::default_random_engine re;
157 std::uniform_real_distribution<double> Dist(0, 1);
162 void SampleWithoutReplacement(
int populationSize,
int sampleSize, std::vector<int>& samples) {
166 while (m < sampleSize) {
168 if ((populationSize - t) * u >= sampleSize - m) {
207 rate_constant = constant;
230 this->num_points = point_cloud.size(); data_dimension = point_cloud[0].size();
231 point_cloud_name =
"cloud"; cover.resize(this->num_points);
232 for(
int i = 0; i < this->num_points; i++){
233 boost::add_vertex(one_skeleton_OFF);
234 vertices.push_back(boost::add_vertex(one_skeleton));
236 this->point_cloud = point_cloud;
245 point_cloud_name = off_file_name;
246 std::ifstream input(off_file_name);
250 while (comment ==
'#') {
251 std::getline(input, line);
252 if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(
int))isspace))
253 comment = line[line.find_first_not_of(
' ')];
255 if (strcmp((
char*)line.c_str(),
"nOFF") == 0) {
257 while (comment ==
'#') {
258 std::getline(input, line);
259 if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(
int))isspace))
260 comment = line[line.find_first_not_of(
' ')];
262 std::stringstream stream(line);
263 stream >> data_dimension;
269 int numedges, numfaces, i, dim;
270 while (comment ==
'#') {
271 std::getline(input, line);
272 if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(
int))isspace))
273 comment = line[line.find_first_not_of(
' ')];
275 std::stringstream stream(line);
276 stream >> this->num_points;
281 while (i < this->num_points) {
282 std::getline(input, line);
283 if (!line.empty() && line[line.find_first_not_of(
' ')] !=
'#' &&
284 !all_of(line.begin(), line.end(), (int (*)(
int))isspace)) {
285 std::stringstream iss(line);
286 std::vector<double> point;
287 point.assign(std::istream_iterator<double>(iss), std::istream_iterator<double>());
288 point_cloud.emplace_back(point.begin(), point.begin() + data_dimension);
289 boost::add_vertex(one_skeleton_OFF);
290 vertices.push_back(boost::add_vertex(one_skeleton));
291 cover.emplace_back();
297 while (i < numfaces) {
298 std::getline(input, line);
299 if (!line.empty() && line[line.find_first_not_of(
' ')] !=
'#' &&
300 !all_of(line.begin(), line.end(), (int (*)(
int))isspace)) {
301 std::vector<int> simplex;
302 std::stringstream iss(line);
303 simplex.assign(std::istream_iterator<int>(iss), std::istream_iterator<int>());
305 for (
int j = 1; j <= dim; j++)
306 for (
int k = j + 1; k <= dim; k++)
307 boost::add_edge(vertices[simplex[j]], vertices[simplex[k]], one_skeleton_OFF);
312 return input.is_open();
328 remove_edges(one_skeleton);
330 std::ifstream input(graph_file_name);
333 while (std::getline(input, line)) {
334 std::stringstream stream(line);
336 while (stream >> neighb) boost::add_edge(vertices[source], vertices[neighb], one_skeleton);
345 remove_edges(one_skeleton);
346 if (num_edges(one_skeleton_OFF))
347 one_skeleton = one_skeleton_OFF;
349 std::cerr <<
"No triangulation read in OFF file!" << std::endl;
359 template <
typename Distance>
361 remove_edges(one_skeleton);
362 if (distances.size() == 0) compute_pairwise_distances(distance);
363 for (
int i = 0; i < this->num_points; i++) {
364 for (
int j = i + 1; j < this->num_points; j++) {
365 if (distances[i][j] <= threshold) {
366 boost::add_edge(vertices[i], vertices[j], one_skeleton);
367 boost::put(boost::edge_weight, one_skeleton, boost::edge(vertices[i], vertices[j], one_skeleton).first,
375 void set_graph_weights() {
376 Index_map index =
boost::get(boost::vertex_index, one_skeleton);
377 Weight_map weight =
boost::get(boost::edge_weight, one_skeleton);
378 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
379 for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
380 boost::put(weight, *ei,
381 distances[index[boost::source(*ei, one_skeleton)]][index[boost::target(*ei, one_skeleton)]]);
391 this->num_points = distance_matrix.size(); data_dimension = 0; point_cloud_name =
"matrix";
392 cover.resize(this->num_points); point_cloud.resize(this->num_points);
393 for(
int i = 0; i < this->num_points; i++){
394 boost::add_vertex(one_skeleton_OFF);
395 vertices.push_back(boost::add_vertex(one_skeleton));
397 distances = distance_matrix;
403 template <
typename Distance>
404 void compute_pairwise_distances(Distance ref_distance) {
405 std::vector<double> zeros(this->num_points);
406 for (
int i = 0; i < this->num_points; i++) distances.push_back(zeros);
407 if (verbose) std::clog <<
"Computing distances..." << std::endl;
408 for (
int i = 0; i < this->num_points; i++) {
409 int state = 100 * (i + 1) / this->num_points;
410 if (verbose && state % 10 == 0) std::clog <<
"\r" << state <<
"%" << std::flush;
411 for (
int j = i; j < this->num_points; j++) {
412 double dis = ref_distance(point_cloud[i], point_cloud[j]);
413 distances[i][j] = dis;
414 distances[j][i] = dis;
417 if (verbose) std::clog << std::endl;
430 template <
typename Distance>
432 int m = floor(this->num_points / std::exp((1 + rate_power) * std::log(std::log(this->num_points) / std::log(rate_constant))));
433 m = (std::min)(m, this->num_points - 1);
436 if (verbose) std::clog << this->num_points <<
" points in R^" << data_dimension << std::endl;
437 if (verbose) std::clog <<
"Subsampling " << m <<
" points" << std::endl;
439 if (distances.size() == 0) compute_pairwise_distances(distance);
442 std::mutex deltamutex;
443 tbb::parallel_for(0, N, [&](
int i){
444 std::vector<int> samples(m);
445 SampleWithoutReplacement(this->num_points, m, samples);
446 double hausdorff_dist = 0;
447 for (
int j = 0; j < this->num_points; j++) {
448 double mj = distances[j][samples[0]];
449 for (
int k = 1; k < m; k++) mj = (std::min)(mj, distances[j][samples[k]]);
450 hausdorff_dist = (std::max)(hausdorff_dist, mj);
453 delta += hausdorff_dist / N;
457 for (
int i = 0; i < N; i++) {
458 std::vector<int> samples(m);
459 SampleWithoutReplacement(this->num_points, m, samples);
460 double hausdorff_dist = 0;
461 for (
int j = 0; j < this->num_points; j++) {
462 double mj = distances[j][samples[0]];
463 for (
int k = 1; k < m; k++) mj = (std::min)(mj, distances[j][samples[k]]);
464 hausdorff_dist = (std::max)(hausdorff_dist, mj);
466 delta += hausdorff_dist / N;
470 if (verbose) std::clog <<
"delta = " << delta << std::endl;
486 std::ifstream input(func_file_name);
489 while (std::getline(input, line)) {
490 std::stringstream stream(line);
494 functional_cover =
true;
495 cover_name = func_file_name;
505 if(point_cloud[0].size() > 0){
506 for (
int i = 0; i < this->num_points; i++) func.push_back(point_cloud[i][k]);
507 functional_cover =
true;
508 cover_name =
"coordinate " + std::to_string(k);
511 std::cerr <<
"Only pairwise distances provided---cannot access " << k <<
"th coordinate; returning null vector instead" << std::endl;
512 for (
int i = 0; i < this->num_points; i++) func.push_back(0.0);
513 functional_cover =
true;
524 template <
class InputRange>
526 for (
int i = 0; i < this->num_points; i++) func.push_back(function[i]);
527 functional_cover =
true;
543 if (!functional_cover) {
544 std::cerr <<
"Cover needs to come from the preimages of a function." << std::endl;
547 if (type !=
"Nerve" && type !=
"GIC") {
548 std::cerr <<
"Type of complex needs to be specified." << std::endl;
553 Index_map index =
boost::get(boost::vertex_index, one_skeleton);
556 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
557 for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
558 reso = (std::max)(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
559 func[index[boost::target(*ei, one_skeleton)]]));
560 if (verbose) std::clog <<
"resolution = " << reso << std::endl;
561 resolution_double = reso;
564 if (type ==
"Nerve") {
565 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
566 for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
567 reso = (std::max)(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
568 func[index[boost::target(*ei, one_skeleton)]]) /
570 if (verbose) std::clog <<
"resolution = " << reso << std::endl;
571 resolution_double = reso;
602 if (resolution_double == -1 && resolution_int == -1) {
603 std::cerr <<
"Number and/or length of intervals not specified" << std::endl;
607 std::cerr <<
"Gain not specified" << std::endl;
612 double minf = (std::numeric_limits<float>::max)();
613 double maxf = std::numeric_limits<float>::lowest();
614 for (
int i = 0; i < this->num_points; i++) {
615 minf = (std::min)(minf, func[i]);
616 maxf = (std::max)(maxf, func[i]);
618 if (verbose) std::clog <<
"Min function value = " << minf <<
" and Max function value = " << maxf << std::endl;
621 std::vector<std::pair<double, double> > intervals;
624 if (resolution_double == -1) {
625 double incr = (maxf - minf) / resolution_int;
627 double alpha = (incr * gain) / (2 - 2 * gain);
628 double y = minf + incr + alpha;
629 std::pair<double, double> interm(x, y);
630 intervals.push_back(interm);
631 for (
int i = 1; i < resolution_int - 1; i++) {
632 x = minf + i * incr - alpha;
633 y = minf + (i + 1) * incr + alpha;
634 std::pair<double, double> inter(x, y);
635 intervals.push_back(inter);
637 x = minf + (resolution_int - 1) * incr - alpha;
639 std::pair<double, double> interM(x, y);
640 intervals.push_back(interM);
641 res = intervals.size();
643 for (
int i = 0; i < res; i++)
644 std::clog <<
"Interval " << i <<
" = [" << intervals[i].first <<
", " << intervals[i].second <<
"]"
648 if (resolution_int == -1) {
650 double y = x + resolution_double;
651 while (y <= maxf && maxf - (y - gain * resolution_double) >= resolution_double) {
652 std::pair<double, double> inter(x, y);
653 intervals.push_back(inter);
654 x = y - gain * resolution_double;
655 y = x + resolution_double;
657 std::pair<double, double> interM(x, maxf);
658 intervals.push_back(interM);
659 res = intervals.size();
661 for (
int i = 0; i < res; i++)
662 std::clog <<
"Interval " << i <<
" = [" << intervals[i].first <<
", " << intervals[i].second <<
"]"
667 double y = x + resolution_double;
669 while (count < resolution_int && y <= maxf && maxf - (y - gain * resolution_double) >= resolution_double) {
670 std::pair<double, double> inter(x, y);
671 intervals.push_back(inter);
673 x = y - gain * resolution_double;
674 y = x + resolution_double;
676 res = intervals.size();
678 for (
int i = 0; i < res; i++)
679 std::clog <<
"Interval " << i <<
" = [" << intervals[i].first <<
", " << intervals[i].second <<
"]"
686 std::vector<int> points(this->num_points);
687 for (
int i = 0; i < this->num_points; i++) points[i] = i;
688 std::sort(points.begin(), points.end(), [
this](
int p1,
int p2){return (this->func[p1] < this->func[p2]);});
692 Index_map index =
boost::get(boost::vertex_index, one_skeleton);
693 std::map<int, std::vector<int> > preimages;
694 std::map<int, double> funcstd;
696 if (verbose) std::clog <<
"Computing preimages..." << std::endl;
697 for (
int i = 0; i < res; i++) {
699 std::pair<double, double> inter1 = intervals[i];
705 std::pair<double, double> inter3 = intervals[i - 1];
706 while (func[points[tmp]] < inter3.second && tmp != this->num_points) {
707 preimages[i].push_back(points[tmp]);
715 std::pair<double, double> inter2 = intervals[i + 1];
716 while (func[points[tmp]] < inter2.first && tmp != this->num_points) {
717 preimages[i].push_back(points[tmp]);
722 while (func[points[tmp]] < inter1.second && tmp != this->num_points) {
723 preimages[i].push_back(points[tmp]);
728 std::pair<double, double> inter3 = intervals[i - 1];
729 while (func[points[tmp]] < inter3.second && tmp != this->num_points) {
730 preimages[i].push_back(points[tmp]);
733 while (tmp != this->num_points) {
734 preimages[i].push_back(points[tmp]);
741 funcstd[i] = 0.5 * (u + v);
745 if (verbose) std::clog <<
"Computing connected components (parallelized)..." << std::endl;
746 std::mutex covermutex, idmutex;
747 tbb::parallel_for(0, res, [&](
int i){
749 Graph G = one_skeleton.create_subgraph();
750 int num = preimages[i].size();
751 std::vector<int> component(num);
752 for (
int j = 0; j < num; j++) boost::add_vertex(index[vertices[preimages[i][j]]], G);
753 boost::connected_components(G, &component[0]);
757 for (
int j = 0; j < num; j++) {
759 if (component[j] > max) max = component[j];
762 int identifier = ((i + component[j])*(i + component[j]) + 3 * i + component[j]) / 2;
766 cover[preimages[i][j]].push_back(identifier);
767 cover_back[identifier].push_back(preimages[i][j]);
768 cover_fct[identifier] = i;
769 cover_std[identifier] = funcstd[i];
770 cover_color[identifier].second += func_color[preimages[i][j]];
771 cover_color[identifier].first += 1;
781 if (verbose) std::clog <<
"Computing connected components..." << std::endl;
782 for (
int i = 0; i < res; i++) {
784 Graph G = one_skeleton.create_subgraph();
785 int num = preimages[i].size();
786 std::vector<int> component(num);
787 for (
int j = 0; j < num; j++) boost::add_vertex(index[vertices[preimages[i][j]]], G);
788 boost::connected_components(G, &component[0]);
792 for (
int j = 0; j < num; j++) {
794 if (component[j] > max) max = component[j];
797 int identifier = (std::pow(i + component[j], 2) + 3 * i + component[j]) / 2;
800 cover[preimages[i][j]].push_back(identifier);
801 cover_back[identifier].push_back(preimages[i][j]);
802 cover_fct[identifier] = i;
803 cover_std[identifier] = funcstd[i];
804 cover_color[identifier].second += func_color[preimages[i][j]];
805 cover_color[identifier].first += 1;
813 maximal_dim =
id - 1;
814 for (std::map<
int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++)
815 iit->second.second /= iit->second.first;
828 std::vector<int> cov_elts, cov_number;
829 std::ifstream input(cover_file_name);
831 while (std::getline(input, line)) {
833 std::stringstream stream(line);
834 while (stream >> cov) {
835 cov_elts.push_back(cov);
836 cov_number.push_back(cov);
837 cover_fct[cov] = cov;
838 cover_color[cov].second += func_color[i];
839 cover_color[cov].first++;
840 cover_back[cov].push_back(i);
846 std::sort(cov_number.begin(), cov_number.end());
847 std::vector<int>::iterator it = std::unique(cov_number.begin(), cov_number.end());
848 cov_number.resize(std::distance(cov_number.begin(), it));
850 maximal_dim = cov_number.size() - 1;
851 for (
int i = 0; i <= maximal_dim; i++) cover_color[i].second /= cover_color[i].first;
852 cover_name = cover_file_name;
862 template <
class AssignmentRange>
864 std::vector<int> cov_elts, cov_number;
865 for(
int i=0; i < static_cast<int>(assignments.size()); i++){
867 for (
int cov : assignments[i]){
868 cov_elts.push_back(cov);
869 cov_number.push_back(cov);
870 cover_fct[cov] = cov;
871 auto& cc = cover_color[cov];
872 cc.second += func_color[i];
874 cover_back[cov].push_back(i);
879 std::sort(cov_number.begin(), cov_number.end());
880 std::vector<int>::iterator it = std::unique(cov_number.begin(), cov_number.end());
881 cov_number.resize(std::distance(cov_number.begin(), it));
883 maximal_dim = cov_number.size() - 1;
884 for (
int i = 0; i <= maximal_dim; i++) cover_color[i].second /= cover_color[i].first;
894 template <
typename Distance>
896 voronoi_subsamples.resize(m);
897 SampleWithoutReplacement(this->num_points, m, voronoi_subsamples);
898 if (distances.size() == 0) compute_pairwise_distances(distance);
900 Weight_map weight =
boost::get(boost::edge_weight, one_skeleton);
901 Index_map index =
boost::get(boost::vertex_index, one_skeleton);
902 std::vector<double> mindist(this->num_points);
903 for (
int j = 0; j < this->num_points; j++) mindist[j] = (std::numeric_limits<double>::max)();
907 if (verbose) std::clog <<
"Computing geodesic distances (parallelized)..." << std::endl;
908 std::mutex coverMutex; std::mutex mindistMutex;
909 tbb::parallel_for(0, m, [&](
int i){
910 int seed = voronoi_subsamples[i];
911 std::vector<double> dmap(this->num_points);
912 boost::dijkstra_shortest_paths(
913 one_skeleton, vertices[seed],
914 boost::weight_map(weight).distance_map(boost::make_iterator_property_map(dmap.begin(), index)));
916 coverMutex.lock(); mindistMutex.lock();
917 for (
int j = 0; j < this->num_points; j++)
918 if (mindist[j] > dmap[j]) {
919 mindist[j] = dmap[j];
920 if (cover[j].size() == 0)
921 cover[j].push_back(i);
925 coverMutex.unlock(); mindistMutex.unlock();
928 for (
int i = 0; i < m; i++) {
929 if (verbose) std::clog <<
"Computing geodesic distances to seed " << i <<
"..." << std::endl;
930 int seed = voronoi_subsamples[i];
931 std::vector<double> dmap(this->num_points);
932 boost::dijkstra_shortest_paths(
933 one_skeleton, vertices[seed],
934 boost::weight_map(weight).distance_map(boost::make_iterator_property_map(dmap.begin(), index)));
936 for (
int j = 0; j < this->num_points; j++)
937 if (mindist[j] > dmap[j]) {
938 mindist[j] = dmap[j];
939 if (cover[j].size() == 0)
940 cover[j].push_back(i);
947 for (
int i = 0; i < this->num_points; i++) {
948 cover_back[cover[i][0]].push_back(i);
949 cover_color[cover[i][0]].second += func_color[i];
950 cover_color[cover[i][0]].first++;
952 for (
int i = 0; i < m; i++) cover_color[i].second /= cover_color[i].first;
954 cover_name =
"Voronoi";
974 double subcolor(
int c) {
return cover_color[c].second; }
988 std::ifstream input(color_file_name);
991 while (std::getline(input, line)) {
992 std::stringstream stream(line);
994 func_color.push_back(f);
996 color_name = color_file_name;
1006 if(point_cloud[0].size() > 0){
1007 for (
int i = 0; i < this->num_points; i++) func_color.push_back(point_cloud[i][k]);
1008 color_name =
"coordinate ";
1009 color_name.append(std::to_string(k));
1012 std::cerr <<
"Only pairwise distances provided---cannot access " << k <<
"th coordinate; returning null vector instead" << std::endl;
1013 for (
int i = 0; i < this->num_points; i++) func.push_back(0.0);
1014 functional_cover =
true;
1015 cover_name =
"null";
1026 for (
unsigned int i = 0; i < color.size(); i++) func_color.push_back(color[i]);
1035 std::string mapp = point_cloud_name +
"_sc.dot";
1036 std::ofstream graphic(mapp);
1038 double maxv = std::numeric_limits<double>::lowest();
1039 double minv = (std::numeric_limits<double>::max)();
1040 for (std::map<
int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
1041 maxv = (std::max)(maxv, iit->second.second);
1042 minv = (std::min)(minv, iit->second.second);
1045 std::vector<int> nodes;
1048 graphic <<
"graph GIC {" << std::endl;
1050 for (std::map<
int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
1051 if (iit->second.first > mask) {
1052 nodes.push_back(iit->first);
1053 name2id[iit->first] = id;
1054 name2idinv[id] = iit->first;
1056 graphic << name2id[iit->first] <<
"[shape=circle fontcolor=black color=black label=\"" << name2id[iit->first]
1057 <<
":" << iit->second.first <<
"\" style=filled fillcolor=\""
1058 << (1 - (maxv - iit->second.second) / (maxv - minv)) * 0.6 <<
", 1, 1\"]" << std::endl;
1061 int num_simplices = simplices.size();
1062 for (
int i = 0; i < num_simplices; i++)
1063 if (simplices[i].size() == 2) {
1064 if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask) {
1065 graphic <<
" " << name2id[simplices[i][0]] <<
" -- " << name2id[simplices[i][1]] <<
" [weight=15];"
1071 std::clog << mapp <<
" file generated. It can be visualized with e.g. neato." << std::endl;
1079 int num_simplices = simplices.size();
1081 std::string mapp = point_cloud_name +
"_sc.txt";
1082 std::ofstream graphic(mapp);
1084 for (
int i = 0; i < num_simplices; i++)
1085 if (simplices[i].size() == 2)
1086 if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask) num_edges++;
1088 graphic << point_cloud_name << std::endl;
1089 graphic << cover_name << std::endl;
1090 graphic << color_name << std::endl;
1091 graphic << resolution_double <<
" " << gain << std::endl;
1092 graphic << cover_color.size() <<
" " << num_edges << std::endl;
1095 for (std::map<
int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
1096 graphic <<
id <<
" " << iit->second.second <<
" " << iit->second.first << std::endl;
1097 name2id[iit->first] = id;
1098 name2idinv[id] = iit->first;
1102 for (
int i = 0; i < num_simplices; i++)
1103 if (simplices[i].size() == 2)
1104 if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask)
1105 graphic << name2id[simplices[i][0]] <<
" " << name2id[simplices[i][1]] << std::endl;
1108 <<
" generated. It can be visualized with e.g. python KeplerMapperVisuFromTxtFile.py and firefox."
1118 assert(cover_name ==
"Voronoi");
1120 int m = voronoi_subsamples.size();
1123 std::vector<std::vector<int> > edges, faces;
1124 int numsimplices = simplices.size();
1126 std::string mapp = point_cloud_name +
"_sc.off";
1127 std::ofstream graphic(mapp);
1129 graphic <<
"OFF" << std::endl;
1130 for (
int i = 0; i < numsimplices; i++) {
1131 if (simplices[i].size() == 2) {
1133 edges.push_back(simplices[i]);
1135 if (simplices[i].size() == 3) {
1137 faces.push_back(simplices[i]);
1140 graphic << m <<
" " << numedges + numfaces << std::endl;
1141 for (
int i = 0; i < m; i++) {
1142 if (data_dimension <= 3) {
1143 for (
int j = 0; j < data_dimension; j++) graphic << point_cloud[voronoi_subsamples[i]][j] <<
" ";
1144 for (
int j = data_dimension; j < 3; j++) graphic << 0 <<
" ";
1145 graphic << std::endl;
1147 for (
int j = 0; j < 3; j++) graphic << point_cloud[voronoi_subsamples[i]][j] <<
" ";
1150 for (
int i = 0; i < numedges; i++) graphic << 2 <<
" " << edges[i][0] <<
" " << edges[i][1] << std::endl;
1151 for (
int i = 0; i < numfaces; i++)
1152 graphic << 3 <<
" " << faces[i][0] <<
" " << faces[i][1] <<
" " << faces[i][2] << std::endl;
1154 std::clog << mapp <<
" generated. It can be visualized with e.g. geomview." << std::endl;
1169 double maxf = std::numeric_limits<double>::lowest();
1170 double minf = (std::numeric_limits<double>::max)();
1171 for (std::map<int, double>::iterator it = cover_std.begin(); it != cover_std.end(); it++) {
1172 maxf = (std::max)(maxf, it->second);
1173 minf = (std::min)(minf, it->second);
1177 for (
auto const& simplex : simplices) {
1178 std::vector<int> splx = simplex;
1183 for (std::map<int, double>::iterator it = cover_std.begin(); it != cover_std.end(); it++) {
1184 int vertex = it->first;
float val = it->second;
1185 int vert[] = {vertex};
int edge[] = {vertex, -2};
1198 int max_dim = st.dimension();
1199 for (
int i = 0; i < max_dim; i++) {
1201 int num_bars = bars.size();
if(i == 0) num_bars -= 1;
1202 if(verbose) std::clog << num_bars <<
" interval(s) in dimension " << i <<
":" << std::endl;
1203 for (
int j = 0; j < num_bars; j++) {
1204 double birth = bars[j].first;
1205 double death = bars[j].second;
1206 if (i == 0 && std::isinf(death))
continue;
1208 birth = minf + (birth + 2) * (maxf - minf);
1210 birth = minf + (2 - birth) * (maxf - minf);
1212 death = minf + (death + 2) * (maxf - minf);
1214 death = minf + (2 - death) * (maxf - minf);
1215 PD.push_back(std::pair<double, double>(birth, death));
1216 if (verbose) std::clog <<
" [" << birth <<
", " << death <<
"]" << std::endl;
1229 unsigned int sz = distribution.size();
1231 for (
unsigned int i = 0; i < N - sz; i++) {
1232 if (verbose) std::clog <<
"Computing " << i <<
"th bootstrap, bottleneck distance = ";
1234 Cover_complex Cboot; Cboot.num_points = this->num_points; Cboot.data_dimension = this->data_dimension; Cboot.type = this->type; Cboot.functional_cover =
true;
1236 std::vector<int> boot(this->num_points);
1237 for (
int j = 0; j < this->num_points; j++) {
1238 double u = GetUniform();
1239 int id = std::floor(u * (this->num_points)); boot[j] = id;
1240 Cboot.point_cloud.push_back(this->point_cloud[
id]); Cboot.cover.emplace_back(); Cboot.func.push_back(this->func[
id]);
1241 boost::add_vertex(Cboot.one_skeleton_OFF); Cboot.vertices.push_back(boost::add_vertex(Cboot.one_skeleton));
1245 for (
int j = 0; j < this->num_points; j++) {
1246 std::vector<double> dist(this->num_points);
1247 for (
int k = 0; k < this->num_points; k++) dist[k] = distances[boot[j]][boot[k]];
1248 Cboot.distances.push_back(dist);
1257#ifdef GUDHI_GIC_USE_CGAL
1259#elif defined GUDHI_GIC_USE_HERA
1260 double db = hera::bottleneckDistExact(this->PD, Cboot.PD);
1263 throw std::logic_error(
"This function requires CGAL or Hera for the bottleneck distance.");
1265 if (verbose) std::clog << db << std::endl;
1266 distribution.push_back(db);
1269 std::sort(distribution.begin(), distribution.end());
1280 unsigned int N = distribution.size();
1281 double d = distribution[std::floor(alpha * N)];
1282 if (verbose) std::clog <<
"Distance corresponding to confidence " << alpha <<
" is " << d << std::endl;
1293 unsigned int N = distribution.size();
1295 for (
unsigned int i = 0; i < N; i++)
1296 if (distribution[i] >= d){ level = i * 1.0 / N;
break; }
1297 if (verbose) std::clog <<
"Confidence level of distance " << d <<
" is " << level << std::endl;
1307 double distancemin = (std::numeric_limits<double>::max)();
int N = PD.size();
1308 for (
int i = 0; i < N; i++) distancemin = (std::min)(distancemin, 0.5 * std::abs(PD[i].second - PD[i].first));
1310 if (verbose) std::clog <<
"p value = " << p_value << std::endl;
1324 template <
typename SimplicialComplex>
1326 unsigned int dimension = 0;
1327 for (
auto const& simplex : simplices) {
1328 int numvert = simplex.size();
1329 double filt = std::numeric_limits<double>::lowest();
1330 for (
int i = 0; i < numvert; i++) filt = (std::max)(cover_color[simplex[i]].second, filt);
1331 complex.insert_simplex_and_subfaces(simplex, filt);
1332 if (dimension < simplex.size() - 1) dimension = simplex.size() - 1;
1340 if (type !=
"Nerve" && type !=
"GIC") {
1341 std::cerr <<
"Type of complex needs to be specified." << std::endl;
1345 if (type ==
"Nerve") {
1347 std::sort(simplices.begin(), simplices.end());
1348 std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1349 simplices.resize(std::distance(simplices.begin(), it));
1352 if (type ==
"GIC") {
1353 Index_map index =
boost::get(boost::vertex_index, one_skeleton);
1355 if (functional_cover) {
1360 throw std::invalid_argument(
1361 "the output of this function is correct ONLY if the cover is minimal, i.e. the gain is less than 0.5.");
1364 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
1365 for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei) {
1366 int nums = cover[index[boost::source(*ei, one_skeleton)]].size();
1367 for (
int i = 0; i < nums; i++) {
1368 int vs = cover[index[boost::source(*ei, one_skeleton)]][i];
1369 int numt = cover[index[boost::target(*ei, one_skeleton)]].size();
1370 for (
int j = 0; j < numt; j++) {
1371 int vt = cover[index[boost::target(*ei, one_skeleton)]][j];
1372 if (cover_fct[vs] == cover_fct[vt] + 1 || cover_fct[vt] == cover_fct[vs] + 1) {
1373 std::vector<int> edge(2);
1374 edge[0] = (std::min)(vs, vt);
1375 edge[1] = (std::max)(vs, vt);
1376 simplices.push_back(edge);
1383 std::sort(simplices.begin(), simplices.end());
1384 std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1385 simplices.resize(std::distance(simplices.begin(), it));
1390 boost::graph_traits<Graph>::edge_iterator ei, ei_end;
1391 for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
1392 if (!(cover[index[boost::target(*ei, one_skeleton)]].size() == 1 &&
1393 cover[index[boost::target(*ei, one_skeleton)]] == cover[index[boost::source(*ei, one_skeleton)]])) {
1394 std::vector<int> edge(2);
1395 edge[0] = index[boost::source(*ei, one_skeleton)];
1396 edge[1] = index[boost::target(*ei, one_skeleton)];
1409 std::vector<int> simplx;
1411 unsigned int sz = cover[vertex].size();
1412 for (
unsigned int i = 0; i < sz; i++) {
1413 simplx.push_back(cover[vertex][i]);
1416 std::sort(simplx.begin(), simplx.end());
1417 std::vector<int>::iterator it = std::unique(simplx.begin(), simplx.end());
1418 simplx.resize(std::distance(simplx.begin(), it));
1419 simplices.push_back(simplx);
1422 std::sort(simplices.begin(), simplices.end());
1423 std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1424 simplices.resize(std::distance(simplices.begin(), it));
Compute the Euclidean distance between two Points given by a range of coordinates....
Definition: distance_functions.h:32
Options::Filtration_value Filtration_value
Type for the value of the filtration function.
Definition: Simplex_tree.h:105
void assign_filtration(Simplex_handle sh, Filtration_value fv)
Sets the filtration value of a simplex.
Definition: Simplex_tree.h:738
Complex_simplex_range complex_simplex_range() const
Returns a range over the simplices of the simplicial complex.
Definition: Simplex_tree.h:321
bool make_filtration_non_decreasing()
This function ensures that each simplex has a higher filtration value than its faces by increasing th...
Definition: Simplex_tree.h:2051
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) const
Returns a range over the vertices of a simplex.
Definition: Simplex_tree.h:369
bool has_children(SimplexHandle sh) const
Returns true if the node in the simplex tree pointed by the given simplex handle has children.
Definition: Simplex_tree.h:874
void expansion(int max_dim)
Expands the Simplex_tree containing only its one skeleton until dimension max_dim.
Definition: Simplex_tree.h:1512
std::pair< Simplex_handle, bool > insert_simplex_and_subfaces(const InputVertexRange &Nsimplex, Filtration_value filtration=0)
Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of Vertex_handles,...
Definition: Simplex_tree.h:1074
Simplex_handle find(const InputVertexRange &s) const
Given a range of Vertex_handles, returns the Simplex_handle of the simplex in the simplicial complex ...
Definition: Simplex_tree.h:899
static Simplex_handle null_simplex()
Returns a Simplex_handle different from all Simplex_handles associated to the simplices in the simpli...
Definition: Simplex_tree.h:748
Cover complex data structure.
Definition: GIC.h:99
void set_function_from_file(const std::string &func_file_name)
Creates the function f from a file containing the function values.
Definition: GIC.h:485
double set_automatic_resolution()
Computes the optimal length of intervals (i.e. the smallest interval length avoiding discretization a...
Definition: GIC.h:542
void set_cover_from_Voronoi(Distance distance, int m=100)
Creates the cover C from the Voronoï cells of a subsampling of the point cloud.
Definition: GIC.h:895
void set_resolution_with_interval_number(int reso)
Sets a number of intervals from a value stored in memory.
Definition: GIC.h:589
void set_mask(int nodemask)
Sets the mask, which is a threshold integer such that nodes in the complex that contain a number of d...
Definition: GIC.h:219
Persistence_diagram compute_PD()
Computes the extended persistence diagram of the complex.
Definition: GIC.h:1165
double compute_distance_from_confidence_level(double alpha)
Computes the bottleneck distance threshold corresponding to a specific confidence level.
Definition: GIC.h:1279
void set_graph_from_rips(double threshold, Distance distance)
Creates a graph G from a Rips complex.
Definition: GIC.h:360
void set_cover_from_file(const std::string &cover_file_name)
Creates the cover C from a file containing the cover elements of each point (the order has to be the ...
Definition: GIC.h:825
void set_graph_from_file(const std::string &graph_file_name)
Creates a graph G from a file containing the edges.
Definition: GIC.h:327
void set_cover_from_range(AssignmentRange const &assignments)
Creates the cover C from a vector of assignments stored in memory. The assignments,...
Definition: GIC.h:863
void create_complex(SimplicialComplex &complex)
Creates the simplicial complex.
Definition: GIC.h:1325
void set_type(const std::string &t)
Specifies whether the type of the output simplicial complex.
Definition: GIC.h:188
void set_distances_from_range(const std::vector< std::vector< double > > &distance_matrix)
Reads and stores the distance matrices from vector stored in memory.
Definition: GIC.h:390
void find_simplices()
Computes the simplices of the simplicial complex.
Definition: GIC.h:1339
void set_function_from_range(InputRange const &function)
Creates the function f from a vector stored in memory.
Definition: GIC.h:525
void set_cover_from_function()
Creates a cover C from the preimages of the function f.
Definition: GIC.h:601
double subcolor(int c)
Returns the mean color corresponding to a specific node of the created complex.
Definition: GIC.h:974
void set_color_from_file(const std::string &color_file_name)
Computes the function used to color the nodes of the simplicial complex from a file containing the fu...
Definition: GIC.h:987
void write_info()
Creates a .txt file called SC.txt describing the 1-skeleton, which can then be plotted with e....
Definition: GIC.h:1078
void plot_OFF()
Creates a .off file called SC.off for 3D visualization, which contains the 2-skeleton of the GIC....
Definition: GIC.h:1117
void plot_DOT()
Creates a .dot file called SC.dot for neato (part of the graphviz package) once the simplicial comple...
Definition: GIC.h:1034
void set_color_from_range(std::vector< double > color)
Computes the function used to color the nodes of the simplicial complex from a vector stored in memor...
Definition: GIC.h:1025
void set_subsampling(double constant, double power)
Sets the constants used to subsample the data set. These constants are explained in .
Definition: GIC.h:206
void set_point_cloud_from_range(const std::vector< std::vector< double > > &point_cloud)
Reads and stores the input point cloud from vector stored in memory.
Definition: GIC.h:229
double set_graph_from_automatic_rips(Distance distance, int N=100)
Creates a graph G from a Rips complex whose threshold value is automatically tuned with subsampling—s...
Definition: GIC.h:431
void set_resolution_with_interval_length(double reso)
Sets a length of intervals from a value stored in memory.
Definition: GIC.h:583
const std::vector< int > & subpopulation(int c)
Returns the data subset corresponding to a specific node of the created complex.
Definition: GIC.h:964
void set_gain(double g=0.3)
Sets a gain from a value stored in memory (default value 0.3).
Definition: GIC.h:595
void set_function_from_coordinate(int k)
Creates the function f from the k-th coordinate of the point cloud P.
Definition: GIC.h:504
void set_color_from_coordinate(int k=0)
Computes the function used to color the nodes of the simplicial complex from the k-th coordinate.
Definition: GIC.h:1005
void compute_distribution(unsigned int N=100)
Computes bootstrapped distances distribution.
Definition: GIC.h:1228
double compute_confidence_level_from_distance(double d)
Computes the confidence level of a specific bottleneck distance threshold.
Definition: GIC.h:1292
void set_graph_from_OFF()
Creates a graph G from the triangulation given by the input .OFF file.
Definition: GIC.h:344
double compute_p_value()
Computes the p-value, i.e. the opposite of the confidence level of the largest bottleneck distance pr...
Definition: GIC.h:1306
bool read_point_cloud(const std::string &off_file_name)
Reads and stores the input point cloud from .(n)OFF file.
Definition: GIC.h:244
void set_verbose(bool verb=false)
Specifies whether the program should display information or not.
Definition: GIC.h:196
Computes the persistent cohomology of a filtered complex.
Definition: Persistent_cohomology.h:54
std::vector< std::pair< Filtration_value, Filtration_value > > intervals_in_dimension(int dimension)
Returns persistence intervals for a given dimension.
Definition: Persistent_cohomology.h:680
void compute_persistent_cohomology(Filtration_value min_interval_length=0)
Compute the persistent homology of the filtered simplicial complex.
Definition: Persistent_cohomology.h:168
void init_coefficients(int charac)
Initializes the coefficient field.
Definition: Persistent_cohomology.h:152
Rips complex data structure.
Definition: Rips_complex.h:45
Global distance functions.
Graph simplicial complex methods.
double bottleneck_distance(const Persistence_diagram1 &diag1, const Persistence_diagram2 &diag2, double e=(std::numeric_limits< double >::min)())
Function to compute the Bottleneck distance between two persistence diagrams.
Definition: Bottleneck.h:116
constexpr auto & get(Gudhi::persistence_matrix::Persistence_interval< Dimension, Event_value > &i) noexcept
Partial specialization of get for Gudhi::persistence_matrix::Persistence_interval.
Definition: persistence_interval.h:199
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14
This file includes common file reader for GUDHI.
Value type for a filtration function on a cell complex.
Definition: FiltrationValue.h:20