/* This file is part of the Gudhi Library. The Gudhi library * (Geometric Understanding in Higher Dimensions) is a generic C++ * library for computational topology. * * Author(s): Pawel Dlotko * * Copyright (C) 2015 INRIA (France) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef Vector_distances_in_diagram_H #define Vector_distances_in_diagram_H #include #include #include #include #include //gudhi include #include #include #include namespace Gudhi { namespace Gudhi_stat { /* template struct Euclidean_distance { double operator() ( const std::pair< T,T >& f , const std::pair& s ) { return sqrt( (f.first-s.first)*(f.first-s.first) + (f.second-s.second)*(f.second-s.second) ); } double operator() ( const std::vector< T >& f , const std::vector < T >& s ) { if ( f.size() != s.size() ) { std::cerr << "Not compatible points dimensions in the procedure to compute Euclidean distance. The program will now terminate. \n"; std::cout << f.size() << " , " << s.size() << std::endl; throw "Not compatible points dimensions in the procedure to compute Euclidean distance. The program will now terminate. \n"; } double result = 0; for ( size_t i = 0 ; i != f.size() ; ++i ) { result += ( f[i]-s[i] )*( f[i]-s[i] ); } return sqrt( result ); } }; * */ template struct Maximum_distance { double operator() ( const std::pair< T,T >& f , const std::pair& s ) { return std::max( fabs( f.first - s.first ) , fabs( f.second - s.second ) ); } }; /** * This is an implementation of idea presented in the paper 'Stable Topological Signatures for Points on 3D Shapes' by * M. Carriere, S. Y. Oudot and M. Ovsjanikov published in Computer Graphics Forum (proc. SGP 2015). * The parameter of the class is the class that computes distance used to construct the vectors. The typical function is * either Eucludean of maximum (Manhattan) distance. * This class implements the following concepts: Vectorized_topological_data, Topological_data_with_distances, Real_valued_topological_data, Topological_data_with_averages, Topological_data_with_scalar_product * **/ template class Vector_distances_in_diagram { public: /** * The default constructor. **/ Vector_distances_in_diagram(){}; /** * The constructor that takes as an input a multiset of persistence intervals (given as vector of birth-death pairs). The second parameter is the desiered length of the output vectors. **/ Vector_distances_in_diagram( const std::vector< std::pair< double , double > >& intervals , size_t where_to_cut ); /** * The constructor taking as an input a file with birth-death pairs. The second parameter is the desiered length of the output vectors. **/ Vector_distances_in_diagram( const char* filename , size_t where_to_cut ); /** * Writing to a stream. **/ template friend std::ostream& operator << ( std::ostream& out , const Vector_distances_in_diagram& d ) { for ( size_t i = 0 ; i != std::min( d.sorted_vector_of_distances.size() , d.where_to_cut) ; ++i ) { out << d.sorted_vector_of_distances[i] << " "; } return out; } /** * This procedure gives the value of a vector on a given position. **/ inline double vector_in_position( size_t position )const { if ( position >= this->sorted_vector_of_distances.size() )throw("Wrong position in accessing Vector_distances_in_diagram::sorted_vector_of_distances\n"); return this->sorted_vector_of_distances[position]; } /** * Return a size of a vector. **/ inline size_t size()const{return this->sorted_vector_of_distances.size();} /** * Write a vector to a file. **/ void write_to_file( const char* filename )const; /** * Write a vector to a file. **/ void print_to_file( const char* filename )const { this->write_to_file( filename ); } /** * Loading a vector to a file. **/ void load_from_file( const char* filename ); /** * Comparision operators: **/ bool operator == ( const Vector_distances_in_diagram& second )const { if ( this->sorted_vector_of_distances.size() != second.sorted_vector_of_distances.size() )return false; for ( size_t i = 0 ; i != this->sorted_vector_of_distances.size() ; ++i ) { if ( !almost_equal(this->sorted_vector_of_distances[i] , second.sorted_vector_of_distances[i]) )return false; } return true; } bool operator != ( const Vector_distances_in_diagram& second )const { return !( *this == second ); } //Implementations of functions for various concepts. /** * Compute projection to real numbers of persistence vector. This function is required by the Real_valued_topological_data concept **/ double project_to_R( int number_of_function )const; /** * The function gives the number of possible projections to R. This function is required by the Real_valued_topological_data concept. **/ size_t number_of_projections_to_R()const { return this->number_of_functions_for_projections_to_reals; } /** * Compute a vectorization of a persistent vectors. It is required in a concept Vectorized_topological_data. **/ std::vector vectorize( int number_of_function )const; /** * This function return the number of functions that allows vectorization of a persisence vector. It is required in a concept Vectorized_topological_data. **/ size_t number_of_vectorize_functions()const { return this->number_of_functions_for_vectorization; } /** * Compute a average of two persistent vectors. This function is required by Topological_data_with_averages concept. **/ void compute_average( const std::vector< Vector_distances_in_diagram* >& to_average ); /** * Compute a distance of two persistent vectors. This function is required in Topological_data_with_distances concept. * For max norm distance, set power to std::numeric_limits::max() **/ double distance( const Vector_distances_in_diagram& second , double power = 1)const; /** * Compute a scalar product of two persistent vectors. This function is required in Topological_data_with_scalar_product concept. **/ double compute_scalar_product( const Vector_distances_in_diagram& second )const; //end of implementation of functions needed for concepts. /** * For visualization use output from vectorize and build histograms. **/ std::vector< double > output_for_visualization()const { return this->sorted_vector_of_distances; } /** * Create a gnuplot script to vizualize the data structure. **/ void plot( const char* filename )const { std::stringstream gnuplot_script; gnuplot_script << filename << "_GnuplotScript"; std::ofstream out; out.open( gnuplot_script.str().c_str() ); out << "set style data histogram" << std::endl; out << "set style histogram cluster gap 1" << std::endl; out << "set style fill solid border -1" << std::endl; out << "plot '-' notitle" << std::endl; for ( size_t i = 0 ; i != this->sorted_vector_of_distances.size() ; ++i ) { out << this->sorted_vector_of_distances[i] << std::endl; } out < get_x_range()const { return std::make_pair( 0 , this->sorted_vector_of_distances.size() ); } /** * The y-range of the persistence vector. **/ std::pair< double , double > get_y_range()const { if ( this->sorted_vector_of_distances.size() == 0 )return std::make_pair(0,0); return std::make_pair( this->sorted_vector_of_distances[0] , 0); } //arythmetic operations: template < typename Operation_type > friend Vector_distances_in_diagram operation_on_pair_of_vectors( const Vector_distances_in_diagram& first , const Vector_distances_in_diagram& second , Operation_type opertion ) { Vector_distances_in_diagram result; //Operation_type operation; result.sorted_vector_of_distances.reserve(std::max( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ); for ( size_t i = 0 ; i != std::min( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ; ++i ) { result.sorted_vector_of_distances.push_back( opertion( first.sorted_vector_of_distances[i] , second.sorted_vector_of_distances[i]) ); } if ( first.sorted_vector_of_distances.size() == std::min( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ) { for ( size_t i = std::min( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ; i != std::max( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ; ++i ) { result.sorted_vector_of_distances.push_back( opertion(0,second.sorted_vector_of_distances[i]) ); } } else { for ( size_t i = std::min( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ; i != std::max( first.sorted_vector_of_distances.size() , second.sorted_vector_of_distances.size() ) ; ++i ) { result.sorted_vector_of_distances.push_back( opertion(first.sorted_vector_of_distances[i],0) ); } } return result; }//operation_on_pair_of_vectors /** * This function implements an operation of multiplying Vector_distances_in_diagram by a scalar. **/ Vector_distances_in_diagram multiply_by_scalar( double scalar )const { Vector_distances_in_diagram result; result.sorted_vector_of_distances.reserve( this->sorted_vector_of_distances.size() ); for ( size_t i = 0 ; i != this->sorted_vector_of_distances.size() ; ++i ) { result.sorted_vector_of_distances.push_back( scalar * this->sorted_vector_of_distances[i] ); } return result; }//multiply_by_scalar /** * This function computes a sum of two objects of a type Vector_distances_in_diagram. **/ friend Vector_distances_in_diagram operator+( const Vector_distances_in_diagram& first , const Vector_distances_in_diagram& second ) { return operation_on_pair_of_vectors( first , second , std::plus() ); } /** * This function computes a difference of two objects of a type Vector_distances_in_diagram. **/ friend Vector_distances_in_diagram operator-( const Vector_distances_in_diagram& first , const Vector_distances_in_diagram& second ) { return operation_on_pair_of_vectors( first , second , std::minus() ); } /** * This function computes a product of an object of a type Vector_distances_in_diagram with real number. **/ friend Vector_distances_in_diagram operator*( double scalar , const Vector_distances_in_diagram& A ) { return A.multiply_by_scalar( scalar ); } /** * This function computes a product of an object of a type Vector_distances_in_diagram with real number. **/ friend Vector_distances_in_diagram operator*( const Vector_distances_in_diagram& A , double scalar ) { return A.multiply_by_scalar( scalar ); } /** * This function computes a product of an object of a type Vector_distances_in_diagram with real number. **/ Vector_distances_in_diagram operator*( double scalar ) { return this->multiply_by_scalar( scalar ); } /** * += operator for Vector_distances_in_diagram. **/ Vector_distances_in_diagram operator += ( const Vector_distances_in_diagram& rhs ) { *this = *this + rhs; return *this; } /** * -= operator for Vector_distances_in_diagram. **/ Vector_distances_in_diagram operator -= ( const Vector_distances_in_diagram& rhs ) { *this = *this - rhs; return *this; } /** * *= operator for Vector_distances_in_diagram. **/ Vector_distances_in_diagram operator *= ( double x ) { *this = *this*x; return *this; } /** * /= operator for Vector_distances_in_diagram. **/ Vector_distances_in_diagram operator /= ( double x ) { if ( x == 0 )throw( "In operator /=, division by 0. Program terminated." ); *this = *this * (1/x); return *this; } private: std::vector< std::pair< double , double > > intervals; std::vector< double > sorted_vector_of_distances; size_t number_of_functions_for_vectorization; size_t number_of_functions_for_projections_to_reals; size_t where_to_cut; void compute_sorted_vector_of_distances_via_heap( size_t where_to_cut ); void compute_sorted_vector_of_distances_via_vector_sorting( size_t where_to_cut ); Vector_distances_in_diagram( const std::vector< double >& sorted_vector_of_distances_ ):sorted_vector_of_distances(sorted_vector_of_distances_) { this->set_up_numbers_of_functions_for_vectorization_and_projections_to_reals(); } void set_up_numbers_of_functions_for_vectorization_and_projections_to_reals() { //warning, this function can be only called after filling in the intervals vector. this->number_of_functions_for_vectorization = this->sorted_vector_of_distances.size(); this->number_of_functions_for_projections_to_reals = this->sorted_vector_of_distances.size(); } }; template Vector_distances_in_diagram::Vector_distances_in_diagram( const std::vector< std::pair< double,double > >& intervals_ , size_t where_to_cut_ ):where_to_cut(where_to_cut_) { std::vector< std::pair< double,double > > i( intervals_ ); this->intervals = i; //this->compute_sorted_vector_of_distances_via_heap( where_to_cut ); this->compute_sorted_vector_of_distances_via_vector_sorting(where_to_cut); this->set_up_numbers_of_functions_for_vectorization_and_projections_to_reals(); } template Vector_distances_in_diagram::Vector_distances_in_diagram( const char* filename , size_t where_to_cut ):where_to_cut(where_to_cut) { //standard file with barcode std::vector< std::pair< double , double > > intervals = read_standard_persistence_file( filename ); //gudhi file with barcode //std::vector< std::pair< double , double > > intervals = read_gudhi_persistence_file( filename , dimension ); this->intervals = intervals; this->compute_sorted_vector_of_distances_via_heap( where_to_cut ); //this->compute_sorted_vector_of_distances_via_vector_sorting( where_to_cut ); set_up_numbers_of_functions_for_vectorization_and_projections_to_reals(); } template < typename F> void Vector_distances_in_diagram::compute_sorted_vector_of_distances_via_heap( size_t where_to_cut ) { bool dbg = false; if ( dbg ) { std::cerr << "Here are the intervals : \n"; for ( size_t i = 0 ; i != this->intervals.size() ; ++i ) { std::cerr << this->intervals[i].first << " , " << this->intervals[i].second <intervals.size() * ( this->intervals.size() - 1 ) + this->intervals.size())); std::vector< double > heap( where_to_cut , std::numeric_limits::max() ); std::make_heap (heap.begin(),heap.end()); F f; //for every pair of points in the diagram, compute the minimum of their distance, and distance of those points from diagonal for ( size_t i = 0 ; i < this->intervals.size() ; ++i ) { for ( size_t j = i+1 ; j < this->intervals.size() ; ++j ) { double value = std::min( f( this->intervals[i] , this->intervals[j] ), std::min( f( this->intervals[i] , std::make_pair( 0.5*(this->intervals[i].first+this->intervals[i].second) , 0.5*(this->intervals[i].first+this->intervals[i].second) ) ), f( this->intervals[j] , std::make_pair( 0.5*(this->intervals[j].first+this->intervals[j].second) , 0.5*(this->intervals[j].first+this->intervals[j].second) ) ) ) ); if ( dbg ) { std::cerr << "Value : " << value <intervals.size() ; ++i ) { double value = f( this->intervals[i] , std::make_pair( 0.5*(this->intervals[i].first+this->intervals[i].second) , 0.5*(this->intervals[i].first+this->intervals[i].second) ) ); if ( -value < heap.front() ) { //std::cerr << "Replacing : " << heap.front() << " with : " << -value <::max() ) { heap[i] = 0; } else { heap[i] *= -1; } } if ( dbg ) { std::cerr << "This is the heap after all the operations :\n"; for ( size_t i = 0 ; i != heap.size() ; ++i ) { std::cout << heap[i] << " "; } std::cout <sorted_vector_of_distances = heap; } template < typename F> void Vector_distances_in_diagram::compute_sorted_vector_of_distances_via_vector_sorting( size_t where_to_cut ) { std::vector< double > distances; distances.reserve( (size_t)(0.5 * this->intervals.size() * ( this->intervals.size() - 1 ) + this->intervals.size()) ); F f; //for every pair of points in the diagram, compute the minimum of their distance, and distance of those points from diagonal for ( size_t i = 0 ; i < this->intervals.size() ; ++i ) { //add distance of i-th point in the diagram from the diagonal to the distances vector distances.push_back( f( this->intervals[i] , std::make_pair( 0.5*(this->intervals[i].first+this->intervals[i].second) , 0.5*(this->intervals[i].first+this->intervals[i].second) ) ) ); for ( size_t j = i+1 ; j < this->intervals.size() ; ++j ) { double value = std::min( f( this->intervals[i] , this->intervals[j] ), std::min( f( this->intervals[i] , std::make_pair( 0.5*(this->intervals[i].first+this->intervals[i].second) , 0.5*(this->intervals[i].first+this->intervals[i].second) ) ), f( this->intervals[j] , std::make_pair( 0.5*(this->intervals[j].first+this->intervals[j].second) , 0.5*(this->intervals[j].first+this->intervals[j].second) ) ) ) ); distances.push_back( value ); } } std::sort( distances.begin() , distances.end() , std::greater() ); if ( distances.size() > where_to_cut )distances.resize( where_to_cut ); this->sorted_vector_of_distances = distances; } //Implementations of functions for various concepts. template double Vector_distances_in_diagram::project_to_R( int number_of_function )const { if ( (size_t)number_of_function > this->number_of_functions_for_projections_to_reals )throw "Wrong index of a function in a method Vector_distances_in_diagram::project_to_R"; if ( number_of_function < 0 )throw "Wrong index of a function in a method Vector_distances_in_diagram::project_to_R"; double result = 0; for ( size_t i = 0 ; i != (size_t)number_of_function ; ++i ) { result += sorted_vector_of_distances[i]; } return result; } template void Vector_distances_in_diagram::compute_average( const std::vector< Vector_distances_in_diagram* >& to_average ) { if ( to_average.size() == 0 ) { (*this) = Vector_distances_in_diagram(); return; } size_t maximal_length_of_vector = 0; for ( size_t i = 0 ; i != to_average.size() ; ++i ) { if ( to_average[i]->sorted_vector_of_distances.size() > maximal_length_of_vector ) { maximal_length_of_vector = to_average[i]->sorted_vector_of_distances.size(); } } std::vector< double > av( maximal_length_of_vector , 0 ); for ( size_t i = 0 ; i != to_average.size() ; ++i ) { for ( size_t j = 0 ; j != to_average[i]->sorted_vector_of_distances.size() ; ++j ) { av[j] += to_average[i]->sorted_vector_of_distances[j]; } } for ( size_t i = 0 ; i != maximal_length_of_vector ; ++i ) { av[i] /= (double)to_average.size(); } this->sorted_vector_of_distances = av; this->where_to_cut = av.size(); } template double Vector_distances_in_diagram::distance( const Vector_distances_in_diagram& second_ , double power )const { bool dbg = false; if ( dbg ) { std::cerr << "Entering double Vector_distances_in_diagram::distance( const Abs_Topological_data_with_distances* second , double power ) procedure \n"; std::cerr << "Power : " << power << std::endl; std::cerr << "This : " << *this << std::endl; std::cerr << "second : " << second_ << std::endl; } double result = 0; for ( size_t i = 0 ; i != std::min(this->sorted_vector_of_distances.size(), second_.sorted_vector_of_distances.size()) ; ++i ) { if ( power == 1 ) { if ( dbg ) { std::cerr << "|" << this->sorted_vector_of_distances[i] << " - " << second_.sorted_vector_of_distances[i] << " | : " << fabs( this->sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ) <sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ); } else { if ( power < std::numeric_limits::max() ) { result += std::pow( fabs( this->sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ) , power ); } else { //nax morm if ( result < fabs( this->sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ) )result = fabs( this->sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ); } if ( dbg ) { std::cerr << "| " << this->sorted_vector_of_distances[i] << " - " << second_.sorted_vector_of_distances[i] << " : " << fabs( this->sorted_vector_of_distances[i] - second_.sorted_vector_of_distances[i] ) <sorted_vector_of_distances.size() != second_.sorted_vector_of_distances.size() ) { if ( this->sorted_vector_of_distances.size() > second_.sorted_vector_of_distances.size() ) { for ( size_t i = second_.sorted_vector_of_distances.size() ; i != this->sorted_vector_of_distances.size() ; ++i ) { result += fabs( this->sorted_vector_of_distances[i] ); } } else { //this->sorted_vector_of_distances.size() < second_.sorted_vector_of_distances.size() for ( size_t i = this->sorted_vector_of_distances.size() ; i != second_.sorted_vector_of_distances.size() ; ++i ) { result += fabs( second_.sorted_vector_of_distances[i] ); } } } if ( power != 1 ) { result = std::pow( result , (1.0/power) ); } return result; } template < typename F> std::vector Vector_distances_in_diagram::vectorize( int number_of_function )const { if ( (size_t)number_of_function > this->number_of_functions_for_vectorization )throw "Wrong index of a function in a method Vector_distances_in_diagram::vectorize"; if ( number_of_function < 0 )throw "Wrong index of a function in a method Vector_distances_in_diagram::vectorize"; std::vector< double > result( std::min( (size_t)number_of_function , this->sorted_vector_of_distances.size() ) ); for ( size_t i = 0 ; i != std::min( (size_t)number_of_function , this->sorted_vector_of_distances.size() ) ; ++i ) { result[i] = this->sorted_vector_of_distances[i]; } return result; } template < typename F> void Vector_distances_in_diagram::write_to_file( const char* filename )const { std::ofstream out; out.open( filename ); for ( size_t i = 0 ; i != this->sorted_vector_of_distances.size() ; ++i ) { out << this->sorted_vector_of_distances[i] << " "; } out.close(); } template < typename F> void Vector_distances_in_diagram::load_from_file( const char* filename ) { //check if the file exist. if ( !( access( filename, F_OK ) != -1 ) ) { std::cerr << "The file : " << filename << " do not exist. The program will now terminate \n"; throw "The file from which you are trying to read the persistence landscape do not exist. The program will now terminate \n"; } std::ifstream in; in.open( filename ); double number; while ( true ) { in >> number; if ( in.eof() )break; this->sorted_vector_of_distances.push_back(number); } in.close(); } template < typename F> double Vector_distances_in_diagram::compute_scalar_product( const Vector_distances_in_diagram& second_vector )const { double result = 0; for ( size_t i = 0 ; i != std::min(this->sorted_vector_of_distances.size(),second_vector.sorted_vector_of_distances.size()) ; ++i ) { result += this->sorted_vector_of_distances[i] * second_vector.sorted_vector_of_distances[i]; } return result; } }//namespace Gudhi_stat }//namespace Gudhi #endif // Vector_distances_in_diagram_H