Loading [MathJax]/extensions/TeX/AMSsymbols.js
All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Modules Pages
GIC.h
1 /* This file is part of the Gudhi Library. The Gudhi library
2  * (Geometric Understanding in Higher Dimensions) is a generic C++
3  * library for computational topology.
4  *
5  * Author: Mathieu Carriere
6  *
7  * Copyright (C) 2017 INRIA
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #ifndef GIC_H_
24 #define GIC_H_
25 
26 #include <gudhi/Debug_utils.h>
27 #include <gudhi/graph_simplicial_complex.h>
28 #include <gudhi/reader_utils.h>
29 #include <gudhi/Simplex_tree.h>
30 #include <gudhi/Rips_complex.h>
31 #include <gudhi/Points_off_io.h>
33 #include <gudhi/Persistent_cohomology.h>
34 #include <gudhi/Bottleneck.h>
35 
36 #include <boost/config.hpp>
37 #include <boost/graph/graph_traits.hpp>
38 #include <boost/graph/adjacency_list.hpp>
39 #include <boost/graph/connected_components.hpp>
40 #include <boost/graph/dijkstra_shortest_paths.hpp>
41 #include <boost/graph/subgraph.hpp>
42 #include <boost/graph/graph_utility.hpp>
43 
44 #include <iostream>
45 #include <vector>
46 #include <map>
47 #include <string>
48 #include <limits> // for numeric_limits
49 #include <utility> // for std::pair<>
50 #include <algorithm> // for std::max
51 #include <random>
52 #include <cassert>
53 #include <cmath>
54 
55 namespace Gudhi {
56 
57 namespace cover_complex {
58 
62 using Persistence_diagram = std::vector<std::pair<double, double> >;
63 using Graph = boost::subgraph<
64  boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, boost::no_property,
65  boost::property<boost::edge_index_t, int, boost::property<boost::edge_weight_t, double> > > >;
66 using Vertex_t = boost::graph_traits<Graph>::vertex_descriptor;
67 using Index_map = boost::property_map<Graph, boost::vertex_index_t>::type;
68 using Weight_map = boost::property_map<Graph, boost::edge_weight_t>::type;
69 
90 template <typename Point>
92  private:
93  bool verbose = false; // whether to display information.
94  std::string type; // Nerve or GIC
95 
96  std::vector<Point> point_cloud; // input point cloud.
97  std::vector<std::vector<double> > distances; // all pairwise distances.
98  int maximal_dim; // maximal dimension of output simplicial complex.
99  int data_dimension; // dimension of input data.
100  int n; // number of points.
101 
102  std::map<int, double> func; // function used to compute the output simplicial complex.
103  std::map<int, double>
104  func_color; // function used to compute the colors of the nodes of the output simplicial complex.
105  bool functional_cover = false; // whether we use a cover with preimages of a function or not.
106 
107  Graph one_skeleton_OFF; // one-skeleton given by the input OFF file (if it exists).
108  Graph one_skeleton; // one-skeleton used to compute the connected components.
109  std::vector<Vertex_t> vertices; // vertices of one_skeleton.
110 
111  std::vector<std::vector<int> > simplices; // simplices of output simplicial complex.
112  std::vector<int> voronoi_subsamples; // Voronoi germs (in case of Voronoi cover).
113 
114  Persistence_diagram PD;
115  std::vector<double> distribution;
116 
117  std::map<int, std::vector<int> >
118  cover; // function associating to each data point its vectors of cover elements to which it belongs.
119  std::map<int, std::vector<int> >
120  cover_back; // inverse of cover, in order to get the data points associated to a specific cover element.
121  std::map<int, double> cover_std; // standard function (induced by func) used to compute the extended persistence
122  // diagram of the output simplicial complex.
123  std::map<int, int>
124  cover_fct; // integer-valued function that allows to state if two elements of the cover are consecutive or not.
125  std::map<int, std::pair<int, double> >
126  cover_color; // size and coloring (induced by func_color) of the vertices of the output simplicial complex.
127 
128  int resolution_int = -1;
129  double resolution_double = -1;
130  double gain = -1;
131  double rate_constant = 10; // Constant in the subsampling.
132  double rate_power = 0.001; // Power in the subsampling.
133  int mask = 0; // Ignore nodes containing less than mask points.
134 
135  std::map<int, int> name2id, name2idinv;
136 
137  std::string cover_name;
138  std::string point_cloud_name;
139  std::string color_name;
140 
141  // Point comparator
142  struct Less {
143  Less(std::map<int, double> func) { Fct = func; }
144  std::map<int, double> Fct;
145  bool operator()(int a, int b) {
146  if (Fct[a] == Fct[b])
147  return a < b;
148  else
149  return Fct[a] < Fct[b];
150  }
151  };
152 
153  // Remove all edges of a graph.
154  void remove_edges(Graph& G) {
155  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
156  for (boost::tie(ei, ei_end) = boost::edges(G); ei != ei_end; ++ei) boost::remove_edge(*ei, G);
157  }
158 
159  // Find random number in [0,1].
160  double GetUniform() {
161  thread_local std::default_random_engine re;
162  thread_local std::uniform_real_distribution<double> Dist(0, 1);
163  return Dist(re);
164  }
165 
166  // Subsample points.
167  void SampleWithoutReplacement(int populationSize, int sampleSize, std::vector<int>& samples) {
168  int t = 0;
169  int m = 0;
170  double u;
171  while (m < sampleSize) {
172  u = GetUniform();
173  if ((populationSize - t) * u >= sampleSize - m) {
174  t++;
175  } else {
176  samples[m] = t;
177  t++;
178  m++;
179  }
180  }
181  }
182 
183  // *******************************************************************************************************************
184  // Utils.
185  // *******************************************************************************************************************
186 
187  public:
193  void set_type(const std::string& t) { type = t; }
194 
195  public:
201  void set_verbose(bool verb = false) { verbose = verb; }
202 
203  public:
211  void set_subsampling(double constant, double power) {
212  rate_constant = constant;
213  rate_power = power;
214  }
215 
216  public:
224  void set_mask(int nodemask) { mask = nodemask; }
225 
226  public:
232  bool read_point_cloud(const std::string& off_file_name) {
233  point_cloud_name = off_file_name;
234  std::ifstream input(off_file_name);
235  std::string line;
236 
237  char comment = '#';
238  while (comment == '#') {
239  std::getline(input, line);
240  if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(int))isspace))
241  comment = line[line.find_first_not_of(' ')];
242  }
243  if (strcmp((char*)line.c_str(), "nOFF") == 0) {
244  comment = '#';
245  while (comment == '#') {
246  std::getline(input, line);
247  if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(int))isspace))
248  comment = line[line.find_first_not_of(' ')];
249  }
250  std::stringstream stream(line);
251  stream >> data_dimension;
252  } else {
253  data_dimension = 3;
254  }
255 
256  comment = '#';
257  int numedges, numfaces, i, dim;
258  while (comment == '#') {
259  std::getline(input, line);
260  if (!line.empty() && !all_of(line.begin(), line.end(), (int (*)(int))isspace))
261  comment = line[line.find_first_not_of(' ')];
262  }
263  std::stringstream stream(line);
264  stream >> n;
265  stream >> numfaces;
266  stream >> numedges;
267 
268  i = 0;
269  while (i < n) {
270  std::getline(input, line);
271  if (!line.empty() && line[line.find_first_not_of(' ')] != '#' &&
272  !all_of(line.begin(), line.end(), (int (*)(int))isspace)) {
273  std::stringstream iss(line);
274  std::vector<double> point;
275  point.assign(std::istream_iterator<double>(iss), std::istream_iterator<double>());
276  point_cloud.emplace_back(point.begin(), point.begin() + data_dimension);
277  boost::add_vertex(one_skeleton_OFF);
278  vertices.push_back(boost::add_vertex(one_skeleton));
279  i++;
280  }
281  }
282 
283  i = 0;
284  while (i < numfaces) {
285  std::getline(input, line);
286  if (!line.empty() && line[line.find_first_not_of(' ')] != '#' &&
287  !all_of(line.begin(), line.end(), (int (*)(int))isspace)) {
288  std::vector<int> simplex;
289  std::stringstream iss(line);
290  simplex.assign(std::istream_iterator<int>(iss), std::istream_iterator<int>());
291  dim = simplex[0];
292  for (int j = 1; j <= dim; j++)
293  for (int k = j + 1; k <= dim; k++)
294  boost::add_edge(vertices[simplex[j]], vertices[simplex[k]], one_skeleton_OFF);
295  i++;
296  }
297  }
298 
299  return input.is_open();
300  }
301 
302  // *******************************************************************************************************************
303  // Graphs.
304  // *******************************************************************************************************************
305 
306  public: // Set graph from file.
314  void set_graph_from_file(const std::string& graph_file_name) {
315  remove_edges(one_skeleton);
316  int neighb;
317  std::ifstream input(graph_file_name);
318  std::string line;
319  int source;
320  while (std::getline(input, line)) {
321  std::stringstream stream(line);
322  stream >> source;
323  while (stream >> neighb) boost::add_edge(vertices[source], vertices[neighb], one_skeleton);
324  }
325  }
326 
327  public: // Set graph from OFF file.
332  remove_edges(one_skeleton);
333  if (num_edges(one_skeleton_OFF))
334  one_skeleton = one_skeleton_OFF;
335  else
336  std::cout << "No triangulation read in OFF file!" << std::endl;
337  }
338 
339  public: // Set graph from Rips complex.
346  template <typename Distance>
347  void set_graph_from_rips(double threshold, Distance distance) {
348  remove_edges(one_skeleton);
349  if (distances.size() == 0) compute_pairwise_distances(distance);
350  for (int i = 0; i < n; i++) {
351  for (int j = i + 1; j < n; j++) {
352  if (distances[i][j] <= threshold) {
353  boost::add_edge(vertices[i], vertices[j], one_skeleton);
354  boost::put(boost::edge_weight, one_skeleton, boost::edge(vertices[i], vertices[j], one_skeleton).first,
355  distances[i][j]);
356  }
357  }
358  }
359  }
360 
361  public:
362  void set_graph_weights() {
363  Index_map index = boost::get(boost::vertex_index, one_skeleton);
364  Weight_map weight = boost::get(boost::edge_weight, one_skeleton);
365  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
366  for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
367  boost::put(weight, *ei,
368  distances[index[boost::source(*ei, one_skeleton)]][index[boost::target(*ei, one_skeleton)]]);
369  }
370 
371  public: // Pairwise distances.
374  template <typename Distance>
375  void compute_pairwise_distances(Distance ref_distance) {
376  double d;
377  std::vector<double> zeros(n);
378  for (int i = 0; i < n; i++) distances.push_back(zeros);
379  std::string distance = point_cloud_name + "_dist";
380  std::ifstream input(distance, std::ios::out | std::ios::binary);
381 
382  if (input.good()) {
383  if (verbose) std::cout << "Reading distances..." << std::endl;
384  for (int i = 0; i < n; i++) {
385  for (int j = i; j < n; j++) {
386  input.read((char*)&d, 8);
387  distances[i][j] = d;
388  distances[j][i] = d;
389  }
390  }
391  input.close();
392  } else {
393  if (verbose) std::cout << "Computing distances..." << std::endl;
394  input.close();
395  std::ofstream output(distance, std::ios::out | std::ios::binary);
396  for (int i = 0; i < n; i++) {
397  int state = (int)floor(100 * (i * 1.0 + 1) / n) % 10;
398  if (state == 0 && verbose) std::cout << "\r" << state << "%" << std::flush;
399  for (int j = i; j < n; j++) {
400  double dis = ref_distance(point_cloud[i], point_cloud[j]);
401  distances[i][j] = dis;
402  distances[j][i] = dis;
403  output.write((char*)&dis, 8);
404  }
405  }
406  output.close();
407  if (verbose) std::cout << std::endl;
408  }
409  }
410 
411  public: // Automatic tuning of Rips complex.
421  template <typename Distance>
422  double set_graph_from_automatic_rips(Distance distance, int N = 100) {
423  int m = floor(n / std::exp((1 + rate_power) * std::log(std::log(n) / std::log(rate_constant))));
424  m = std::min(m, n - 1);
425  std::vector<int> samples(m);
426  double delta = 0;
427 
428  if (verbose) std::cout << n << " points in R^" << data_dimension << std::endl;
429  if (verbose) std::cout << "Subsampling " << m << " points" << std::endl;
430 
431  if (distances.size() == 0) compute_pairwise_distances(distance);
432 
433  // #pragma omp parallel for
434  for (int i = 0; i < N; i++) {
435  SampleWithoutReplacement(n, m, samples);
436  double hausdorff_dist = 0;
437  for (int j = 0; j < n; j++) {
438  double mj = distances[j][samples[0]];
439  for (int k = 1; k < m; k++) mj = std::min(mj, distances[j][samples[k]]);
440  hausdorff_dist = std::max(hausdorff_dist, mj);
441  }
442  delta += hausdorff_dist / N;
443  }
444 
445  if (verbose) std::cout << "delta = " << delta << std::endl;
446  set_graph_from_rips(delta, distance);
447  return delta;
448  }
449 
450  // *******************************************************************************************************************
451  // Functions.
452  // *******************************************************************************************************************
453 
454  public: // Set function from file.
460  void set_function_from_file(const std::string& func_file_name) {
461  int i = 0;
462  std::ifstream input(func_file_name);
463  std::string line;
464  double f;
465  while (std::getline(input, line)) {
466  std::stringstream stream(line);
467  stream >> f;
468  func.emplace(i, f);
469  i++;
470  }
471  functional_cover = true;
472  cover_name = func_file_name;
473  }
474 
475  public: // Set function from kth coordinate
482  for (int i = 0; i < n; i++) func.emplace(i, point_cloud[i][k]);
483  functional_cover = true;
484  cover_name = "coordinate " + std::to_string(k);
485  }
486 
487  public: // Set function from vector.
493  template <class InputRange>
494  void set_function_from_range(InputRange const& function) {
495  for (int i = 0; i < n; i++) func.emplace(i, function[i]);
496  functional_cover = true;
497  }
498 
499  // *******************************************************************************************************************
500  // Covers.
501  // *******************************************************************************************************************
502 
503  public: // Automatic tuning of resolution.
512  if (!functional_cover) {
513  std::cout << "Cover needs to come from the preimages of a function." << std::endl;
514  return 0;
515  }
516  if (type != "Nerve" && type != "GIC") {
517  std::cout << "Type of complex needs to be specified." << std::endl;
518  return 0;
519  }
520 
521  double reso = 0;
522  Index_map index = boost::get(boost::vertex_index, one_skeleton);
523 
524  if (type == "GIC") {
525  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
526  for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
527  reso = std::max(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
528  func[index[boost::target(*ei, one_skeleton)]]));
529  if (verbose) std::cout << "resolution = " << reso << std::endl;
530  resolution_double = reso;
531  }
532 
533  if (type == "Nerve") {
534  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
535  for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
536  reso = std::max(reso, std::abs(func[index[boost::source(*ei, one_skeleton)]] -
537  func[index[boost::target(*ei, one_skeleton)]]) /
538  gain);
539  if (verbose) std::cout << "resolution = " << reso << std::endl;
540  resolution_double = reso;
541  }
542 
543  return reso;
544  }
545 
546  public:
552  void set_resolution_with_interval_length(double reso) { resolution_double = reso; }
558  void set_resolution_with_interval_number(int reso) { resolution_int = reso; }
564  void set_gain(double g = 0.3) { gain = g; }
565 
566  public: // Set cover with preimages of function.
571  if (resolution_double == -1 && resolution_int == -1) {
572  std::cout << "Number and/or length of intervals not specified" << std::endl;
573  return;
574  }
575  if (gain == -1) {
576  std::cout << "Gain not specified" << std::endl;
577  return;
578  }
579 
580  // Read function values and compute min and max
581  double minf = std::numeric_limits<float>::max();
582  double maxf = std::numeric_limits<float>::lowest();
583  for (int i = 0; i < n; i++) {
584  minf = std::min(minf, func[i]);
585  maxf = std::max(maxf, func[i]);
586  }
587  if (verbose) std::cout << "Min function value = " << minf << " and Max function value = " << maxf << std::endl;
588 
589  // Compute cover of im(f)
590  std::vector<std::pair<double, double> > intervals;
591  int res;
592 
593  if (resolution_double == -1) { // Case we use an integer for the number of intervals.
594  double incr = (maxf - minf) / resolution_int;
595  double x = minf;
596  double alpha = (incr * gain) / (2 - 2 * gain);
597  double y = minf + incr + alpha;
598  std::pair<double, double> interm(x, y);
599  intervals.push_back(interm);
600  for (int i = 1; i < resolution_int - 1; i++) {
601  x = minf + i * incr - alpha;
602  y = minf + (i + 1) * incr + alpha;
603  std::pair<double, double> inter(x, y);
604  intervals.push_back(inter);
605  }
606  x = minf + (resolution_int - 1) * incr - alpha;
607  y = maxf;
608  std::pair<double, double> interM(x, y);
609  intervals.push_back(interM);
610  res = intervals.size();
611  if (verbose) {
612  for (int i = 0; i < res; i++)
613  std::cout << "Interval " << i << " = [" << intervals[i].first << ", " << intervals[i].second << "]"
614  << std::endl;
615  }
616  } else {
617  if (resolution_int == -1) { // Case we use a double for the length of the intervals.
618  double x = minf;
619  double y = x + resolution_double;
620  while (y <= maxf && maxf - (y - gain * resolution_double) >= resolution_double) {
621  std::pair<double, double> inter(x, y);
622  intervals.push_back(inter);
623  x = y - gain * resolution_double;
624  y = x + resolution_double;
625  }
626  std::pair<double, double> interM(x, maxf);
627  intervals.push_back(interM);
628  res = intervals.size();
629  if (verbose) {
630  for (int i = 0; i < res; i++)
631  std::cout << "Interval " << i << " = [" << intervals[i].first << ", " << intervals[i].second << "]"
632  << std::endl;
633  }
634  } else { // Case we use an integer and a double for the length of the intervals.
635  double x = minf;
636  double y = x + resolution_double;
637  int count = 0;
638  while (count < resolution_int && y <= maxf && maxf - (y - gain * resolution_double) >= resolution_double) {
639  std::pair<double, double> inter(x, y);
640  intervals.push_back(inter);
641  count++;
642  x = y - gain * resolution_double;
643  y = x + resolution_double;
644  }
645  res = intervals.size();
646  if (verbose) {
647  for (int i = 0; i < res; i++)
648  std::cout << "Interval " << i << " = [" << intervals[i].first << ", " << intervals[i].second << "]"
649  << std::endl;
650  }
651  }
652  }
653 
654  // Sort points according to function values
655  std::vector<int> points(n);
656  for (int i = 0; i < n; i++) points[i] = i;
657  std::sort(points.begin(), points.end(), Less(this->func));
658 
659  int id = 0;
660  int pos = 0;
661  Index_map index = boost::get(boost::vertex_index, one_skeleton); // int maxc = -1;
662  std::map<int, std::vector<int> > preimages;
663  std::map<int, double> funcstd;
664 
665  if (verbose) std::cout << "Computing preimages..." << std::endl;
666  for (int i = 0; i < res; i++) {
667  // Find points in the preimage
668  std::pair<double, double> inter1 = intervals[i];
669  int tmp = pos;
670  double u, v;
671 
672  if (i != res - 1) {
673  if (i != 0) {
674  std::pair<double, double> inter3 = intervals[i - 1];
675  while (func[points[tmp]] < inter3.second && tmp != n) {
676  preimages[i].push_back(points[tmp]);
677  tmp++;
678  }
679  u = inter3.second;
680  } else {
681  u = inter1.first;
682  }
683 
684  std::pair<double, double> inter2 = intervals[i + 1];
685  while (func[points[tmp]] < inter2.first && tmp != n) {
686  preimages[i].push_back(points[tmp]);
687  tmp++;
688  }
689  v = inter2.first;
690  pos = tmp;
691  while (func[points[tmp]] < inter1.second && tmp != n) {
692  preimages[i].push_back(points[tmp]);
693  tmp++;
694  }
695 
696  } else {
697  std::pair<double, double> inter3 = intervals[i - 1];
698  while (func[points[tmp]] < inter3.second && tmp != n) {
699  preimages[i].push_back(points[tmp]);
700  tmp++;
701  }
702  while (tmp != n) {
703  preimages[i].push_back(points[tmp]);
704  tmp++;
705  }
706  u = inter3.second;
707  v = inter1.second;
708  }
709 
710  funcstd[i] = 0.5 * (u + v);
711  }
712 
713  if (verbose) std::cout << "Computing connected components..." << std::endl;
714  // #pragma omp parallel for
715  for (int i = 0; i < res; i++) {
716  // Compute connected components
717  Graph G = one_skeleton.create_subgraph();
718  int num = preimages[i].size();
719  std::vector<int> component(num);
720  for (int j = 0; j < num; j++) boost::add_vertex(index[vertices[preimages[i][j]]], G);
721  boost::connected_components(G, &component[0]);
722  int max = 0;
723 
724  // For each point in preimage
725  for (int j = 0; j < num; j++) {
726  // Update number of components in preimage
727  if (component[j] > max) max = component[j];
728 
729  // Identify component with Cantor polynomial N^2 -> N
730  int identifier = (std::pow(i + component[j], 2) + 3 * i + component[j]) / 2;
731 
732  // Update covers
733  cover[preimages[i][j]].push_back(identifier);
734  cover_back[identifier].push_back(preimages[i][j]);
735  cover_fct[identifier] = i;
736  cover_std[identifier] = funcstd[i];
737  cover_color[identifier].second += func_color[preimages[i][j]];
738  cover_color[identifier].first += 1;
739  }
740 
741  // Maximal dimension is total number of connected components
742  id += max + 1;
743  }
744 
745  maximal_dim = id - 1;
746  for (std::map<int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++)
747  iit->second.second /= iit->second.first;
748  }
749 
750  public: // Set cover from file.
757  void set_cover_from_file(const std::string& cover_file_name) {
758  int i = 0;
759  int cov;
760  std::vector<int> cov_elts, cov_number;
761  std::ifstream input(cover_file_name);
762  std::string line;
763  while (std::getline(input, line)) {
764  cov_elts.clear();
765  std::stringstream stream(line);
766  while (stream >> cov) {
767  cov_elts.push_back(cov);
768  cov_number.push_back(cov);
769  cover_fct[cov] = cov;
770  cover_color[cov].second += func_color[i];
771  cover_color[cov].first++;
772  cover_back[cov].push_back(i);
773  }
774  cover[i] = cov_elts;
775  i++;
776  }
777 
778  std::sort(cov_number.begin(), cov_number.end());
779  std::vector<int>::iterator it = std::unique(cov_number.begin(), cov_number.end());
780  cov_number.resize(std::distance(cov_number.begin(), it));
781 
782  maximal_dim = cov_number.size() - 1;
783  for (int i = 0; i <= maximal_dim; i++) cover_color[i].second /= cover_color[i].first;
784  cover_name = cover_file_name;
785  }
786 
787  public: // Set cover from Voronoi
794  template <typename Distance>
795  void set_cover_from_Voronoi(Distance distance, int m = 100) {
796  voronoi_subsamples.resize(m);
797  SampleWithoutReplacement(n, m, voronoi_subsamples);
798  if (distances.size() == 0) compute_pairwise_distances(distance);
799  set_graph_weights();
800  Weight_map weight = boost::get(boost::edge_weight, one_skeleton);
801  Index_map index = boost::get(boost::vertex_index, one_skeleton);
802  std::vector<double> mindist(n);
803  for (int j = 0; j < n; j++) mindist[j] = std::numeric_limits<double>::max();
804 
805  // Compute the geodesic distances to subsamples with Dijkstra
806  // #pragma omp parallel for
807  for (int i = 0; i < m; i++) {
808  if (verbose) std::cout << "Computing geodesic distances to seed " << i << "..." << std::endl;
809  int seed = voronoi_subsamples[i];
810  std::vector<double> dmap(n);
811  boost::dijkstra_shortest_paths(
812  one_skeleton, vertices[seed],
813  boost::weight_map(weight).distance_map(boost::make_iterator_property_map(dmap.begin(), index)));
814 
815  for (int j = 0; j < n; j++)
816  if (mindist[j] > dmap[j]) {
817  mindist[j] = dmap[j];
818  if (cover[j].size() == 0)
819  cover[j].push_back(i);
820  else
821  cover[j][0] = i;
822  }
823  }
824 
825  for (int i = 0; i < n; i++) {
826  cover_back[cover[i][0]].push_back(i);
827  cover_color[cover[i][0]].second += func_color[i];
828  cover_color[cover[i][0]].first++;
829  }
830  for (int i = 0; i < m; i++) cover_color[i].second /= cover_color[i].first;
831  maximal_dim = m - 1;
832  cover_name = "Voronoi";
833  }
834 
835  public: // return subset of data corresponding to a node
842  const std::vector<int>& subpopulation(int c) { return cover_back[name2idinv[c]]; }
843 
844  // *******************************************************************************************************************
845  // Visualization.
846  // *******************************************************************************************************************
847 
848  public: // Set color from file.
855  void set_color_from_file(const std::string& color_file_name) {
856  int i = 0;
857  std::ifstream input(color_file_name);
858  std::string line;
859  double f;
860  while (std::getline(input, line)) {
861  std::stringstream stream(line);
862  stream >> f;
863  func_color.emplace(i, f);
864  i++;
865  }
866  color_name = color_file_name;
867  }
868 
869  public: // Set color from kth coordinate
875  void set_color_from_coordinate(int k = 0) {
876  for (int i = 0; i < n; i++) func_color[i] = point_cloud[i][k];
877  color_name = "coordinate ";
878  color_name.append(std::to_string(k));
879  }
880 
881  public: // Set color from vector.
887  void set_color_from_vector(std::vector<double> color) {
888  for (unsigned int i = 0; i < color.size(); i++) func_color[i] = color[i];
889  }
890 
891  public: // Create a .dot file that can be compiled with neato to produce a .pdf file.
896  void plot_DOT() {
897  std::string mapp = point_cloud_name + "_sc.dot";
898  std::ofstream graphic(mapp);
899 
900  double maxv = std::numeric_limits<double>::lowest();
901  double minv = std::numeric_limits<double>::max();
902  for (std::map<int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
903  maxv = std::max(maxv, iit->second.second);
904  minv = std::min(minv, iit->second.second);
905  }
906 
907  int k = 0;
908  std::vector<int> nodes;
909  nodes.clear();
910 
911  graphic << "graph GIC {" << std::endl;
912  int id = 0;
913  for (std::map<int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
914  if (iit->second.first > mask) {
915  nodes.push_back(iit->first);
916  name2id[iit->first] = id;
917  name2idinv[id] = iit->first;
918  id++;
919  graphic << name2id[iit->first] << "[shape=circle fontcolor=black color=black label=\"" << name2id[iit->first]
920  << ":" << iit->second.first << "\" style=filled fillcolor=\""
921  << (1 - (maxv - iit->second.second) / (maxv - minv)) * 0.6 << ", 1, 1\"]" << std::endl;
922  k++;
923  }
924  }
925  int ke = 0;
926  int num_simplices = simplices.size();
927  for (int i = 0; i < num_simplices; i++)
928  if (simplices[i].size() == 2) {
929  if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask) {
930  graphic << " " << name2id[simplices[i][0]] << " -- " << name2id[simplices[i][1]] << " [weight=15];"
931  << std::endl;
932  ke++;
933  }
934  }
935  graphic << "}";
936  graphic.close();
937  std::cout << mapp << " file generated. It can be visualized with e.g. neato." << std::endl;
938  }
939 
940  public: // Create a .txt file that can be compiled with KeplerMapper.
944  void write_info() {
945  int num_simplices = simplices.size();
946  int num_edges = 0;
947  std::string mapp = point_cloud_name + "_sc.txt";
948  std::ofstream graphic(mapp);
949 
950  for (int i = 0; i < num_simplices; i++)
951  if (simplices[i].size() == 2)
952  if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask) num_edges++;
953 
954  graphic << point_cloud_name << std::endl;
955  graphic << cover_name << std::endl;
956  graphic << color_name << std::endl;
957  graphic << resolution_double << " " << gain << std::endl;
958  graphic << cover_color.size() << " " << num_edges << std::endl;
959 
960  int id = 0;
961  for (std::map<int, std::pair<int, double> >::iterator iit = cover_color.begin(); iit != cover_color.end(); iit++) {
962  graphic << id << " " << iit->second.second << " " << iit->second.first << std::endl;
963  name2id[iit->first] = id;
964  name2idinv[id] = iit->first;
965  id++;
966  }
967 
968  for (int i = 0; i < num_simplices; i++)
969  if (simplices[i].size() == 2)
970  if (cover_color[simplices[i][0]].first > mask && cover_color[simplices[i][1]].first > mask)
971  graphic << name2id[simplices[i][0]] << " " << name2id[simplices[i][1]] << std::endl;
972  graphic.close();
973  std::cout << mapp
974  << " generated. It can be visualized with e.g. python KeplerMapperVisuFromTxtFile.py and firefox."
975  << std::endl;
976  }
977 
978  public: // Create a .off file that can be visualized (e.g. with Geomview).
983  void plot_OFF() {
984  assert(cover_name == "Voronoi");
985 
986  int m = voronoi_subsamples.size();
987  int numedges = 0;
988  int numfaces = 0;
989  std::vector<std::vector<int> > edges, faces;
990  int numsimplices = simplices.size();
991 
992  std::string mapp = point_cloud_name + "_sc.off";
993  std::ofstream graphic(mapp);
994 
995  graphic << "OFF" << std::endl;
996  for (int i = 0; i < numsimplices; i++) {
997  if (simplices[i].size() == 2) {
998  numedges++;
999  edges.push_back(simplices[i]);
1000  }
1001  if (simplices[i].size() == 3) {
1002  numfaces++;
1003  faces.push_back(simplices[i]);
1004  }
1005  }
1006  graphic << m << " " << numedges + numfaces << std::endl;
1007  for (int i = 0; i < m; i++) {
1008  if (data_dimension <= 3) {
1009  for (int j = 0; j < data_dimension; j++) graphic << point_cloud[voronoi_subsamples[i]][j] << " ";
1010  for (int j = data_dimension; j < 3; j++) graphic << 0 << " ";
1011  graphic << std::endl;
1012  } else {
1013  for (int j = 0; j < 3; j++) graphic << point_cloud[voronoi_subsamples[i]][j] << " ";
1014  }
1015  }
1016  for (int i = 0; i < numedges; i++) graphic << 2 << " " << edges[i][0] << " " << edges[i][1] << std::endl;
1017  for (int i = 0; i < numfaces; i++)
1018  graphic << 3 << " " << faces[i][0] << " " << faces[i][1] << " " << faces[i][2] << std::endl;
1019  graphic.close();
1020  std::cout << mapp << " generated. It can be visualized with e.g. geomview." << std::endl;
1021  }
1022 
1023  // *******************************************************************************************************************
1024  // Extended Persistence Diagrams.
1025  // *******************************************************************************************************************
1026 
1027  public:
1031  void compute_PD() {
1032  Simplex_tree st;
1033 
1034  // Compute max and min
1035  double maxf = std::numeric_limits<double>::lowest();
1036  double minf = std::numeric_limits<double>::max();
1037  for (std::map<int, double>::iterator it = cover_std.begin(); it != cover_std.end(); it++) {
1038  maxf = std::max(maxf, it->second);
1039  minf = std::min(minf, it->second);
1040  }
1041 
1042  for (auto const& simplex : simplices) {
1043  // Add a simplex and a cone on it
1044  std::vector<int> splx = simplex;
1045  splx.push_back(-2);
1046  st.insert_simplex_and_subfaces(splx);
1047  }
1048 
1049  // Build filtration
1050  for (auto simplex : st.complex_simplex_range()) {
1051  double filta = std::numeric_limits<double>::lowest();
1052  double filts = filta;
1053  bool ascending = true;
1054  for (auto vertex : st.simplex_vertex_range(simplex)) {
1055  if (vertex == -2) {
1056  ascending = false;
1057  continue;
1058  }
1059  filta = std::max(-2 + (cover_std[vertex] - minf) / (maxf - minf), filta);
1060  filts = std::max(2 - (cover_std[vertex] - minf) / (maxf - minf), filts);
1061  }
1062  if (ascending)
1063  st.assign_filtration(simplex, filta);
1064  else
1065  st.assign_filtration(simplex, filts);
1066  }
1067  int magic[] = {-2};
1068  st.assign_filtration(st.find(magic), -3);
1069 
1070  // Compute PD
1071  st.initialize_filtration();
1073  pcoh.init_coefficients(2);
1075 
1076  // Output PD
1077  int max_dim = st.dimension();
1078  for (int i = 0; i < max_dim; i++) {
1079  std::vector<std::pair<double, double> > bars = pcoh.intervals_in_dimension(i);
1080  int num_bars = bars.size();
1081  if(verbose) std::cout << num_bars << " interval(s) in dimension " << i << ":" << std::endl;
1082  for (int j = 0; j < num_bars; j++) {
1083  double birth = bars[j].first;
1084  double death = bars[j].second;
1085  if (i == 0 && std::isinf(death)) continue;
1086  if (birth < 0)
1087  birth = minf + (birth + 2) * (maxf - minf);
1088  else
1089  birth = minf + (2 - birth) * (maxf - minf);
1090  if (death < 0)
1091  death = minf + (death + 2) * (maxf - minf);
1092  else
1093  death = minf + (2 - death) * (maxf - minf);
1094  PD.push_back(std::pair<double, double>(birth, death));
1095  if (verbose) std::cout << " [" << birth << ", " << death << "]" << std::endl;
1096  }
1097  }
1098  }
1099 
1100  public:
1106  template <typename SimplicialComplex>
1107  void compute_distribution(int N = 100) {
1108  if (distribution.size() >= N) {
1109  std::cout << "Already done!" << std::endl;
1110  } else {
1111  for (int i = 0; i < N - distribution.size(); i++) {
1112  Cover_complex Cboot;
1113  Cboot.n = this->n;
1114  std::vector<int> boot(this->n);
1115  for (int j = 0; j < this->n; j++) {
1116  double u = GetUniform();
1117  int id = std::floor(u * (this->n));
1118  boot[j] = id;
1119  Cboot.point_cloud[j] = this->point_cloud[id];
1120  Cboot.func.emplace(j, this->func[id]);
1121  }
1122  for (int j = 0; j < n; j++) {
1123  std::vector<double> dist(n);
1124  for (int k = 0; k < n; k++) dist[k] = distances[boot[j]][boot[k]];
1125  Cboot.distances.push_back(dist);
1126  }
1127 
1129  Cboot.set_automatic_resolution();
1130  Cboot.set_gain();
1131  Cboot.set_cover_from_function();
1132  Cboot.find_simplices();
1133  Cboot.compute_PD();
1134 
1135  distribution.push_back(Gudhi::persistence_diagram::bottleneck_distance(this->PD, Cboot.PD));
1136  }
1137 
1138  std::sort(distribution.begin(), distribution.end());
1139  }
1140  }
1141 
1142  public:
1149  int N = distribution.size();
1150  return distribution[std::floor(alpha * N)];
1151  }
1152 
1153  public:
1160  int N = distribution.size();
1161  for (int i = 0; i < N; i++)
1162  if (distribution[i] > d) return i * 1.0 / N;
1163  }
1164 
1165  public:
1170  double compute_p_value() {
1171  double distancemin = -std::numeric_limits<double>::lowest();
1172  int N = PD.size();
1173  for (int i = 0; i < N; i++) distancemin = std::min(distancemin, 0.5 * (PD[i].second - PD[i].first));
1174  return 1 - compute_confidence_level_from_distance(distancemin);
1175  }
1176 
1177  // *******************************************************************************************************************
1178  // Computation of simplices.
1179  // *******************************************************************************************************************
1180 
1181  public:
1187  template <typename SimplicialComplex>
1188  void create_complex(SimplicialComplex& complex) {
1189  unsigned int dimension = 0;
1190  for (auto const& simplex : simplices) {
1191  int numvert = simplex.size();
1192  double filt = std::numeric_limits<double>::lowest();
1193  for (int i = 0; i < numvert; i++) filt = std::max(cover_color[simplex[i]].second, filt);
1194  complex.insert_simplex_and_subfaces(simplex, filt);
1195  if (dimension < simplex.size() - 1) dimension = simplex.size() - 1;
1196  }
1197  }
1198 
1199  public:
1203  if (type != "Nerve" && type != "GIC") {
1204  std::cout << "Type of complex needs to be specified." << std::endl;
1205  return;
1206  }
1207 
1208  if (type == "Nerve") {
1209  for(auto& simplex : cover)
1210  simplices.push_back(simplex.second);
1211  std::sort(simplices.begin(), simplices.end());
1212  std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1213  simplices.resize(std::distance(simplices.begin(), it));
1214  }
1215 
1216  if (type == "GIC") {
1217  Index_map index = boost::get(boost::vertex_index, one_skeleton);
1218 
1219  if (functional_cover) {
1220  // Computes the simplices in the GIC by looking at all the edges of the graph and adding the
1221  // corresponding edges in the GIC if the images of the endpoints belong to consecutive intervals.
1222 
1223  if (gain >= 0.5)
1224  throw std::invalid_argument(
1225  "the output of this function is correct ONLY if the cover is minimal, i.e. the gain is less than 0.5.");
1226 
1227  // Loop on all edges.
1228  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
1229  for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei) {
1230  int nums = cover[index[boost::source(*ei, one_skeleton)]].size();
1231  for (int i = 0; i < nums; i++) {
1232  int vs = cover[index[boost::source(*ei, one_skeleton)]][i];
1233  int numt = cover[index[boost::target(*ei, one_skeleton)]].size();
1234  for (int j = 0; j < numt; j++) {
1235  int vt = cover[index[boost::target(*ei, one_skeleton)]][j];
1236  if (cover_fct[vs] == cover_fct[vt] + 1 || cover_fct[vt] == cover_fct[vs] + 1) {
1237  std::vector<int> edge(2);
1238  edge[0] = std::min(vs, vt);
1239  edge[1] = std::max(vs, vt);
1240  simplices.push_back(edge);
1241  goto afterLoop;
1242  }
1243  }
1244  }
1245  afterLoop:;
1246  }
1247  std::sort(simplices.begin(), simplices.end());
1248  std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1249  simplices.resize(std::distance(simplices.begin(), it));
1250 
1251  } else {
1252  // Find edges to keep
1253  Simplex_tree st;
1254  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
1255  for (boost::tie(ei, ei_end) = boost::edges(one_skeleton); ei != ei_end; ++ei)
1256  if (!(cover[index[boost::target(*ei, one_skeleton)]].size() == 1 &&
1257  cover[index[boost::target(*ei, one_skeleton)]] == cover[index[boost::source(*ei, one_skeleton)]])) {
1258  std::vector<int> edge(2);
1259  edge[0] = index[boost::source(*ei, one_skeleton)];
1260  edge[1] = index[boost::target(*ei, one_skeleton)];
1261  st.insert_simplex_and_subfaces(edge);
1262  }
1263 
1264  // st.insert_graph(one_skeleton);
1265 
1266  // Build the Simplex Tree corresponding to the graph
1267  st.expansion(maximal_dim);
1268 
1269  // Find simplices of GIC
1270  simplices.clear();
1271  for (auto simplex : st.complex_simplex_range()) {
1272  if (!st.has_children(simplex)) {
1273  std::vector<int> simplx;
1274  for (auto vertex : st.simplex_vertex_range(simplex)) {
1275  unsigned int sz = cover[vertex].size();
1276  for (unsigned int i = 0; i < sz; i++) {
1277  simplx.push_back(cover[vertex][i]);
1278  }
1279  }
1280  std::sort(simplx.begin(), simplx.end());
1281  std::vector<int>::iterator it = std::unique(simplx.begin(), simplx.end());
1282  simplx.resize(std::distance(simplx.begin(), it));
1283  simplices.push_back(simplx);
1284  }
1285  }
1286  std::sort(simplices.begin(), simplices.end());
1287  std::vector<std::vector<int> >::iterator it = std::unique(simplices.begin(), simplices.end());
1288  simplices.resize(std::distance(simplices.begin(), it));
1289  }
1290  }
1291  }
1292 };
1293 
1294 } // namespace cover_complex
1295 
1296 } // namespace Gudhi
1297 
1298 #endif // GIC_H_
void init_coefficients(int charac)
Initializes the coefficient field.
Definition: Persistent_cohomology.h:168
void set_color_from_vector(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:887
void compute_distribution(int N=100)
Computes bootstrapped distances distribution.
Definition: GIC.h:1107
void plot_DOT()
Creates a .dot file called SC.dot for neato (part of the graphviz package) once the simplicial comple...
Definition: GIC.h:896
bool read_point_cloud(const std::string &off_file_name)
Reads and stores the input point cloud.
Definition: GIC.h:232
void expansion(int max_dim)
Expands the Simplex_tree containing only its one skeleton until dimension max_dim.
Definition: Simplex_tree.h:1033
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:224
void set_function_from_range(InputRange const &function)
Creates the function f from a vector stored in memory.
Definition: GIC.h:494
void set_graph_from_file(const std::string &graph_file_name)
Creates a graph G from a file containing the edges.
Definition: GIC.h:314
Computes the persistent cohomology of a filtered complex.
Definition: Persistent_cohomology.h:64
const std::vector< int > & subpopulation(int c)
Returns the data subset corresponding to a specific node of the created complex.
Definition: GIC.h:842
Simplex Tree data structure for representing simplicial complexes.
Definition: Simplex_tree.h:72
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:683
double compute_distance_from_confidence_level(double alpha)
Computes the bottleneck distance threshold corresponding to a specific confidence level...
Definition: GIC.h:1148
void set_type(const std::string &t)
Specifies whether the type of the output simplicial complex.
Definition: GIC.h:193
void find_simplices()
Computes the simplices of the simplicial complex.
Definition: GIC.h:1202
void write_info()
Creates a .txt file called SC.txt describing the 1-skeleton, which can then be plotted with e...
Definition: GIC.h:944
Compute the Euclidean distance between two Points given by a range of coordinates. The points are assumed to have the same dimension.
Definition: distance_functions.h:43
void set_function_from_coordinate(int k)
Creates the function f from the k-th coordinate of the point cloud P.
Definition: GIC.h:481
Definition: SimplicialComplexForAlpha.h:26
Rips complex data structure.
Definition: Rips_complex.h:57
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:875
void create_complex(SimplicialComplex &complex)
Creates the simplicial complex.
Definition: GIC.h:1188
void set_graph_from_OFF()
Creates a graph G from the triangulation given by the input .OFF file.
Definition: GIC.h:331
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:460
void set_subsampling(double constant, double power)
Sets the constants used to subsample the data set. These constants are explained in ...
Definition: GIC.h:211
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:795
void set_verbose(bool verb=false)
Specifies whether the program should display information or not.
Definition: GIC.h:201
std::vector< std::pair< Filtration_value, Filtration_value > > intervals_in_dimension(int dimension)
Returns persistence intervals for a given dimension.
Definition: Persistent_cohomology.h:703
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:1170
void initialize_filtration()
Initializes the filtrations, i.e. sort the simplices according to their order in the filtration and i...
Definition: Simplex_tree.h:796
Simplex_handle find(const InputVertexRange &s)
Given a range of Vertex_handles, returns the Simplex_handle of the simplex in the simplicial complex ...
Definition: Simplex_tree.h:517
Value type for a filtration function on a cell complex.
Definition: FiltrationValue.h:32
Complex_simplex_range complex_simplex_range()
Returns a range over the simplices of the simplicial complex.
Definition: Simplex_tree.h:214
void assign_filtration(Simplex_handle sh, Filtration_value fv)
Sets the filtration value of a simplex. In debug mode, if sh is a null_simplex.
Definition: Simplex_tree.h:421
Simplex_vertex_range simplex_vertex_range(Simplex_handle sh)
Returns a range over the vertices of a simplex.
Definition: Simplex_tree.h:261
Global distance functions.
void set_graph_from_rips(double threshold, Distance distance)
Creates a graph G from a Rips complex.
Definition: GIC.h:347
bool has_children(SimplexHandle sh) const
Returns true if the node in the simplex tree pointed by sh has children.
Definition: Simplex_tree.h:504
void plot_OFF()
Creates a .off file called SC.off for 3D visualization, which contains the 2-skeleton of the GIC...
Definition: GIC.h:983
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:103
int dimension(Simplex_handle sh)
Returns the dimension of a simplex.
Definition: Simplex_tree.h:476
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—...
Definition: GIC.h:422
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:855
double compute_confidence_level_from_distance(double d)
Computes the confidence level of a specific bottleneck distance threshold.
Definition: GIC.h:1159
void set_resolution_with_interval_number(int reso)
Sets a number of intervals from a value stored in memory.
Definition: GIC.h:558
void set_resolution_with_interval_length(double reso)
Sets a length of intervals from a value stored in memory.
Definition: GIC.h:552
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:757
void set_cover_from_function()
Creates a cover C from the preimages of the function f.
Definition: GIC.h:570
Options::Filtration_value Filtration_value
Type for the value of the filtration function.
Definition: Simplex_tree.h:79
void compute_persistent_cohomology(Filtration_value min_interval_length=0)
Compute the persistent homology of the filtered simplicial complex.
Definition: Persistent_cohomology.h:184
Cover complex data structure.
Definition: GIC.h:91
void compute_PD()
Computes the extended persistence diagram of the complex.
Definition: GIC.h:1031
double set_automatic_resolution()
Computes the optimal length of intervals (i.e. the smallest interval length avoiding discretization a...
Definition: GIC.h:511
This file includes common file reader for GUDHI.
void set_gain(double g=0.3)
Sets a gain from a value stored in memory (default value 0.3).
Definition: GIC.h:564
GUDHI  Version 2.1.0  - C++ library for Topological Data Analysis (TDA) and Higher Dimensional Geometry Understanding.  - Copyright : GPL v3 Generated on Wed Jan 31 2018 09:40:55 for GUDHI by Doxygen 1.8.11