Loading...
Searching...
No Matches
Persistence_heat_maps.h
1/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2 * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3 * Author(s): Pawel Dlotko and Mathieu Carriere
4 *
5 * Copyright (C) 2019 Inria
6 *
7 * Modification(s):
8 * - 2018/04 MC: Add discrete/non-discrete mechanism and non-discrete version
9 * - 2025/06 Hannah Schreiber: Various small bug fixes (missing `inline`s, `DEBUG_TRACES`s etc.)
10 * - YYYY/MM Author: Description of the modification
11 */
12
13#ifndef PERSISTENCE_HEAT_MAPS_H_
14#define PERSISTENCE_HEAT_MAPS_H_
15
16// standard include
17#ifdef DEBUG_TRACES
18#include <iostream> // std::clog, std::cerr
19#endif
20#include <fstream> // std::ofstream, std::ifstream
21#include <sstream> // std::stringstream
22#include <cstddef> // std::size_t
23#include <stdexcept> // std::invalid_argument
24#include <cmath> // std::sqrt, std::pow, std::atan, std::exp
25#include <limits> // std::numeric_limits
26#include <algorithm> // std::nth_element
27#include <functional> // std::function
28#include <utility> // std::pair
29#include <vector>
30#include <string>
31
32// gudhi include
33#include <gudhi/read_persistence_from_file.h>
35#include <gudhi/Debug_utils.h>
36
37namespace Gudhi {
38namespace Persistence_representations {
39
46inline std::vector<std::vector<double> > create_Gaussian_filter(std::size_t pixel_radius, double sigma)
47{
48 // we are computing the kernel mask to 2 standard deviations away from the center. We discretize it in a grid of a
49 // size 2*pixel_radius times 2*pixel_radius.
50
51 double r = 0;
52 double sigma_sqr = sigma * sigma;
53
54 // sum is for normalization
55 double sum = 0;
56
57 // initialization of a kernel:
58 std::vector<std::vector<double> > kernel(2 * pixel_radius + 1, std::vector<double>(2 * pixel_radius + 1, 0));
59
60#ifdef DEBUG_TRACES
61 std::clog << "Kernel initialize \n";
62 std::clog << "pixel_radius : " << pixel_radius << std::endl;
63 std::clog << "kernel.size() : " << kernel.size() << std::endl;
64#endif
65
66 for (int x = -pixel_radius; x <= static_cast<int>(pixel_radius); x++) {
67 for (int y = -pixel_radius; y <= static_cast<int>(pixel_radius); y++) {
68 double real_x = 2 * sigma * x / pixel_radius;
69 double real_y = 2 * sigma * y / pixel_radius;
70 r = std::sqrt(real_x * real_x + real_y * real_y);
71 kernel[x + pixel_radius][y + pixel_radius] = (std::exp(-(r * r) / sigma_sqr)) / (3.141592 * sigma_sqr);
72 sum += kernel[x + pixel_radius][y + pixel_radius];
73 }
74 }
75
76 // normalize the kernel
77 for (std::size_t i = 0; i != kernel.size(); ++i) {
78 for (std::size_t j = 0; j != kernel[i].size(); ++j) {
79 kernel[i][j] /= sum;
80 }
81 }
82
83#ifdef DEBUG_TRACES
84 std::clog << "Here is the kernel : \n";
85 for (std::size_t i = 0; i != kernel.size(); ++i) {
86 for (std::size_t j = 0; j != kernel[i].size(); ++j) {
87 std::clog << kernel[i][j] << " ";
88 }
89 std::clog << std::endl;
90 }
91#endif
92 return kernel;
93}
94
95// There are various options to scale the points depending on their location. One can for instance:
96// (1) do nothing (scale all of them with the weight 1), as with constant_scaling_function
97// (2) Scale them by the distance to the diagonal, as with distance_from_diagonal_scaling
98// (3) Scale them with the square of their distance to diagonal, as with squared_distance_from_diagonal_scaling
99// (4) Scale them with arc_tan_of_persistence_of_point
100// (5) Scale them with weight_by_setting_maximal_interval_to_have_length_one
101
102// TODO: replace all std::pow(x, 2) with x * x ?
103
112{
113 public:
114 double operator()(const std::pair<double, double>& point_in_diagram) { return 1; }
115};
116
125{
126 public:
127 double operator()(const std::pair<double, double>& point_in_diagram)
128 {
129 // (point_in_diagram.first+point_in_diagram.second)/2.0
130 return std::sqrt(std::pow((point_in_diagram.first - (point_in_diagram.first + point_in_diagram.second) / 2.0), 2) +
131 std::pow((point_in_diagram.second - (point_in_diagram.first + point_in_diagram.second) / 2.0), 2));
132 }
133};
134
143{
144 public:
145 double operator()(const std::pair<double, double>& point_in_diagram)
146 {
147 return std::pow((point_in_diagram.first - (point_in_diagram.first + point_in_diagram.second) / 2.0), 2) +
148 std::pow((point_in_diagram.second - (point_in_diagram.first + point_in_diagram.second) / 2.0), 2);
149 }
150};
151
161{
162 public:
163 double operator()(const std::pair<double, double>& point_in_diagram)
164 {
165 return std::atan(point_in_diagram.second - point_in_diagram.first);
166 }
167};
168
178class weight_by_setting_maximal_interval_to_have_length_one
179{
180 public:
181 weight_by_setting_maximal_interval_to_have_length_one(double len) : length_of_maximal_interval(len) {}
182
183 double operator()(const std::pair<double, double>& point_in_diagram)
184 {
185 return (point_in_diagram.second - point_in_diagram.first) / this->length_of_maximal_interval;
186 }
187
188 private:
189 double length_of_maximal_interval;
190};
191
201template <typename Scalling_of_kernels = constant_scaling_function>
203{
204 public:
210 : min_(0),
211 max_(0),
212 number_of_functions_for_vectorization_(1),
213 number_of_functions_for_projections_to_reals_(1),
214 f_(),
215 erase_below_diagonal_(false)
216 {}
217
234 Persistence_heat_maps(const std::vector<std::pair<double, double> >& interval,
235 const std::vector<std::vector<double> >& filter = create_Gaussian_filter(5, 1),
236 bool erase_below_diagonal = false,
237 std::size_t number_of_pixels = 1000,
238 double min = std::numeric_limits<double>::max(),
239 double max = std::numeric_limits<double>::max())
240 : number_of_functions_for_vectorization_(1), number_of_functions_for_projections_to_reals_(1)
241 {
242 this->_construct(interval, filter, erase_below_diagonal, number_of_pixels, min, max);
243 }
244
264 Persistence_heat_maps(const char* filename,
265 const std::vector<std::vector<double> >& filter = create_Gaussian_filter(5, 1),
266 bool erase_below_diagonal = false,
267 std::size_t number_of_pixels = 1000,
268 double min = std::numeric_limits<double>::max(),
269 double max = std::numeric_limits<double>::max(),
270 unsigned int dimension = std::numeric_limits<unsigned int>::max())
271 : number_of_functions_for_vectorization_(1), number_of_functions_for_projections_to_reals_(1)
272 {
273 int dim = dimension == std::numeric_limits<unsigned int>::max() ? -1 : static_cast<int>(dimension);
274 auto intervals_ = read_persistence_intervals_in_one_dimension_from_file(filename, dim);
275 this->_construct(intervals_, filter, erase_below_diagonal, number_of_pixels, min, max);
276 }
277
283 const std::vector<std::pair<double, double> >& interval,
284 const std::function<double(const std::pair<double, double>&, const std::pair<double, double>&)>& kernel,
285 std::size_t number_of_x_pixels,
286 std::size_t number_of_y_pixels,
287 double min_x = 0,
288 double max_x = 1,
289 double min_y = 0,
290 double max_y = 1)
291 : min_(min_x),
292 max_(max_x),
293 heat_map_(number_of_y_pixels),
294 discrete_(true),
295 number_of_functions_for_vectorization_(1),
296 number_of_functions_for_projections_to_reals_(1)
297 {
298 double step_x = (max_x - min_x) / (number_of_x_pixels - 1);
299 double step_y = (max_y - min_y) / (number_of_y_pixels - 1);
300
301 int num_pts = interval.size();
302 for (std::size_t i = 0; i < number_of_y_pixels; i++) {
303 double y = min_y + i * step_y;
304 this->heat_map_[i].reserve(number_of_x_pixels);
305 for (std::size_t j = 0; j < number_of_x_pixels; j++) {
306 double x = min_x + j * step_x;
307 std::pair<double, double> grid_point(x, y);
308 double pixel_value = 0;
309 for (int k = 0; k < num_pts; k++) pixel_value += this->f_(interval[k]) * kernel(interval[k], grid_point);
310 this->heat_map_[i].push_back(pixel_value);
311 }
312 }
313 }
314
319 const std::vector<std::pair<double, double> >& interval,
320 const std::function<double(const std::pair<double, double>&, const std::pair<double, double>&)>& kernel)
321 : discrete_(false),
322 number_of_functions_for_vectorization_(1),
323 number_of_functions_for_projections_to_reals_(1),
324 kernel_(kernel),
325 interval_(interval)
326 {
327 int num_pts = this->interval_.size();
328 for (int i = 0; i < num_pts; i++) this->weights_.push_back(this->f_(this->interval_[i]));
329 }
330
336 void compute_mean(const std::vector<Persistence_heat_maps*>& maps);
337
343 void compute_median(const std::vector<Persistence_heat_maps*>& maps);
344
348 void compute_percentage_of_active(const std::vector<Persistence_heat_maps*>& maps, std::size_t cutoff = 1);
349
350 // put to file subroutine
356 void print_to_file(const char* filename) const;
357
362 void load_from_file(const char* filename);
363
364 // TODO: replace all use of this method with a GUDHI_CHECK?
368 bool check_if_the_same(const Persistence_heat_maps& second) const
369 {
370 if (this->heat_map_.size() != second.heat_map_.size()) {
371#ifdef DEBUG_TRACES
372 std::clog << "this->heat_map_.size() : " << this->heat_map_.size()
373 << " \n second.heat_map_.size() : " << second.heat_map_.size() << std::endl;
374#endif
375 return false;
376 }
377 if (this->min_ != second.min_) {
378#ifdef DEBUG_TRACES
379 std::clog << "this->min_ : " << this->min_ << ", second.min_ : " << second.min_ << std::endl;
380#endif
381 return false;
382 }
383 if (this->max_ != second.max_) {
384#ifdef DEBUG_TRACES
385 std::clog << "this->max_ : " << this->max_ << ", second.max_ : " << second.max_ << std::endl;
386#endif
387 return false;
388 }
389 // in the other case we may assume that the persistence images are defined on the same domain.
390 return true;
391 }
392
396 double get_min() const { return this->min_; }
397
401 double get_max() const { return this->max_; }
402
406 bool operator==(const Persistence_heat_maps& rhs) const
407 {
408 if (!this->check_if_the_same(rhs)) {
409#ifdef DEBUG_TRACES
410 std::clog << "The domains are not the same \n";
411#endif
412 return false; // in this case, the domains are not the same, so the maps cannot be the same.
413 }
414 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
415 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
416 if (!almost_equal(this->heat_map_[i][j], rhs.heat_map_[i][j])) {
417#ifdef DEBUG_TRACES
418 std::clog << "this->heat_map_[" << i << "][" << j << "] = " << this->heat_map_[i][j] << std::endl;
419 std::clog << "rhs.heat_map_[" << i << "][" << j << "] = " << rhs.heat_map_[i][j] << std::endl;
420#endif
421 return false;
422 }
423 }
424 }
425 return true;
426 }
427
431 bool operator!=(const Persistence_heat_maps& rhs) const { return !((*this) == rhs); }
432
436 void plot(const char* filename) const;
437
438 template <typename Operation_type>
439 friend Persistence_heat_maps operation_on_pair_of_heat_maps(const Persistence_heat_maps& first,
440 const Persistence_heat_maps& second,
441 Operation_type&& operation)
442 {
443 // first check if the heat maps are compatible
444 if (!first.check_if_the_same(second)) {
445#ifdef DEBUG_TRACES
446 std::cerr << "Sizes of the heat maps are not compatible. The program will now terminate \n";
447#endif
448 throw std::invalid_argument("Sizes of the heat maps are not compatible. The program will now terminate \n");
449 }
451 result.min_ = first.min_;
452 result.max_ = first.max_;
453 result.heat_map_.reserve(first.heat_map_.size());
454 for (std::size_t i = 0; i != first.heat_map_.size(); ++i) {
455 std::vector<double> v;
456 v.reserve(first.heat_map_[i].size());
457 for (std::size_t j = 0; j != first.heat_map_[i].size(); ++j) {
458 v.push_back(operation(first.heat_map_[i][j], second.heat_map_[i][j]));
459 }
460 result.heat_map_.push_back(v);
461 }
462 return result;
463 } // operation_on_pair_of_heat_maps
464
465 // TODO: would this method not make more sense as a friend to mirror `operation_on_pair_of_heat_maps`?
466 // Or as modifying the heat map itself instead of a copy (and invert the hierarchy of operator* and operator*=) ?
472 {
474 result.min_ = this->min_;
475 result.max_ = this->max_;
476 result.heat_map_.reserve(this->heat_map_.size());
477 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
478 std::vector<double> v;
479 v.reserve(this->heat_map_[i].size());
480 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
481 v.push_back(this->heat_map_[i][j] * scalar);
482 }
483 result.heat_map_.push_back(v);
484 }
485 return result;
486 }
487
492 {
493 return operation_on_pair_of_heat_maps(first, second, std::plus<double>());
494 }
495
500 {
501 return operation_on_pair_of_heat_maps(first, second, std::minus<double>());
502 }
503
508 {
509 return A.multiply_by_scalar(scalar);
510 }
511
516 {
517 return A.multiply_by_scalar(scalar);
518 }
519
523 Persistence_heat_maps operator*(double scalar) { return this->multiply_by_scalar(scalar); }
524
529 {
530 *this = *this + rhs;
531 return *this;
532 }
533
538 {
539 *this = *this - rhs;
540 return *this;
541 }
542
547 {
548 *this = *this * x;
549 return *this;
550 }
551
556 {
557 if (x == 0) throw std::invalid_argument("In operator /=, division by 0. Program terminated.");
558 *this = *this * (1 / x);
559 return *this;
560 }
561
562 // Implementations of functions for various concepts.
563
568 std::vector<double> vectorize(int number_of_function) const;
569
574 std::size_t number_of_vectorize_functions() const { return this->number_of_functions_for_vectorization_; }
575
582 double project_to_R(int number_of_function) const;
583
588 std::size_t number_of_projections_to_R() const { return this->number_of_functions_for_projections_to_reals_; }
589
596 double distance(const Persistence_heat_maps& second_, double power = 1) const;
597
602 void compute_average(const std::vector<Persistence_heat_maps*>& to_average);
603
609 double compute_scalar_product(const Persistence_heat_maps& second_) const;
610
611 // end of implementation of functions needed for concepts.
612
616 std::pair<double, double> get_x_range() const { return std::make_pair(this->min_, this->max_); }
617
621 std::pair<double, double> get_y_range() const { return this->get_x_range(); }
622
623 protected:
624 double min_;
625 double max_;
626 std::vector<std::vector<double> > heat_map_;
627
628 private:
629 std::vector<std::vector<double> > _check_and_initialize_maps(const std::vector<Persistence_heat_maps*>& maps);
630 void _construct(const std::vector<std::pair<double, double> >& intervals,
631 const std::vector<std::vector<double> >& filter = create_Gaussian_filter(5, 1),
632 bool erase_below_diagonal = false,
633 std::size_t number_of_pixels = 1000,
634 double min = std::numeric_limits<double>::max(),
635 double max = std::numeric_limits<double>::max());
636
637 // Boolean indicating if we are computing persistence image (true) or persistence weighted gaussian kernel (false)
638 // TODO: constexpr ?
639 const bool discrete_ = true;
640 std::size_t number_of_functions_for_vectorization_;
641 std::size_t number_of_functions_for_projections_to_reals_;
642 std::function<double(std::pair<double, double>, std::pair<double, double>)> kernel_;
643 std::vector<std::pair<double, double> > interval_;
644 std::vector<double> weights_;
645
646 // data
647 Scalling_of_kernels f_;
648 bool erase_below_diagonal_;
649};
650
651// if min == max, then the program is requested to set up the values itself based on persistence intervals
652template <typename Scalling_of_kernels>
653void Persistence_heat_maps<Scalling_of_kernels>::_construct(
654 const std::vector<std::pair<double, double> >& intervals,
655 const std::vector<std::vector<double> >& filter,
656 bool erase_below_diagonal,
657 std::size_t number_of_pixels,
658 double min,
659 double max)
660{
661#ifdef DEBUG_TRACES
662 std::clog << "Entering construct procedure \n";
663#endif
664
665 Scalling_of_kernels f;
666 this->f_ = f;
667
668#ifdef DEBUG_TRACES
669 std::clog << "min and max passed to construct() procedure: " << min << " " << max << std::endl;
670#endif
671
672 if (min == max) {
673#ifdef DEBUG_TRACES
674 std::clog << "min and max parameters will be determined based on intervals \n";
675#endif
676 // in this case, we want the program to set up the min and max values by itself.
677 min = std::numeric_limits<double>::max();
678 max = -std::numeric_limits<double>::max();
679
680 for (std::size_t i = 0; i != intervals.size(); ++i) {
681 if (intervals[i].first < min) min = intervals[i].first;
682 if (intervals[i].second > max) max = intervals[i].second;
683 }
684 // now we have the structure filled in, and moreover we know min and max values of the interval, so we know the
685 // range.
686
687 // add some more space:
688 min -= fabs(max - min) / 100;
689 max += fabs(max - min) / 100;
690 }
691
692#ifdef DEBUG_TRACES
693 std::clog << "min_ : " << min << std::endl;
694 std::clog << "max_ : " << max << std::endl;
695 std::clog << "number_of_pixels : " << number_of_pixels << std::endl;
696#endif
697
698 this->min_ = min;
699 this->max_ = max;
700
701 // initialization of the structure heat_map_
702 std::vector<std::vector<double> > heat_map_;
703 for (std::size_t i = 0; i != number_of_pixels; ++i) {
704 std::vector<double> v(number_of_pixels, 0);
705 heat_map_.push_back(v);
706 }
707 this->heat_map_ = heat_map_;
708
709#ifdef DEBUG_TRACES
710 std::clog << "Done creating of the heat map, now we will fill in the structure \n";
711#endif
712
713 for (std::size_t pt_nr = 0; pt_nr != intervals.size(); ++pt_nr) {
714 // compute the value of intervals_[pt_nr] in the grid:
715 int x_grid =
716 static_cast<int>((intervals[pt_nr].first - this->min_) / (this->max_ - this->min_) * number_of_pixels);
717 int y_grid =
718 static_cast<int>((intervals[pt_nr].second - this->min_) / (this->max_ - this->min_) * number_of_pixels);
719
720#ifdef DEBUG_TRACES
721 std::clog << "point : " << intervals[pt_nr].first << " , " << intervals[pt_nr].second << std::endl;
722 std::clog << "x_grid : " << x_grid << std::endl;
723 std::clog << "y_grid : " << y_grid << std::endl;
724#endif
725
726 // x_grid and y_grid gives a center of the kernel. We want to have its lower left corner. To get this, we need to
727 // shift x_grid and y_grid by a grid diameter.
728 x_grid -= filter.size() / 2;
729 y_grid -= filter.size() / 2;
730 // note that the numbers x_grid and y_grid may be negative.
731
732#ifdef DEBUG_TRACES
733 std::clog << "After shift : \n";
734 std::clog << "x_grid : " << x_grid << std::endl;
735 std::clog << "y_grid : " << y_grid << std::endl;
736#endif
737
738 double scaling_value = this->f_(intervals[pt_nr]);
739
740 for (std::size_t i = 0; i != filter.size(); ++i) {
741 for (std::size_t j = 0; j != filter.size(); ++j) {
742 // if the point (x_grid+i,y_grid+j) is the correct point in the grid.
743 if (((x_grid + i) >= 0) && (x_grid + i < this->heat_map_.size()) && ((y_grid + j) >= 0) &&
744 (y_grid + j < this->heat_map_.size())) {
745#ifdef DEBUG_TRACES
746 std::clog << y_grid + j << " " << x_grid + i << std::endl;
747#endif
748 this->heat_map_[y_grid + j][x_grid + i] += scaling_value * filter[i][j];
749#ifdef DEBUG_TRACES
750 std::clog << "Position : (" << x_grid + i << "," << y_grid + j
751 << ") got increased by the value : " << filter[i][j] << std::endl;
752#endif
753 }
754 }
755 }
756 }
757
758 // now it remains to cut everything below diagonal if the user wants us to.
759 if (erase_below_diagonal) {
760 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
761 for (std::size_t j = i; j != this->heat_map_.size(); ++j) {
762 this->heat_map_[i][j] = 0;
763 }
764 }
765 }
766} // construct
767
768template <typename Scalling_of_kernels>
769std::vector<std::vector<double> > Persistence_heat_maps<Scalling_of_kernels>::_check_and_initialize_maps(
770 const std::vector<Persistence_heat_maps*>& maps)
771{
772 // checking if all the heat maps are of the same size:
773 GUDHI_CHECK_code(
774 for (std::size_t i = 0; i != maps.size(); ++i) {
775 GUDHI_CHECK(maps[i]->heat_map_.size() == maps[0]->heat_map_.size(),
776 "Sizes of Persistence_heat_maps are not compatible.");
777 GUDHI_CHECK(maps[i]->heat_map_[0].size() == maps[0]->heat_map_[0].size(),
778 "Sizes of Persistence_heat_maps are not compatible.");
779 }
780 );
781
782 const auto& map = maps[0]->heat_map_;
783 return std::vector<std::vector<double> >(map.size(), std::vector<double>(map[0].size(), 0));
784}
785
786template <typename Scalling_of_kernels>
787void Persistence_heat_maps<Scalling_of_kernels>::compute_median(const std::vector<Persistence_heat_maps*>& maps)
788{
789 std::vector<std::vector<double> > heat_maps = this->_check_and_initialize_maps(maps);
790
791 std::vector<double> to_compute_median(maps.size());
792 for (std::size_t i = 0; i != heat_maps.size(); ++i) {
793 for (std::size_t j = 0; j != heat_maps[i].size(); ++j) {
794 for (std::size_t map_no = 0; map_no != maps.size(); ++map_no) {
795 to_compute_median[map_no] = maps[map_no]->heat_map_[i][j];
796 }
797 std::nth_element(
798 to_compute_median.begin(), to_compute_median.begin() + to_compute_median.size() / 2, to_compute_median.end());
799 heat_maps[i][j] = to_compute_median[to_compute_median.size() / 2];
800 }
801 }
802 this->heat_map_ = heat_maps;
803 this->min_ = maps[0]->min_;
804 this->max_ = maps[0]->max_;
805}
806
807template <typename Scalling_of_kernels>
808void Persistence_heat_maps<Scalling_of_kernels>::compute_mean(const std::vector<Persistence_heat_maps*>& maps)
809{
810 std::vector<std::vector<double> > heat_maps = this->_check_and_initialize_maps(maps);
811 for (std::size_t i = 0; i != heat_maps.size(); ++i) {
812 for (std::size_t j = 0; j != heat_maps[i].size(); ++j) {
813 double mean = 0;
814 for (std::size_t map_no = 0; map_no != maps.size(); ++map_no) {
815 mean += maps[map_no]->heat_map_[i][j];
816 }
817 heat_maps[i][j] = mean / static_cast<double>(maps.size());
818 }
819 }
820 this->heat_map_ = heat_maps;
821 this->min_ = maps[0]->min_;
822 this->max_ = maps[0]->max_;
823}
824
825template <typename Scalling_of_kernels>
827 const std::vector<Persistence_heat_maps*>& maps,
828 std::size_t cutoff)
829{
830 std::vector<std::vector<double> > heat_maps = this->_check_and_initialize_maps(maps);
831
832 for (std::size_t i = 0; i != heat_maps.size(); ++i) {
833 for (std::size_t j = 0; j != heat_maps[i].size(); ++j) {
834 std::size_t number_of_active_levels = 0;
835 for (std::size_t map_no = 0; map_no != maps.size(); ++map_no) {
836 if (maps[map_no]->heat_map_[i][j]) number_of_active_levels++;
837 }
838 if (number_of_active_levels > cutoff) {
839 heat_maps[i][j] = number_of_active_levels;
840 } else {
841 heat_maps[i][j] = 0;
842 }
843 }
844 }
845 this->heat_map_ = heat_maps;
846 this->min_ = maps[0]->min_;
847 this->max_ = maps[0]->max_;
848}
849
850template <typename Scalling_of_kernels>
852{
853 std::ofstream out;
854 std::stringstream gnuplot_script;
855 gnuplot_script << filename << "_GnuplotScript";
856
857 out.open(gnuplot_script.str().c_str());
858 out << "plot '-' matrix with image" << std::endl;
859 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
860 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
861 out << this->heat_map_[i][j] << " ";
862 }
863 out << std::endl;
864 }
865 out.close();
866#ifdef DEBUG_TRACES
867 std::clog << "To visualize, install gnuplot and type the command: gnuplot -persist -e \"load \'"
868 << gnuplot_script.str().c_str() << "\'\"" << std::endl;
869#endif
870}
871
872template <typename Scalling_of_kernels>
874{
875 std::ofstream out;
876 out.open(filename);
877
878 // First we store this->min_ and this->max_ values:
879 out << this->min_ << " " << this->max_ << std::endl;
880 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
881 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
882 out << this->heat_map_[i][j] << " ";
883 }
884 out << std::endl;
885 }
886 out.close();
887}
888
889template <typename Scalling_of_kernels>
891{
892 std::ifstream in;
893 in.open(filename);
894
895 // checking if the file exist / if it was open.
896 if (!in.good()) {
897#ifdef DEBUG_TRACES
898 std::cerr << "The file : " << filename << " do not exist. The program will now terminate \n";
899#endif
900 throw std::invalid_argument("The persistence landscape file do not exist.");
901 }
902
903 // now we read the file one by one.
904
905 in >> this->min_ >> this->max_;
906#ifdef DEBUG_TRACES
907 std::clog << "Reading the following values of min and max : " << this->min_ << " , " << this->max_ << std::endl;
908#endif
909
910 std::string temp;
911 std::getline(in, temp);
912 while (in.good()) {
913 std::getline(in, temp);
914 std::stringstream lineSS;
915 lineSS << temp;
916
917 std::vector<double> line_of_heat_map;
918 while (lineSS.good()) {
919 double point;
920
921 lineSS >> point;
922 line_of_heat_map.push_back(point);
923#ifdef DEBUG_TRACES
924 std::clog << point << " ";
925#endif
926 }
927#ifdef DEBUG_TRACES
928 std::clog << std::endl;
929#endif
930
931 if (in.good()) this->heat_map_.push_back(line_of_heat_map);
932 }
933 in.close();
934#ifdef DEBUG_TRACES
935 std::clog << "Done \n";
936#endif
937}
938
939// Implementation of virtual methods:
940template <typename Scalling_of_kernels>
941std::vector<double> Persistence_heat_maps<Scalling_of_kernels>::vectorize(int number_of_function) const
942{
943 std::vector<double> result;
944
945 if (!discrete_) {
946#ifdef DEBUG_TRACES
947 std::clog << "No vectorize method in case of infinite dimensional vectorization" << std::endl;
948#endif
949 return result;
950 }
951
952 // convert this->heat_map_ into one large vector:
953 std::size_t size_of_result = 0;
954 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
955 size_of_result += this->heat_map_[i].size();
956 }
957
958 result.reserve(size_of_result);
959
960 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
961 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
962 result.push_back(this->heat_map_[i][j]);
963 }
964 }
965
966 return result;
967}
968
969template <typename Scalling_of_kernels>
971 double power) const
972{
973 if (this->discrete_) {
974 // first we need to check if (*this) and second are defined on the same domain and have the same dimensions:
975 if (!this->check_if_the_same(second)) {
976#ifdef DEBUG_TRACES
977 std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between "
978 "them. The program will now terminate";
979#endif
980 throw std::invalid_argument("The persistence images are of non compatible sizes.");
981 }
982
983 // if we are here, we know that the two persistence images are defined on the same domain, so we can start
984 // computing their distances:
985
986 double distance = 0;
987 if (power < std::numeric_limits<double>::max()) {
988 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
989 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
990 distance += std::pow(std::fabs(this->heat_map_[i][j] - second.heat_map_[i][j]), power);
991 }
992 }
993 } else {
994 // in this case, we compute max norm distance
995 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
996 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
997 auto diff = std::fabs(this->heat_map_[i][j] - second.heat_map_[i][j]);
998 if (distance < diff) distance = diff;
999 }
1000 }
1001 }
1002 return distance;
1003 } else {
1004 return std::sqrt(this->compute_scalar_product(*this) + second.compute_scalar_product(second) -
1005 2 * this->compute_scalar_product(second));
1006 }
1007}
1008
1009template <typename Scalling_of_kernels>
1011{
1012 double result = 0;
1013 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
1014 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
1015 result += this->heat_map_[i][j];
1016 }
1017 }
1018 return result;
1019}
1020
1021template <typename Scalling_of_kernels>
1023 const std::vector<Persistence_heat_maps*>& to_average)
1024{
1025 this->compute_mean(to_average);
1026}
1027
1028template <typename Scalling_of_kernels>
1030 const Persistence_heat_maps& second) const
1031{
1032 if (discrete_) {
1033 // first we need to check if (*this) and second are defined on the same domain and have the same dimensions:
1034 if (!this->check_if_the_same(second)) {
1035#ifdef DEBUG_TRACES
1036 std::cerr << "The persistence images are of non compatible sizes. We cannot therefore compute distance between "
1037 "them. The program will now terminate";
1038#endif
1039 throw std::invalid_argument("The persistence images are of non compatible sizes.");
1040 }
1041
1042 // if we are here, we know that the two persistence images are defined on the same domain, so we can start computing
1043 // their scalar product:
1044 double scalar_prod = 0;
1045 for (std::size_t i = 0; i != this->heat_map_.size(); ++i) {
1046 for (std::size_t j = 0; j != this->heat_map_[i].size(); ++j) {
1047 scalar_prod += this->heat_map_[i][j] * second.heat_map_[i][j];
1048 }
1049 }
1050 return scalar_prod;
1051 } else {
1052 int num_pts1 = this->interval_.size();
1053 int num_pts2 = second.interval_.size();
1054 double kernel_val = 0;
1055 for (int i = 0; i < num_pts1; i++) {
1056 std::pair<double, double> pi = this->interval_[i];
1057 for (int j = 0; j < num_pts2; j++) {
1058 std::pair<double, double> pj = second.interval_[j];
1059 kernel_val += this->weights_[i] * second.weights_[j] * this->kernel_(pi, pj);
1060 }
1061 }
1062 return kernel_val;
1063 }
1064}
1065
1066} // namespace Persistence_representations
1067} // namespace Gudhi
1068
1069#endif // PERSISTENCE_HEAT_MAPS_H_
A class implementing persistence heat maps.
Definition Persistence_heat_maps.h:203
std::size_t number_of_vectorize_functions() const
Definition Persistence_heat_maps.h:574
void compute_mean(const std::vector< Persistence_heat_maps * > &maps)
Definition Persistence_heat_maps.h:808
bool check_if_the_same(const Persistence_heat_maps &second) const
Definition Persistence_heat_maps.h:368
std::vector< double > vectorize(int number_of_function) const
Definition Persistence_heat_maps.h:941
friend Persistence_heat_maps operator*(const Persistence_heat_maps &A, double scalar)
Definition Persistence_heat_maps.h:515
friend Persistence_heat_maps operator*(double scalar, const Persistence_heat_maps &A)
Definition Persistence_heat_maps.h:507
Persistence_heat_maps & operator+=(const Persistence_heat_maps &rhs)
Definition Persistence_heat_maps.h:528
std::pair< double, double > get_y_range() const
Definition Persistence_heat_maps.h:621
Persistence_heat_maps operator*(double scalar)
Definition Persistence_heat_maps.h:523
std::size_t number_of_projections_to_R() const
Definition Persistence_heat_maps.h:588
bool operator!=(const Persistence_heat_maps &rhs) const
Definition Persistence_heat_maps.h:431
Persistence_heat_maps & operator*=(double x)
Definition Persistence_heat_maps.h:546
void print_to_file(const char *filename) const
Definition Persistence_heat_maps.h:873
void compute_percentage_of_active(const std::vector< Persistence_heat_maps * > &maps, std::size_t cutoff=1)
Definition Persistence_heat_maps.h:826
Persistence_heat_maps multiply_by_scalar(double scalar) const
Definition Persistence_heat_maps.h:471
Persistence_heat_maps & operator/=(double x)
Definition Persistence_heat_maps.h:555
Persistence_heat_maps & operator-=(const Persistence_heat_maps &rhs)
Definition Persistence_heat_maps.h:537
bool operator==(const Persistence_heat_maps &rhs) const
Definition Persistence_heat_maps.h:406
double project_to_R(int number_of_function) const
Definition Persistence_heat_maps.h:1010
friend Persistence_heat_maps operator-(const Persistence_heat_maps &first, const Persistence_heat_maps &second)
Definition Persistence_heat_maps.h:499
Persistence_heat_maps(const std::vector< std::pair< double, double > > &interval, const std::vector< std::vector< double > > &filter=create_Gaussian_filter(5, 1), bool erase_below_diagonal=false, std::size_t number_of_pixels=1000, double min=std::numeric_limits< double >::max(), double max=std::numeric_limits< double >::max())
Constructor. All parameters except interval are optional.
Definition Persistence_heat_maps.h:234
Persistence_heat_maps(const char *filename, const std::vector< std::vector< double > > &filter=create_Gaussian_filter(5, 1), bool erase_below_diagonal=false, std::size_t number_of_pixels=1000, double min=std::numeric_limits< double >::max(), double max=std::numeric_limits< double >::max(), unsigned int dimension=std::numeric_limits< unsigned int >::max())
Constructor from a file. All parameters except interval are optional.
Definition Persistence_heat_maps.h:264
Persistence_heat_maps()
Definition Persistence_heat_maps.h:209
Persistence_heat_maps(const std::vector< std::pair< double, double > > &interval, const std::function< double(const std::pair< double, double > &, const std::pair< double, double > &)> &kernel, std::size_t number_of_x_pixels, std::size_t number_of_y_pixels, double min_x=0, double max_x=1, double min_y=0, double max_y=1)
Definition Persistence_heat_maps.h:282
void compute_average(const std::vector< Persistence_heat_maps * > &to_average)
Definition Persistence_heat_maps.h:1022
std::pair< double, double > get_x_range() const
Definition Persistence_heat_maps.h:616
double get_min() const
Definition Persistence_heat_maps.h:396
double get_max() const
Definition Persistence_heat_maps.h:401
double distance(const Persistence_heat_maps &second_, double power=1) const
Definition Persistence_heat_maps.h:970
void load_from_file(const char *filename)
Definition Persistence_heat_maps.h:890
void compute_median(const std::vector< Persistence_heat_maps * > &maps)
Definition Persistence_heat_maps.h:787
Persistence_heat_maps(const std::vector< std::pair< double, double > > &interval, const std::function< double(const std::pair< double, double > &, const std::pair< double, double > &)> &kernel)
Definition Persistence_heat_maps.h:318
void plot(const char *filename) const
Definition Persistence_heat_maps.h:851
double compute_scalar_product(const Persistence_heat_maps &second_) const
Definition Persistence_heat_maps.h:1029
friend Persistence_heat_maps operator+(const Persistence_heat_maps &first, const Persistence_heat_maps &second)
Definition Persistence_heat_maps.h:491
This file contain an implementation of some common procedures used in the Persistence_representations...
constexpr double pi
Definition common_persistence_representations.h:45
std::vector< std::vector< double > > create_Gaussian_filter(std::size_t pixel_radius, double sigma)
Definition Persistence_heat_maps.h:46
bool almost_equal(double a, double b)
Definition common_persistence_representations.h:65
std::vector< std::pair< double, double > > read_persistence_intervals_in_one_dimension_from_file(std::string const &filename, int dimension=-1, double what_to_substitute_for_infinite_bar=-1)
Definition read_persistence_from_file.h:44
Gudhi namespace.
Definition SimplicialComplexForAlpha.h:14